Substitute some VSCode variables in the VSCode client
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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,37 +296,34 @@ export function substituteVariablesInEnv(env: Env): Env {
|
|||||||
return resolvedEnv;
|
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
|
// https://code.visualstudio.com/docs/editor/variables-reference
|
||||||
const supportedVariables: { [k: string]: () => string } = {
|
const supportedVariables: { [k: string]: () => string } = {
|
||||||
workspaceFolder: () => {
|
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 "";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user