diff --git a/common/changes/cadl-vs/solution-settings_2022-08-24-18-47.json b/common/changes/cadl-vs/solution-settings_2022-08-24-18-47.json
new file mode 100644
index 00000000000..fabdba529e0
--- /dev/null
+++ b/common/changes/cadl-vs/solution-settings_2022-08-24-18-47.json
@@ -0,0 +1,10 @@
+{
+ "changes": [
+ {
+ "packageName": "cadl-vs",
+ "comment": "Fix issue with configured cadl-server location not being found when opening a solution.",
+ "type": "patch"
+ }
+ ],
+ "packageName": "cadl-vs"
+}
\ No newline at end of file
diff --git a/packages/cadl-vs/VS2019/Microsoft.Cadl.VS2019.csproj b/packages/cadl-vs/VS2019/Microsoft.Cadl.VS2019.csproj
index d2b4a771440..04304a6779d 100644
--- a/packages/cadl-vs/VS2019/Microsoft.Cadl.VS2019.csproj
+++ b/packages/cadl-vs/VS2019/Microsoft.Cadl.VS2019.csproj
@@ -7,10 +7,14 @@
17.0
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/packages/cadl-vs/VS2022/Microsoft.Cadl.VS2022.csproj b/packages/cadl-vs/VS2022/Microsoft.Cadl.VS2022.csproj
index 6d14bd4047c..1725c8e917e 100644
--- a/packages/cadl-vs/VS2022/Microsoft.Cadl.VS2022.csproj
+++ b/packages/cadl-vs/VS2022/Microsoft.Cadl.VS2022.csproj
@@ -7,10 +7,14 @@
18.0
+
-
+
+
+
+
diff --git a/packages/cadl-vs/src/Exceptions.cs b/packages/cadl-vs/src/Exceptions.cs
index 50416d1befe..e8491f75d2e 100644
--- a/packages/cadl-vs/src/Exceptions.cs
+++ b/packages/cadl-vs/src/Exceptions.cs
@@ -21,6 +21,11 @@ public CadlUserErrorException(string message)
{
}
+
+ public CadlUserErrorException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
}
diff --git a/packages/cadl-vs/src/VSExtension.cs b/packages/cadl-vs/src/VSExtension.cs
index c3b4cd7f687..cfed753ccb4 100644
--- a/packages/cadl-vs/src/VSExtension.cs
+++ b/packages/cadl-vs/src/VSExtension.cs
@@ -1,22 +1,26 @@
+using EnvDTE;
+using Microsoft.VisualStudio.LanguageServer.Client;
+using Microsoft.VisualStudio.Shell;
+using Microsoft.VisualStudio.Threading;
+using Microsoft.VisualStudio.Utilities;
+using Microsoft.VisualStudio.Workspace;
+using Microsoft.VisualStudio.Workspace.Settings;
+using Microsoft.VisualStudio.Workspace.VSIntegration.Contracts;
+using Newtonsoft.Json;
using System;
using System.Collections.Generic;
+using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.IO;
+using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
-using Microsoft.VisualStudio.Workspace;
-using Microsoft.VisualStudio.Workspace.Settings;
-using Microsoft.VisualStudio.Workspace.VSIntegration.Contracts;
-using Microsoft.VisualStudio.LanguageServer.Client;
-using Microsoft.VisualStudio.Shell;
-using Microsoft.VisualStudio.Threading;
-using Microsoft.VisualStudio.Utilities;
+using Debugger = System.Diagnostics.Debugger;
+using Process = System.Diagnostics.Process;
using Task = System.Threading.Tasks.Task;
-using System.Linq;
-using System.ComponentModel;
namespace Microsoft.Cadl.VisualStudio
{
@@ -51,23 +55,24 @@ public sealed class LanguageClient : ILanguageClient
public event AsyncEventHandler? StartAsync;
public event AsyncEventHandler? StopAsync { add { } remove { } } // unused
- private readonly IVsFolderWorkspaceService workspaceService;
+ private readonly IVsFolderWorkspaceService _workspaceService;
+ private readonly SVsServiceProvider _serviceProvider;
+ private string? _workspaceFolder;
+ private string? _configuredCadlServerPath;
[ImportingConstructor]
- public LanguageClient([Import] IVsFolderWorkspaceService workspaceService)
+ public LanguageClient([Import] IVsFolderWorkspaceService workspaceService, [Import] SVsServiceProvider serviceProvider)
{
- this.workspaceService = workspaceService;
+ _workspaceService = workspaceService;
+ _serviceProvider = serviceProvider;
}
public async Task ActivateAsync(CancellationToken token)
{
await Task.Yield();
-
- var workspace = workspaceService.CurrentWorkspace;
- var settingsManager = workspace?.GetSettingsManager();
- var settings = settingsManager?.GetAggregatedSettings(SettingsTypes.Generic);
+ await ReadSettingsAsync();
var options = Environment.GetEnvironmentVariable("CADL_SERVER_NODE_OPTIONS");
- var (serverCommand, serverArgs, env) = resolveCadlServer(settings);
+ var (serverCommand, serverArgs, env) = resolveCadlServer();
var info = new ProcessStartInfo
{
// Use cadl-server on PATH in production
@@ -79,7 +84,7 @@ public LanguageClient([Import] IVsFolderWorkspaceService workspaceService)
UseShellExecute = false,
CreateNoWindow = true,
Environment = { new("NODE_OPTIONS", options) },
- WorkingDirectory = settings?.ScopePath,
+ WorkingDirectory = _workspaceFolder,
};
foreach (var entry in env)
@@ -110,7 +115,7 @@ public LanguageClient([Import] IVsFolderWorkspaceService workspaceService)
{
throw new CadlServerNotFoundException(info.FileName);
}
- throw e;
+ throw;
}
}
@@ -140,17 +145,19 @@ public Task OnServerInitializeFailedAsync(Exception e)
#endif
#if VS2022
- public Task OnServerInitializeFailedAsync(ILanguageClientInitializationInfo initializationState) {
- var exception = initializationState.InitializationException;
- var message = exception is CadlUserErrorException
- ? exception.Message
- : $"File issue at https://github.com/microsoft/cadl\r\n\r\n{exception}";
- Debug.Assert(exception is CadlUserErrorException, "Unexpected error initializing cadl-server:\r\n\r\n" + exception);
- return Task.FromResult(
- new InitializationFailureContext {
- FailureMessage = "Failed to activate Cadl language server!\r\n" + message
- });
- }
+ public Task OnServerInitializeFailedAsync(ILanguageClientInitializationInfo initializationState)
+ {
+ var exception = initializationState.InitializationException;
+ var message = exception is CadlUserErrorException
+ ? exception.Message
+ : $"File issue at https://github.com/microsoft/cadl\r\n\r\n{exception}";
+ Debug.Assert(exception is CadlUserErrorException, "Unexpected error initializing cadl-server:\r\n\r\n" + exception);
+ return Task.FromResult(
+ new InitializationFailureContext
+ {
+ FailureMessage = "Failed to activate Cadl language server!\r\n" + message
+ });
+ }
#endif
public Task OnServerInitializedAsync()
@@ -189,7 +196,7 @@ private static string GetDevelopmentCadlServerPath()
}
#endif
- private (string, string[], IDictionary) resolveCadlServer(IWorkspaceSettings? settings)
+ private (string, string[], IDictionary) resolveCadlServer()
{
var env = new Dictionary();
var args = new string[] { "--stdio" };
@@ -197,23 +204,28 @@ private static string GetDevelopmentCadlServerPath()
// Use local build of cadl-server in development (lauched from F5 in VS)
if (InDevelopmentMode())
{
- var options = Environment.GetEnvironmentVariable("CADL_SERVER_NODE_OPTIONS");
- var module = GetDevelopmentCadlServerPath();
- return ("node.exe", new string[] { module, options }.Concat(args).ToArray(), env);
+ var options = Environment.GetEnvironmentVariable("CADL_SERVER_NODE_OPTIONS");
+ var module = GetDevelopmentCadlServerPath();
+ return ("node.exe", new string[] { module, options }.Concat(args).ToArray(), env);
}
#endif
- var serverPath = settings?.Property("cadl.cadl-server.path");
- if (serverPath == null)
+ var serverPath = _configuredCadlServerPath;
+ if (serverPath == null || serverPath.Length == 0)
{
return ("cadl-server.cmd", args, env);
}
var variables = new Dictionary();
- variables.Add("workspaceFolder", workspaceService.CurrentWorkspace.Location);
- var variableResolver = new VariableResolver(variables);
+ if (_workspaceFolder != null)
+ {
+ variables.Add("workspaceFolder", _workspaceFolder);
+ }
+ var variableResolver = new VariableResolver(variables);
serverPath = variableResolver.ResolveVariables(serverPath);
+ serverPath = Path.GetFullPath(serverPath);
+
if (!serverPath.EndsWith(".js"))
{
if (File.Exists(serverPath))
@@ -223,13 +235,59 @@ private static string GetDevelopmentCadlServerPath()
}
else
{
- serverPath = Path.Combine(serverPath, "cmd/cadl-server.js");
+ serverPath = Path.Combine(serverPath, "cmd", "cadl-server.js");
}
}
+ // We need to check this as the later check when process is started would
+ // only trigger if node.exe is not found, not if the .js file passed to it
+ // is not found.
+ if (!File.Exists(serverPath))
+ {
+ throw new CadlServerNotFoundException(serverPath);
+ }
+
env["CADL_SKIP_COMPILER_RESOLVE"] = "1";
return ("node.exe", new string[] { serverPath }.Concat(args).ToArray(), env);
+ }
+ private async Task ReadSettingsAsync()
+ {
+ var workspace = _workspaceService?.CurrentWorkspace;
+ if (workspace != null)
+ {
+ var settings = workspace.GetSettingsManager()?.GetAggregatedSettings(SettingsTypes.Generic);
+ _configuredCadlServerPath = settings?.Property("cadl.cadl-server.path");
+ _workspaceFolder = workspace.Location;
+ }
+ else
+ {
+ // When a solution is open, there is no workspace settings manager,
+ // so we read the settings file ourselves.
+ await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
+ var dte = (DTE)_serviceProvider.GetService(typeof(DTE));
+ if (dte != null && dte.Solution != null)
+ {
+ _workspaceFolder = Path.GetDirectoryName(dte.Solution.FullName);
+ await Task.Run(() =>
+ {
+ var settingsFile = Path.Combine(_workspaceFolder, ".vs", "VSWorkspaceSettings.json");
+ if (File.Exists(settingsFile))
+ {
+ try
+ {
+ var content = File.ReadAllText(settingsFile);
+ var settings = JsonConvert.DeserializeObject>(content);
+ settings?.TryGetValue("cadl.cadl-server.path", out _configuredCadlServerPath);
+ }
+ catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException || ex is JsonException)
+ {
+ throw new CadlUserErrorException($"Unable to read {settingsFile}: {ex.Message}", ex);
+ }
+ }
+ });
+ }
+ }
}
}
}