From a8e0a20ce4396d3c77adf3b5deb48edfd27d6af2 Mon Sep 17 00:00:00 2001
From: Lukas Wirth <lukastw97@gmail.com>
Date: Thu, 20 Oct 2022 21:12:58 +0200
Subject: [PATCH] internal: Properly handle language configuration config
 changes

---
 editors/code/src/config.ts | 100 ++++++++++++++++++++++++++++---------
 editors/code/src/ctx.ts    |   8 ++-
 editors/code/src/main.ts   |  60 ++--------------------
 3 files changed, 88 insertions(+), 80 deletions(-)

diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index 10e243dc896..61125d7a83e 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -11,13 +11,9 @@ export type RunnableEnvCfg =
 
 export class Config {
     readonly extensionId = "rust-lang.rust-analyzer";
+    configureLang: vscode.Disposable | undefined;
 
     readonly rootSection = "rust-analyzer";
-    private readonly requiresWorkspaceReloadOpts = [
-        // FIXME: This shouldn't be here, changing this setting should reload
-        // `continueCommentsOnNewline` behavior without restart
-        "typing",
-    ].map((opt) => `${this.rootSection}.${opt}`);
     private readonly requiresReloadOpts = [
         "cargo",
         "procMacro",
@@ -25,9 +21,7 @@ export class Config {
         "server",
         "files",
         "lens", // works as lens.*
-    ]
-        .map((opt) => `${this.rootSection}.${opt}`)
-        .concat(this.requiresWorkspaceReloadOpts);
+    ].map((opt) => `${this.rootSection}.${opt}`);
 
     readonly package: {
         version: string;
@@ -45,6 +39,11 @@ export class Config {
             ctx.subscriptions
         );
         this.refreshLogging();
+        this.configureLanguage();
+    }
+
+    dispose() {
+        this.configureLang?.dispose();
     }
 
     private refreshLogging() {
@@ -58,33 +57,86 @@ export class Config {
     private async onDidChangeConfiguration(event: vscode.ConfigurationChangeEvent) {
         this.refreshLogging();
 
+        this.configureLanguage();
+
         const requiresReloadOpt = this.requiresReloadOpts.find((opt) =>
             event.affectsConfiguration(opt)
         );
 
         if (!requiresReloadOpt) return;
 
-        const requiresWorkspaceReloadOpt = this.requiresWorkspaceReloadOpts.find((opt) =>
-            event.affectsConfiguration(opt)
-        );
-
-        if (!requiresWorkspaceReloadOpt && this.restartServerOnConfigChange) {
+        if (this.restartServerOnConfigChange) {
             await vscode.commands.executeCommand("rust-analyzer.reload");
             return;
         }
 
-        const message = requiresWorkspaceReloadOpt
-            ? `Changing "${requiresWorkspaceReloadOpt}" requires a window reload`
-            : `Changing "${requiresReloadOpt}" requires a reload`;
-        const userResponse = await vscode.window.showInformationMessage(message, "Reload now");
+        const message = `Changing "${requiresReloadOpt}" requires a server restart`;
+        const userResponse = await vscode.window.showInformationMessage(message, "Restart now");
 
-        if (userResponse === "Reload now") {
-            const command = requiresWorkspaceReloadOpt
-                ? "workbench.action.reloadWindow"
-                : "rust-analyzer.reload";
-            if (userResponse === "Reload now") {
-                await vscode.commands.executeCommand(command);
-            }
+        if (userResponse) {
+            const command = "rust-analyzer.reload";
+            await vscode.commands.executeCommand(command);
+        }
+    }
+
+    /**
+     * Sets up additional language configuration that's impossible to do via a
+     * separate language-configuration.json file. See [1] for more information.
+     *
+     * [1]: https://github.com/Microsoft/vscode/issues/11514#issuecomment-244707076
+     */
+    private configureLanguage() {
+        if (this.typingContinueCommentsOnNewline && !this.configureLang) {
+            const indentAction = vscode.IndentAction.None;
+
+            this.configureLang = vscode.languages.setLanguageConfiguration("rust", {
+                onEnterRules: [
+                    {
+                        // Doc single-line comment
+                        // e.g. ///|
+                        beforeText: /^\s*\/{3}.*$/,
+                        action: { indentAction, appendText: "/// " },
+                    },
+                    {
+                        // Parent doc single-line comment
+                        // e.g. //!|
+                        beforeText: /^\s*\/{2}\!.*$/,
+                        action: { indentAction, appendText: "//! " },
+                    },
+                    {
+                        // Begins an auto-closed multi-line comment (standard or parent doc)
+                        // e.g. /** | */ or /*! | */
+                        beforeText: /^\s*\/\*(\*|\!)(?!\/)([^\*]|\*(?!\/))*$/,
+                        afterText: /^\s*\*\/$/,
+                        action: {
+                            indentAction: vscode.IndentAction.IndentOutdent,
+                            appendText: " * ",
+                        },
+                    },
+                    {
+                        // Begins a multi-line comment (standard or parent doc)
+                        // e.g. /** ...| or /*! ...|
+                        beforeText: /^\s*\/\*(\*|\!)(?!\/)([^\*]|\*(?!\/))*$/,
+                        action: { indentAction, appendText: " * " },
+                    },
+                    {
+                        // Continues a multi-line comment
+                        // e.g.  * ...|
+                        beforeText: /^(\ \ )*\ \*(\ ([^\*]|\*(?!\/))*)?$/,
+                        action: { indentAction, appendText: "* " },
+                    },
+                    {
+                        // Dedents after closing a multi-line comment
+                        // e.g.  */|
+                        beforeText: /^(\ \ )*\ \*\/\s*$/,
+                        action: { indentAction, removeText: 1 },
+                    },
+                ],
+            });
+        }
+        if (!this.typingContinueCommentsOnNewline && this.configureLang) {
+            this.configureLang.dispose();
+            this.configureLang = undefined;
         }
     }
 
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index e94d4365c37..75c6d4698c1 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -38,6 +38,7 @@ export class Ctx {
                 this.dispose();
             },
         });
+        extCtx.subscriptions.push(this);
         this.statusBar.text = "rust-analyzer";
         this.statusBar.tooltip = "ready";
         this.statusBar.command = "rust-analyzer.analyzerStatus";
@@ -48,10 +49,15 @@ export class Ctx {
         this.config = new Config(extCtx);
     }
 
+    dispose() {
+        this.config.dispose();
+    }
+
     clientFetcher() {
+        const self = this;
         return {
             get client(): lc.LanguageClient | undefined {
-                return this.client;
+                return self.client;
             },
         };
     }
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index c47680fbac5..fa7dc6fe304 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -77,10 +77,6 @@ async function activateServer(ctx: Ctx): Promise<RustAnalyzerExtensionApi> {
 
     await initCommonContext(ctx);
 
-    if (ctx.config.typingContinueCommentsOnNewline) {
-        ctx.pushExtCleanup(configureLanguage());
-    }
-
     vscode.workspace.onDidChangeConfiguration(
         async (_) => {
             await ctx
@@ -129,6 +125,11 @@ async function initCommonContext(ctx: Ctx) {
     ctx.registerCommand("stopServer", (_) => async () => {
         // FIXME: We should re-use the client, that is ctx.deactivate() if none of the configs have changed
         await ctx.disposeClient();
+        ctx.setServerStatus({
+            health: "ok",
+            quiescent: true,
+            message: "server is not running",
+        });
     });
     ctx.registerCommand("analyzerStatus", commands.analyzerStatus);
     ctx.registerCommand("memoryUsage", commands.memoryUsage);
@@ -172,54 +173,3 @@ async function initCommonContext(ctx: Ctx) {
     defaultOnEnter.dispose();
     ctx.registerCommand("onEnter", commands.onEnter);
 }
-
-/**
- * Sets up additional language configuration that's impossible to do via a
- * separate language-configuration.json file. See [1] for more information.
- *
- * [1]: https://github.com/Microsoft/vscode/issues/11514#issuecomment-244707076
- */
-function configureLanguage(): vscode.Disposable {
-    const indentAction = vscode.IndentAction.None;
-    return vscode.languages.setLanguageConfiguration("rust", {
-        onEnterRules: [
-            {
-                // Doc single-line comment
-                // e.g. ///|
-                beforeText: /^\s*\/{3}.*$/,
-                action: { indentAction, appendText: "/// " },
-            },
-            {
-                // Parent doc single-line comment
-                // e.g. //!|
-                beforeText: /^\s*\/{2}\!.*$/,
-                action: { indentAction, appendText: "//! " },
-            },
-            {
-                // Begins an auto-closed multi-line comment (standard or parent doc)
-                // e.g. /** | */ or /*! | */
-                beforeText: /^\s*\/\*(\*|\!)(?!\/)([^\*]|\*(?!\/))*$/,
-                afterText: /^\s*\*\/$/,
-                action: { indentAction: vscode.IndentAction.IndentOutdent, appendText: " * " },
-            },
-            {
-                // Begins a multi-line comment (standard or parent doc)
-                // e.g. /** ...| or /*! ...|
-                beforeText: /^\s*\/\*(\*|\!)(?!\/)([^\*]|\*(?!\/))*$/,
-                action: { indentAction, appendText: " * " },
-            },
-            {
-                // Continues a multi-line comment
-                // e.g.  * ...|
-                beforeText: /^(\ \ )*\ \*(\ ([^\*]|\*(?!\/))*)?$/,
-                action: { indentAction, appendText: "* " },
-            },
-            {
-                // Dedents after closing a multi-line comment
-                // e.g.  */|
-                beforeText: /^(\ \ )*\ \*\/\s*$/,
-                action: { indentAction, removeText: 1 },
-            },
-        ],
-    });
-}