-
Notifications
You must be signed in to change notification settings - Fork 334
Use configured cadl-server path in VS when using a solution #916
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
10 changes: 10 additions & 0 deletions
10
common/changes/cadl-vs/solution-settings_2022-08-24-18-47.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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" | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<EventArgs>? StartAsync; | ||
| public event AsyncEventHandler<EventArgs>? 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<Connection?> 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<InitializationFailureContext?> 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<InitializationFailureContext?>( | ||
| new InitializationFailureContext { | ||
| FailureMessage = "Failed to activate Cadl language server!\r\n" + message | ||
| }); | ||
| } | ||
| public Task<InitializationFailureContext?> 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<InitializationFailureContext?>( | ||
| new InitializationFailureContext | ||
| { | ||
| FailureMessage = "Failed to activate Cadl language server!\r\n" + message | ||
| }); | ||
| } | ||
| #endif | ||
|
|
||
| public Task OnServerInitializedAsync() | ||
|
|
@@ -189,31 +196,36 @@ private static string GetDevelopmentCadlServerPath() | |
| } | ||
| #endif | ||
|
|
||
| private (string, string[], IDictionary<string, string>) resolveCadlServer(IWorkspaceSettings? settings) | ||
| private (string, string[], IDictionary<string, string>) resolveCadlServer() | ||
| { | ||
| var env = new Dictionary<string, string>(); | ||
| var args = new string[] { "--stdio" }; | ||
| #if DEBUG | ||
| // 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<string>("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<string, string>(); | ||
| 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<string>("cadl.cadl-server.path"); | ||
| _workspaceFolder = workspace.Location; | ||
| } | ||
| else | ||
| { | ||
| // When a solution is open, there is no workspace settings manager, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can't believe they don't have this story sorted out. |
||
| // 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<Dictionary<string, string>>(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); | ||
| } | ||
| } | ||
| }); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hhm did the formatter not catch this before?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It breaks with #if it seems, I ran VS format command :(