]> git.lizzy.rs Git - rust.git/blobdiff - src/tools/rust-analyzer/editors/code/src/config.ts
:arrow_up: rust-analyzer
[rust.git] / src / tools / rust-analyzer / editors / code / src / config.ts
index 15846a5e8645e67fd6d2888a81b4b1ca862efefc..632a7d86faa369dcf3da8c1395b1d3c479730412 100644 (file)
@@ -1,4 +1,5 @@
-import path = require("path");
+import * as path from "path";
+import * as os from "os";
 import * as vscode from "vscode";
 import { Env } from "./client";
 import { log } from "./util";
@@ -10,23 +11,17 @@ export type RunnableEnvCfg =
 
 export class Config {
     readonly extensionId = "rust-lang.rust-analyzer";
+    configureLang: vscode.Disposable | undefined;
 
     readonly rootSection = "rust-analyzer";
-    private readonly requiresWorkspaceReloadOpts = [
-        "serverPath",
-        "server",
-        // 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",
+        "serverPath",
+        "server",
         "files",
         "lens", // works as lens.*
-    ]
-        .map((opt) => `${this.rootSection}.${opt}`)
-        .concat(this.requiresWorkspaceReloadOpts);
+    ].map((opt) => `${this.rootSection}.${opt}`);
 
     readonly package: {
         version: string;
@@ -44,6 +39,11 @@ export class Config {
             ctx.subscriptions
         );
         this.refreshLogging();
+        this.configureLanguage();
+    }
+
+    dispose() {
+        this.configureLang?.dispose();
     }
 
     private refreshLogging() {
@@ -57,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");
-
-        if (userResponse === "Reload now") {
-            const command = requiresWorkspaceReloadOpt
-                ? "workbench.action.reloadWindow"
-                : "rust-analyzer.reload";
-            if (userResponse === "Reload now") {
-                await vscode.commands.executeCommand(command);
-            }
+        const message = `Changing "${requiresReloadOpt}" requires a server restart`;
+        const userResponse = await vscode.window.showInformationMessage(message, "Restart now");
+
+        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;
         }
     }
 
@@ -187,6 +240,37 @@ export class Config {
     }
 }
 
+const VarRegex = new RegExp(/\$\{(.+?)\}/g);
+
+export function substituteVSCodeVariableInString(val: string): string {
+    return val.replace(VarRegex, (substring: string, varName) => {
+        if (typeof varName === "string") {
+            return computeVscodeVar(varName) || substring;
+        } else {
+            return substring;
+        }
+    });
+}
+
+export function substituteVSCodeVariables(resp: any): any {
+    if (typeof resp === "string") {
+        return substituteVSCodeVariableInString(resp);
+    } else if (resp && Array.isArray(resp)) {
+        return resp.map((val) => {
+            return substituteVSCodeVariables(val);
+        });
+    } else if (resp && typeof resp === "object") {
+        const res: { [key: string]: any } = {};
+        for (const key in resp) {
+            const val = resp[key];
+            res[key] = substituteVSCodeVariables(val);
+        }
+        return res;
+    } else if (typeof resp === "function") {
+        return null;
+    }
+    return resp;
+}
 export function substituteVariablesInEnv(env: Env): Env {
     const missingDeps = new Set<string>();
     // vscode uses `env:ENV_NAME` for env vars resolution, and it's easier
@@ -233,7 +317,7 @@ export function substituteVariablesInEnv(env: Env): Env {
             }
         } else {
             envWithDeps[dep] = {
-                value: computeVscodeVar(dep),
+                value: computeVscodeVar(dep) || "${" + dep + "}",
                 deps: [],
             };
         }
@@ -264,37 +348,34 @@ export function substituteVariablesInEnv(env: Env): Env {
     return resolvedEnv;
 }
 
-function computeVscodeVar(varName: string): string {
+function computeVscodeVar(varName: string): string | null {
+    const workspaceFolder = () => {
+        const folders = vscode.workspace.workspaceFolders ?? [];
+        if (folders.length === 1) {
+            // TODO: support for remote workspaces?
+            return folders[0].uri.fsPath;
+        } else if (folders.length > 1) {
+            // could use currently opened document to detect the correct
+            // workspace. However, that would be determined by the document
+            // user has opened on Editor startup. Could lead to
+            // unpredictable workspace selection in practice.
+            // It's better to pick the first one
+            return folders[0].uri.fsPath;
+        } else {
+            // no workspace opened
+            return "";
+        }
+    };
     // https://code.visualstudio.com/docs/editor/variables-reference
     const supportedVariables: { [k: string]: () => string } = {
-        workspaceFolder: () => {
-            const folders = vscode.workspace.workspaceFolders ?? [];
-            if (folders.length === 1) {
-                // TODO: support for remote workspaces?
-                return folders[0].uri.fsPath;
-            } else if (folders.length > 1) {
-                // could use currently opened document to detect the correct
-                // workspace. However, that would be determined by the document
-                // user has opened on Editor startup. Could lead to
-                // unpredictable workspace selection in practice.
-                // It's better to pick the first one
-                return folders[0].uri.fsPath;
-            } else {
-                // no workspace opened
-                return "";
-            }
-        },
+        workspaceFolder,
 
         workspaceFolderBasename: () => {
-            const workspaceFolder = computeVscodeVar("workspaceFolder");
-            if (workspaceFolder) {
-                return path.basename(workspaceFolder);
-            } else {
-                return "";
-            }
+            return path.basename(workspaceFolder());
         },
 
         cwd: () => process.cwd(),
+        userHome: () => os.homedir(),
 
         // see
         // https://github.com/microsoft/vscode/blob/08ac1bb67ca2459496b272d8f4a908757f24f56f/src/vs/workbench/api/common/extHostVariableResolverService.ts#L81
@@ -308,7 +389,7 @@ function computeVscodeVar(varName: string): string {
     if (varName in supportedVariables) {
         return supportedVariables[varName]();
     } else {
-        // can't resolve, keep the expression as is
-        return "${" + varName + "}";
+        // return "${" + varName + "}";
+        return null;
     }
 }