Substitute some VSCode variables in the VSCode client

This commit is contained in:
Lukas Wirth
2022-10-16 17:11:24 +02:00
parent ee2d9eddb6
commit d5f467aa4a
2 changed files with 79 additions and 32 deletions

View File

@@ -5,7 +5,7 @@ import * as Is from "vscode-languageclient/lib/common/utils/is";
import { assert } from "./util"; import { assert } from "./util";
import { WorkspaceEdit } from "vscode"; import { WorkspaceEdit } from "vscode";
import { Workspace } from "./ctx"; import { Workspace } from "./ctx";
import { substituteVariablesInEnv } from "./config"; import { substituteVariablesInEnv, substituteVSCodeVariables } from "./config";
import { outputChannel, traceOutputChannel } from "./main"; import { outputChannel, traceOutputChannel } from "./main";
import { randomUUID } from "crypto"; import { randomUUID } from "crypto";
@@ -83,15 +83,17 @@ export async function createClient(
debug: run, debug: run,
}; };
let initializationOptions = vscode.workspace.getConfiguration("rust-analyzer"); let rawInitializationOptions = vscode.workspace.getConfiguration("rust-analyzer");
if (workspace.kind === "Detached Files") { if (workspace.kind === "Detached Files") {
initializationOptions = { rawInitializationOptions = {
detachedFiles: workspace.files.map((file) => file.uri.fsPath), detachedFiles: workspace.files.map((file) => file.uri.fsPath),
...initializationOptions, ...rawInitializationOptions,
}; };
} }
const initializationOptions = substituteVSCodeVariables(rawInitializationOptions);
const clientOptions: lc.LanguageClientOptions = { const clientOptions: lc.LanguageClientOptions = {
documentSelector: [{ scheme: "file", language: "rust" }], documentSelector: [{ scheme: "file", language: "rust" }],
initializationOptions, initializationOptions,
@@ -99,6 +101,22 @@ export async function createClient(
traceOutputChannel: traceOutputChannel(), traceOutputChannel: traceOutputChannel(),
outputChannel: outputChannel(), outputChannel: outputChannel(),
middleware: { 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( async provideHover(
document: vscode.TextDocument, document: vscode.TextDocument,
position: vscode.Position, position: vscode.Position,

View 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 * as vscode from "vscode";
import { Env } from "./client"; import { Env } from "./client";
import { log } from "./util"; 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 { export function substituteVariablesInEnv(env: Env): Env {
const missingDeps = new Set<string>(); const missingDeps = new Set<string>();
// vscode uses `env:ENV_NAME` for env vars resolution, and it's easier // vscode uses `env:ENV_NAME` for env vars resolution, and it's easier
@@ -233,7 +265,7 @@ export function substituteVariablesInEnv(env: Env): Env {
} }
} else { } else {
envWithDeps[dep] = { envWithDeps[dep] = {
value: computeVscodeVar(dep), value: computeVscodeVar(dep) || "${" + dep + "}",
deps: [], deps: [],
}; };
} }
@@ -264,10 +296,8 @@ export function substituteVariablesInEnv(env: Env): Env {
return resolvedEnv; return resolvedEnv;
} }
function computeVscodeVar(varName: string): string { function computeVscodeVar(varName: string): string | null {
// https://code.visualstudio.com/docs/editor/variables-reference const workspaceFolder = () => {
const supportedVariables: { [k: string]: () => string } = {
workspaceFolder: () => {
const folders = vscode.workspace.workspaceFolders ?? []; const folders = vscode.workspace.workspaceFolders ?? [];
if (folders.length === 1) { if (folders.length === 1) {
// TODO: support for remote workspaces? // TODO: support for remote workspaces?
@@ -283,18 +313,17 @@ function computeVscodeVar(varName: string): string {
// no workspace opened // no workspace opened
return ""; return "";
} }
}, };
// https://code.visualstudio.com/docs/editor/variables-reference
const supportedVariables: { [k: string]: () => string } = {
workspaceFolder,
workspaceFolderBasename: () => { workspaceFolderBasename: () => {
const workspaceFolder = computeVscodeVar("workspaceFolder"); return path.basename(workspaceFolder());
if (workspaceFolder) {
return path.basename(workspaceFolder);
} else {
return "";
}
}, },
cwd: () => process.cwd(), cwd: () => process.cwd(),
userHome: () => os.homedir(),
// see // see
// https://github.com/microsoft/vscode/blob/08ac1bb67ca2459496b272d8f4a908757f24f56f/src/vs/workbench/api/common/extHostVariableResolverService.ts#L81 // 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) { if (varName in supportedVariables) {
return supportedVariables[varName](); return supportedVariables[varName]();
} else { } else {
// can't resolve, keep the expression as is // return "${" + varName + "}";
return "${" + varName + "}"; return null;
} }
} }