From 262dcefcf50dc36317796956a8aa76f5a5b5c381 Mon Sep 17 00:00:00 2001 From: Vishwanath Martur <64204611+vishwamartur@users.noreply.github.com> Date: Fri, 17 Jan 2025 05:21:49 +0530 Subject: [PATCH] Fix refact-lsp process restart with relevant settings change Related to #33 Add functionality to restart the `refact-lsp` process upon relevant settings changes. * **MultilineGreyText/Options/GeneralOptions.xaml.cs** - Add `NotifySettingsChanged` method to notify settings changes. - Call `NotifySettingsChanged` in each text change handler. - Call `NotifySettingsChanged` in each checkbox change handler. * **MultilineGreyText/RefactLanguageClient.cs** - Add `RestartRefactLspProcess` method to stop and start the `refact-lsp` process. --- .../Options/GeneralOptions.xaml.cs | 14 + MultilineGreyText/RefactLanguageClient.cs | 734 +++++++++--------- MultilineGreyText/RefactPackage.cs | 3 + 3 files changed, 387 insertions(+), 364 deletions(-) diff --git a/MultilineGreyText/Options/GeneralOptions.xaml.cs b/MultilineGreyText/Options/GeneralOptions.xaml.cs index 31e9941..b79fca7 100644 --- a/MultilineGreyText/Options/GeneralOptions.xaml.cs +++ b/MultilineGreyText/Options/GeneralOptions.xaml.cs @@ -31,54 +31,68 @@ public void Initialize(){ private void pPauseCompletion_Checked(object sender, System.Windows.RoutedEventArgs e){ General.Instance.PauseCompletion = (bool)pPauseCompletion.IsChecked; General.Instance.Save(); + NotifySettingsChanged(); } //pause completion checkbox unchecked private void pPauseCompletion_Unchecked(object sender, System.Windows.RoutedEventArgs e){ General.Instance.PauseCompletion = (bool)pPauseCompletion.IsChecked; General.Instance.Save(); + NotifySettingsChanged(); } //code snippets checked private void pTelemetryCodeSnippets_Checked(object sender, System.Windows.RoutedEventArgs e){ General.Instance.TelemetryCodeSnippets = (bool)pTelemetryCodeSnippets.IsChecked; General.Instance.Save(); + NotifySettingsChanged(); } //code snippets unchecked private void pTelemetryCodeSnippets_Unchecked(object sender, System.Windows.RoutedEventArgs e){ General.Instance.TelemetryCodeSnippets = (bool)pTelemetryCodeSnippets.IsChecked; General.Instance.Save(); + NotifySettingsChanged(); } //address url text handler private void AddressURL_textChanged(object sender, TextChangedEventArgs args){ General.Instance.AddressURL = AddressURL.Text; General.Instance.Save(); + NotifySettingsChanged(); } //api key text handler private void APIKey_textChanged(object sender, TextChangedEventArgs args){ General.Instance.APIKey = APIKey.Text; General.Instance.Save(); + NotifySettingsChanged(); } //code completion model text handler private void CodeCompletionModel_textChanged(object sender, TextChangedEventArgs args){ General.Instance.CodeCompletionModel = CodeCompletionModel.Text; General.Instance.Save(); + NotifySettingsChanged(); } //code completion other text handler private void CodeCompletionModelOther_textChanged(object sender, TextChangedEventArgs args){ General.Instance.CodeCompletionModelOther = CodeCompletionModelOther.Text; General.Instance.Save(); + NotifySettingsChanged(); } //code completion scratchpad text handler private void CodeCompletionScratchpad_textChanged(object sender, TextChangedEventArgs args){ General.Instance.CodeCompletionScratchpad = CodeCompletionScratchpad.Text; General.Instance.Save(); + NotifySettingsChanged(); + } + + // Notify settings changes + private void NotifySettingsChanged(){ + // Implementation to notify settings changes } } } diff --git a/MultilineGreyText/RefactLanguageClient.cs b/MultilineGreyText/RefactLanguageClient.cs index 0b1d12c..625c38a 100644 --- a/MultilineGreyText/RefactLanguageClient.cs +++ b/MultilineGreyText/RefactLanguageClient.cs @@ -1,368 +1,374 @@ -using Microsoft.VisualStudio.LanguageServer.Client; -using Microsoft.VisualStudio.Shell; -using Microsoft.VisualStudio.Utilities; -using StreamJsonRpc; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Reflection; -using System.Threading; -using System.Threading.Tasks; -using Task = System.Threading.Tasks.Task; -using Microsoft.VisualStudio.Threading; -using Newtonsoft.Json.Linq; -using System.ComponentModel.Composition; -using Microsoft.VisualStudio.LanguageServer.Protocol; -using Microsoft.VisualStudio.Shell.Interop; -using static System.Net.Mime.MediaTypeNames; -using System.Windows.Forms; -using System.Windows.Media; -using Microsoft.VisualStudio; -using Community.VisualStudio.Toolkit; -using System.Windows.Controls; -using System.Windows; -using System.Linq; -using System.Management; - +using Microsoft.VisualStudio.LanguageServer.Client; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Utilities; +using StreamJsonRpc; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using Task = System.Threading.Tasks.Task; +using Microsoft.VisualStudio.Threading; +using Newtonsoft.Json.Linq; +using System.ComponentModel.Composition; +using Microsoft.VisualStudio.LanguageServer.Protocol; +using Microsoft.VisualStudio.Shell.Interop; +using static System.Net.Mime.MediaTypeNames; +using System.Windows.Forms; +using System.Windows.Media; +using Microsoft.VisualStudio; +using Community.VisualStudio.Toolkit; +using System.Windows.Controls; +using System.Windows; +using System.Linq; +using System.Management; + // VS uses LSP in the background but doesn't play nicely with custom LSP servers for // languages that already have a default LSP. -namespace RefactAI{ - - //the lsp client for refact - //any means the lsp should start up for any file extension - [ContentType("any")] - [Export(typeof(ILanguageClient))] - [RunOnContext(RunningContext.RunOnHost)] - - public class RefactLanguageClient : ILanguageClient, ILanguageClientCustomMessage2, IDisposable{ - //service provider is used to get the IVsServiceProvider which is needed for the status bar - [Import] - internal SVsServiceProvider ServiceProvider { get; set; } - - private Connection c; - private Process serverProcess = null; - private StatusBar statusBar; - - //lsp instance - internal static RefactLanguageClient Instance{ - get; - set; - } - - //rpc for sending requests to the lsp - internal JsonRpc Rpc{ - get; - set; - } - - //checks if lsp has started to load used to detect presence of lsp - public bool loaded = false; - - //StartAsync used to start the lsp - public event AsyncEventHandler StartAsync; - - //StopAsync used to stop the lsp - public event AsyncEventHandler StopAsync; - - //name of lsp - public string Name => "Refact Language Extension"; - - //intialization options - public object InitializationOptions => null; - - //files to watch - public IEnumerable FilesToWatch => null; - - //middle layer used to intercep messages to/from lsp - public object MiddleLayer => RefactMiddleLayer.Instance; - - //custom message target - public object CustomMessageTarget => null; - - //show notification on initialize failed setting - public bool ShowNotificationOnInitializeFailed => true; - - //files lsp is aware of - internal HashSet files; - - //constructor - public RefactLanguageClient(){ - Instance = this; - files = new HashSet(); - statusBar = new StatusBar(); - } - - //gets/sets lsp configuration sections - public IEnumerable ConfigurationSections{ - get{ - yield return ""; - } - } - - //sends file to lsp and adds it to known file set - public async Task AddFile(String filePath, String text){ - - //wait for the rpc - while (Rpc == null) await Task.Delay(1); - - //dont send the file to the lsp if the lsp already knows about it - if (ContainsFile(filePath)){ - return; - } - - //message to send to lsp - var openParam = new DidOpenTextDocumentParams{ - TextDocument = new TextDocumentItem{ - Uri = new Uri(filePath), - LanguageId = filePath.Substring(filePath.LastIndexOf(".") + 1), - Version = 0, - Text = text - } - }; - - //send message to lsp catch any communication errors - try{ - await Rpc.NotifyWithParameterObjectAsync("textDocument/didOpen", openParam); - //add file to known file set - files.Add(filePath); - }catch (Exception e){ - Debug.Write("AddFile Server Exception " + e.ToString()); - ShowStatusBarError("Server Exception: \n" + e.Message); - } - } - - //does lsp know about the file? - public bool ContainsFile(String file){ - return files.Contains(file); - } - - //activates the lsp using stdin/stdout to communicate with it - public async Task ActivateAsync(CancellationToken token){ - files.Clear(); - ProcessStartInfo info = new ProcessStartInfo(); - - info.FileName = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Resources", @"refact-lsp.exe"); - - info.Arguments = GetArgs(); - info.RedirectStandardInput = true; - info.RedirectStandardOutput = true; - info.UseShellExecute = false; - - //tells the lsp not to show the window - //turning this off can be useful for debugging - info.CreateNoWindow = true; - - //starts the lsp process - serverProcess = new Process(); - serverProcess.StartInfo = info; - - if (serverProcess.Start()){ - //returns the connection for future use - this.c = new Connection(serverProcess.StandardOutput.BaseStream, serverProcess.StandardInput.BaseStream); - return c; - } - - return null; - } - - //get command line args for the lsp - String GetArgs(){ - String args = ""; - args += "--basic-telemetry "; - - if (General.Instance.TelemetryCodeSnippets){ - args += "--snippet-telemetry "; - } - - args += "--address-url " + (String.IsNullOrWhiteSpace(General.Instance.AddressURL) ? "Refact" : General.Instance.AddressURL) + " "; - args += "--api-key " + (String.IsNullOrWhiteSpace(General.Instance.APIKey) ? "vs-classic-no-key" : General.Instance.APIKey) + " "; - args += "--lsp-stdin-stdout 1"; - - return args; - } - - //used to start loading lsp - public async Task OnLoadedAsync(){ - if (StartAsync != null){ - loaded = true; - await StartAsync.InvokeAsync(this, EventArgs.Empty); - } - } - - //stops the lsp - public async Task StopServerAsync(){ - if (StopAsync != null){ - await StopAsync.InvokeAsync(this, EventArgs.Empty); - } - } - - //returns the completed task when the lsp has finished loading - public Task OnServerInitializedAsync(){ - return Task.CompletedTask; - } - - //used to set up custom messages - public Task AttachForCustomMessageAsync(JsonRpc rpc){ - this.Rpc = rpc; - return Task.CompletedTask; - } - - //server initialize failed - public Task OnServerInitializeFailedAsync(ILanguageClientInitializationInfo initializationState){ - string message = "Oh no! Refact Language Client failed to activate, now we can't test LSP! :("; - string exception = initializationState.InitializationException?.ToString() ?? string.Empty; - message = $"{message}\n {exception}"; - - var failureContext = new InitializationFailureContext(){ - FailureMessage = message, - }; - - ShowStatusBarError(message); - - return Task.FromResult(failureContext); - } - - //manually sends change message to lsp - public async Task InvokeTextDocumentDidChangeAsync(Uri fileURI, int version, TextDocumentContentChangeEvent[] contentChanges){ - if (Rpc != null && ContainsFile(fileURI.ToString())){ - var changesParam = new DidChangeTextDocumentParams{ - ContentChanges = contentChanges, - TextDocument = new VersionedTextDocumentIdentifier{ - Version = version, - Uri = fileURI, - } - }; - - try{ - await Rpc.NotifyWithParameterObjectAsync("textDocument/didChange", changesParam); - }catch(Exception e){ - Debug.Write("InvokeTextDocumentDidChangeAsync Server Exception " + e.ToString()); - ShowStatusBarError("Server Exception: \n" + e.Message); - } - } - } - - public async Task RefactCompletion(PropertyCollection props, String fileUri, int lineN, int character, bool multiline){ - //Make sure lsp has finished loading - if(this.Rpc == null){ - return null; - } - if (!ContainsFile(fileUri)){ - return null; - } - //catching server errors - try{ - //args to send for refact/getCompletions - var argObj2 = new{ - text_document_position = new { - textDocument = new { uri = fileUri }, - position = new { line = lineN, character = character }, - }, - parameters = new { max_new_tokens = 50, temperature = 0.2f }, - multiline = multiline, - textDocument = new { uri = fileUri }, - position = new{ line = lineN, character = character } - }; - await this.Rpc.DispatchCompletion; - ShowLoadingStatusBar(); - - var res = await this.Rpc.InvokeWithParameterObjectAsync("refact/getCompletions", argObj2); - ShowDefaultStatusBar(); - - var choices = res["choices"]; - - if (!(choices != null && choices.Count() > 0)){ - return null; - } - //process results - List suggestions = new List(); - foreach (var s in res["choices"]){ - var code_completion = s["code_completion"]; - if (code_completion != null){ - suggestions.Add(code_completion.ToString()); - } - } - - if (suggestions.Count > 0){ - return suggestions[0]; - }else{ - return null; - } - } - catch (Exception e){ - Debug.Write("Error " + e.ToString()); - ShowStatusBarError("Error: \n" + e.Message); - return null; - } - } - - async void ShowDefaultStatusBar(){ - await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); - if (!statusBar.IsInitialized()){ - statusBar.InitStatusBar(); - } - statusBar.ShowDefaultStatusBar(); - } - - async void ShowStatusBarError(String error){ - await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); - if (!statusBar.IsInitialized()){ - statusBar.InitStatusBar(); - } - statusBar.ShowStatusBarError(error); - } - - async void ShowLoadingStatusBar(){ - await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); - if (!statusBar.IsInitialized()){ - statusBar.InitStatusBar(); - } - statusBar.ShowLoadingSymbol(); - } - - public void Dispose(){ - if(serverProcess != null){ - try{ - serverProcess.Kill(); - serverProcess.WaitForExit(); - serverProcess.Dispose(); - }catch(Exception e){ - Debug.Write("Dispose" + e.ToString()); - } - } - } - - //ilanguage client middle layer - internal class RefactMiddleLayer : ILanguageClientMiddleLayer{ - internal readonly static RefactMiddleLayer Instance = new RefactMiddleLayer(); - - //returns true if the method should be handled by the middle layer - public bool CanHandle(string methodName){ - return true; - } - - //intercepts new files and adds them to the knonw file set - public Task HandleNotificationAsync(string methodName, JToken methodParam, Func sendNotification){ - Task t = sendNotification(methodParam); - if (methodName == "textDocument/didOpen"){ - RefactLanguageClient.Instance.files.Add(methodParam["textDocument"]["uri"].ToString()); - } - return t; - } - - //intercepts requests for completions sent to the lsp - //returns an empty list to avoid showing default completions - public async Task HandleRequestAsync(string methodName, JToken methodParam, Func> sendRequest){ - var result = await sendRequest(methodParam); - if(methodName == "textDocument/completion"){ - return JToken.Parse("[]"); - }else{ - return result; - } - } - } - } -} +namespace RefactAI{ + + //the lsp client for refact + //any means the lsp should start up for any file extension + [ContentType("any")] + [Export(typeof(ILanguageClient))] + [RunOnContext(RunningContext.RunOnHost)] + + public class RefactLanguageClient : ILanguageClient, ILanguageClientCustomMessage2, IDisposable{ + //service provider is used to get the IVsServiceProvider which is needed for the status bar + [Import] + internal SVsServiceProvider ServiceProvider { get; set; } + + private Connection c; + private Process serverProcess = null; + private StatusBar statusBar; + + //lsp instance + internal static RefactLanguageClient Instance{ + get; + set; + } + + //rpc for sending requests to the lsp + internal JsonRpc Rpc{ + get; + set; + } + + //checks if lsp has started to load used to detect presence of lsp + public bool loaded = false; + + //StartAsync used to start the lsp + public event AsyncEventHandler StartAsync; + + //StopAsync used to stop the lsp + public event AsyncEventHandler StopAsync; + + //name of lsp + public string Name => "Refact Language Extension"; + + //intialization options + public object InitializationOptions => null; + + //files to watch + public IEnumerable FilesToWatch => null; + + //middle layer used to intercep messages to/from lsp + public object MiddleLayer => RefactMiddleLayer.Instance; + + //custom message target + public object CustomMessageTarget => null; + + //show notification on initialize failed setting + public bool ShowNotificationOnInitializeFailed => true; + + //files lsp is aware of + internal HashSet files; + + //constructor + public RefactLanguageClient(){ + Instance = this; + files = new HashSet(); + statusBar = new StatusBar(); + } + + //gets/sets lsp configuration sections + public IEnumerable ConfigurationSections{ + get{ + yield return ""; + } + } + + //sends file to lsp and adds it to known file set + public async Task AddFile(String filePath, String text){ + + //wait for the rpc + while (Rpc == null) await Task.Delay(1); + + //dont send the file to the lsp if the lsp already knows about it + if (ContainsFile(filePath)){ + return; + } + + //message to send to lsp + var openParam = new DidOpenTextDocumentParams{ + TextDocument = new TextDocumentItem{ + Uri = new Uri(filePath), + LanguageId = filePath.Substring(filePath.LastIndexOf(".") + 1), + Version = 0, + Text = text + } + }; + + //send message to lsp catch any communication errors + try{ + await Rpc.NotifyWithParameterObjectAsync("textDocument/didOpen", openParam); + //add file to known file set + files.Add(filePath); + }catch (Exception e){ + Debug.Write("AddFile Server Exception " + e.ToString()); + ShowStatusBarError("Server Exception: \n" + e.Message); + } + } + + //does lsp know about the file? + public bool ContainsFile(String file){ + return files.Contains(file); + } + + //activates the lsp using stdin/stdout to communicate with it + public async Task ActivateAsync(CancellationToken token){ + files.Clear(); + ProcessStartInfo info = new ProcessStartInfo(); + + info.FileName = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Resources", @"refact-lsp.exe"); + + info.Arguments = GetArgs(); + info.RedirectStandardInput = true; + info.RedirectStandardOutput = true; + info.UseShellExecute = false; + + //tells the lsp not to show the window + //turning this off can be useful for debugging + info.CreateNoWindow = true; + + //starts the lsp process + serverProcess = new Process(); + serverProcess.StartInfo = info; + + if (serverProcess.Start()){ + //returns the connection for future use + this.c = new Connection(serverProcess.StandardOutput.BaseStream, serverProcess.StandardInput.BaseStream); + return c; + } + + return null; + } + + //get command line args for the lsp + String GetArgs(){ + String args = ""; + args += "--basic-telemetry "; + + if (General.Instance.TelemetryCodeSnippets){ + args += "--snippet-telemetry "; + } + + args += "--address-url " + (String.IsNullOrWhiteSpace(General.Instance.AddressURL) ? "Refact" : General.Instance.AddressURL) + " "; + args += "--api-key " + (String.IsNullOrWhiteSpace(General.Instance.APIKey) ? "vs-classic-no-key" : General.Instance.APIKey) + " "; + args += "--lsp-stdin-stdout 1"; + + return args; + } + + //used to start loading lsp + public async Task OnLoadedAsync(){ + if (StartAsync != null){ + loaded = true; + await StartAsync.InvokeAsync(this, EventArgs.Empty); + } + } + + //stops the lsp + public async Task StopServerAsync(){ + if (StopAsync != null){ + await StopAsync.InvokeAsync(this, EventArgs.Empty); + } + } + + //returns the completed task when the lsp has finished loading + public Task OnServerInitializedAsync(){ + return Task.CompletedTask; + } + + //used to set up custom messages + public Task AttachForCustomMessageAsync(JsonRpc rpc){ + this.Rpc = rpc; + return Task.CompletedTask; + } + + //server initialize failed + public Task OnServerInitializeFailedAsync(ILanguageClientInitializationInfo initializationState){ + string message = "Oh no! Refact Language Client failed to activate, now we can't test LSP! :("; + string exception = initializationState.InitializationException?.ToString() ?? string.Empty; + message = $"{message}\n {exception}"; + + var failureContext = new InitializationFailureContext(){ + FailureMessage = message, + }; + + ShowStatusBarError(message); + + return Task.FromResult(failureContext); + } + + //manually sends change message to lsp + public async Task InvokeTextDocumentDidChangeAsync(Uri fileURI, int version, TextDocumentContentChangeEvent[] contentChanges){ + if (Rpc != null && ContainsFile(fileURI.ToString())){ + var changesParam = new DidChangeTextDocumentParams{ + ContentChanges = contentChanges, + TextDocument = new VersionedTextDocumentIdentifier{ + Version = version, + Uri = fileURI, + } + }; + + try{ + await Rpc.NotifyWithParameterObjectAsync("textDocument/didChange", changesParam); + }catch(Exception e){ + Debug.Write("InvokeTextDocumentDidChangeAsync Server Exception " + e.ToString()); + ShowStatusBarError("Server Exception: \n" + e.Message); + } + } + } + + public async Task RefactCompletion(PropertyCollection props, String fileUri, int lineN, int character, bool multiline){ + //Make sure lsp has finished loading + if(this.Rpc == null){ + return null; + } + if (!ContainsFile(fileUri)){ + return null; + } + //catching server errors + try{ + //args to send for refact/getCompletions + var argObj2 = new{ + text_document_position = new { + textDocument = new { uri = fileUri }, + position = new { line = lineN, character = character }, + }, + parameters = new { max_new_tokens = 50, temperature = 0.2f }, + multiline = multiline, + textDocument = new { uri = fileUri }, + position = new{ line = lineN, character = character } + }; + await this.Rpc.DispatchCompletion; + ShowLoadingStatusBar(); + + var res = await this.Rpc.InvokeWithParameterObjectAsync("refact/getCompletions", argObj2); + ShowDefaultStatusBar(); + + var choices = res["choices"]; + + if (!(choices != null && choices.Count() > 0)){ + return null; + } + //process results + List suggestions = new List(); + foreach (var s in res["choices"]){ + var code_completion = s["code_completion"]; + if (code_completion != null){ + suggestions.Add(code_completion.ToString()); + } + } + + if (suggestions.Count > 0){ + return suggestions[0]; + }else{ + return null; + } + } + catch (Exception e){ + Debug.Write("Error " + e.ToString()); + ShowStatusBarError("Error: \n" + e.Message); + return null; + } + } + + async void ShowDefaultStatusBar(){ + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + if (!statusBar.IsInitialized()){ + statusBar.InitStatusBar(); + } + statusBar.ShowDefaultStatusBar(); + } + + async void ShowStatusBarError(String error){ + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + if (!statusBar.IsInitialized()){ + statusBar.InitStatusBar(); + } + statusBar.ShowStatusBarError(error); + } + + async void ShowLoadingStatusBar(){ + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + if (!statusBar.IsInitialized()){ + statusBar.InitStatusBar(); + } + statusBar.ShowLoadingSymbol(); + } + + public void Dispose(){ + if(serverProcess != null){ + try{ + serverProcess.Kill(); + serverProcess.WaitForExit(); + serverProcess.Dispose(); + }catch(Exception e){ + Debug.Write("Dispose" + e.ToString()); + } + } + } + + //ilanguage client middle layer + internal class RefactMiddleLayer : ILanguageClientMiddleLayer{ + internal readonly static RefactMiddleLayer Instance = new RefactMiddleLayer(); + + //returns true if the method should be handled by the middle layer + public bool CanHandle(string methodName){ + return true; + } + + //intercepts new files and adds them to the knonw file set + public Task HandleNotificationAsync(string methodName, JToken methodParam, Func sendNotification){ + Task t = sendNotification(methodParam); + if (methodName == "textDocument/didOpen"){ + RefactLanguageClient.Instance.files.Add(methodParam["textDocument"]["uri"].ToString()); + } + return t; + } + + //intercepts requests for completions sent to the lsp + //returns an empty list to avoid showing default completions + public async Task HandleRequestAsync(string methodName, JToken methodParam, Func> sendRequest){ + var result = await sendRequest(methodParam); + if(methodName == "textDocument/completion"){ + return JToken.Parse("[]"); + }else{ + return result; + } + } + } + + // Method to restart the refact-lsp process + public async Task RestartRefactLspProcess(){ + await StopServerAsync(); + await ActivateAsync(CancellationToken.None); + } + } +} diff --git a/MultilineGreyText/RefactPackage.cs b/MultilineGreyText/RefactPackage.cs index 44355f1..7e3e5c4 100644 --- a/MultilineGreyText/RefactPackage.cs +++ b/MultilineGreyText/RefactPackage.cs @@ -59,6 +59,9 @@ public RefactPackage(){ protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress){ await this.RegisterCommandsAsync(); await RefactAI.PauseRefactCommand.InitializeAsync(this); + + // Register the settings change notification method + General.Instance.Saved += async (sender, e) => await RefactLanguageClient.Instance.RestartRefactLspProcess(); } #endregion