about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2022-10-16 17:11:24 +0200
committerLukas Wirth <lukastw97@gmail.com>2022-10-16 19:45:08 +0200
commitd5f467aa4a6b28ffa6acb7673bfea472fdcd64c8 (patch)
tree4aae97003f1d6556b90a97491a65a2f818cc284c
parentee2d9eddb6f31773a507c918553214cb816a0abe (diff)
downloadrust-d5f467aa4a6b28ffa6acb7673bfea472fdcd64c8.tar.gz
rust-d5f467aa4a6b28ffa6acb7673bfea472fdcd64c8.zip
Substitute some VSCode variables in the VSCode client
-rw-r--r--editors/code/src/client.ts26
-rw-r--r--editors/code/src/config.ts85
2 files changed, 79 insertions, 32 deletions
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index 05d4d08f70b..45a7970b217 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -5,7 +5,7 @@ import * as Is from "vscode-languageclient/lib/common/utils/is";
 import { assert } from "./util";
 import { WorkspaceEdit } from "vscode";
 import { Workspace } from "./ctx";
-import { substituteVariablesInEnv } from "./config";
+import { substituteVariablesInEnv, substituteVSCodeVariables } from "./config";
 import { outputChannel, traceOutputChannel } from "./main";
 import { randomUUID } from "crypto";
 
@@ -83,15 +83,17 @@ export async function createClient(
         debug: run,
     };
 
-    let initializationOptions = vscode.workspace.getConfiguration("rust-analyzer");
+    let rawInitializationOptions = vscode.workspace.getConfiguration("rust-analyzer");
 
     if (workspace.kind === "Detached Files") {
-        initializationOptions = {
+        rawInitializationOptions = {
             detachedFiles: workspace.files.map((file) => file.uri.fsPath),
-            ...initializationOptions,
+            ...rawInitializationOptions,
         };
     }
 
+    const initializationOptions = substituteVSCodeVariables(rawInitializationOptions);
+
     const clientOptions: lc.LanguageClientOptions = {
         documentSelector: [{ scheme: "file", language: "rust" }],
         initializationOptions,
@@ -99,6 +101,22 @@ export async function createClient(
         traceOutputChannel: traceOutputChannel(),
         outputChannel: outputChannel(),
         middleware: {
+            workspace: {
+                async configuration(
+                    params: lc.ConfigurationParams,
+                    token: vscode.CancellationToken,
+                    next: lc.ConfigurationRequest.HandlerSignature
+                ) {
+                    const resp = await next(params, token);
+                    if (resp && Array.isArray(resp)) {
+                        return resp.map((val) => {
+                            return substituteVSCodeVariables(val);
+                        });
+                    } else {
+                        return resp;
+                    }
+                },
+            },
             async provideHover(
                 document: vscode.TextDocument,
                 position: vscode.Position,
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index 15846a5e864..199db6e30fc 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -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";
@@ -187,6 +188,37 @@ export class Config {
     }
 }
 
+const VarRegex = new RegExp(/\$\{(.+?)\}/g);
+
+export function substituteVSCodeVariableInString(val: string): string {
+    return val.replaceAll(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 +265,7 @@ export function substituteVariablesInEnv(env: Env): Env {
             }
         } else {
             envWithDeps[dep] = {
-                value: computeVscodeVar(dep),
+                value: computeVscodeVar(dep) || "${" + dep + "}",
                 deps: [],
             };
         }
@@ -264,37 +296,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 +337,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;
     }
 }