mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-29 05:14:18 +00:00
Auto merge of #14019 - Veykril:ts-bin-og, r=Veykril
Substitute VSCode variables more generally
This commit is contained in:
commit
e86bac92f4
5 changed files with 87 additions and 85 deletions
|
@ -1,6 +1,6 @@
|
||||||
import * as vscode from "vscode";
|
import * as vscode from "vscode";
|
||||||
import * as os from "os";
|
import * as os from "os";
|
||||||
import { Config, substituteVSCodeVariables } from "./config";
|
import { Config } from "./config";
|
||||||
import { log, isValidExecutable } from "./util";
|
import { log, isValidExecutable } from "./util";
|
||||||
import { PersistentState } from "./persistent_state";
|
import { PersistentState } from "./persistent_state";
|
||||||
import { exec } from "child_process";
|
import { exec } from "child_process";
|
||||||
|
@ -31,58 +31,12 @@ export async function bootstrap(
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function patchelf(dest: vscode.Uri): Promise<void> {
|
|
||||||
await vscode.window.withProgress(
|
|
||||||
{
|
|
||||||
location: vscode.ProgressLocation.Notification,
|
|
||||||
title: "Patching rust-analyzer for NixOS",
|
|
||||||
},
|
|
||||||
async (progress, _) => {
|
|
||||||
const expression = `
|
|
||||||
{srcStr, pkgs ? import <nixpkgs> {}}:
|
|
||||||
pkgs.stdenv.mkDerivation {
|
|
||||||
name = "rust-analyzer";
|
|
||||||
src = /. + srcStr;
|
|
||||||
phases = [ "installPhase" "fixupPhase" ];
|
|
||||||
installPhase = "cp $src $out";
|
|
||||||
fixupPhase = ''
|
|
||||||
chmod 755 $out
|
|
||||||
patchelf --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" $out
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
const origFile = vscode.Uri.file(dest.fsPath + "-orig");
|
|
||||||
await vscode.workspace.fs.rename(dest, origFile, { overwrite: true });
|
|
||||||
try {
|
|
||||||
progress.report({ message: "Patching executable", increment: 20 });
|
|
||||||
await new Promise((resolve, reject) => {
|
|
||||||
const handle = exec(
|
|
||||||
`nix-build -E - --argstr srcStr '${origFile.fsPath}' -o '${dest.fsPath}'`,
|
|
||||||
(err, stdout, stderr) => {
|
|
||||||
if (err != null) {
|
|
||||||
reject(Error(stderr));
|
|
||||||
} else {
|
|
||||||
resolve(stdout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
handle.stdin?.write(expression);
|
|
||||||
handle.stdin?.end();
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
await vscode.workspace.fs.delete(origFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getServer(
|
async function getServer(
|
||||||
context: vscode.ExtensionContext,
|
context: vscode.ExtensionContext,
|
||||||
config: Config,
|
config: Config,
|
||||||
state: PersistentState
|
state: PersistentState
|
||||||
): Promise<string | undefined> {
|
): Promise<string | undefined> {
|
||||||
const explicitPath = serverPath(config);
|
const explicitPath = process.env.__RA_LSP_SERVER_DEBUG ?? config.serverPath;
|
||||||
if (explicitPath) {
|
if (explicitPath) {
|
||||||
if (explicitPath.startsWith("~/")) {
|
if (explicitPath.startsWith("~/")) {
|
||||||
return os.homedir() + explicitPath.slice("~".length);
|
return os.homedir() + explicitPath.slice("~".length);
|
||||||
|
@ -131,9 +85,6 @@ async function getServer(
|
||||||
);
|
);
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
function serverPath(config: Config): string | null {
|
|
||||||
return process.env.__RA_LSP_SERVER_DEBUG ?? substituteVSCodeVariables(config.serverPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function isNixOs(): Promise<boolean> {
|
async function isNixOs(): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
|
@ -146,3 +97,48 @@ async function isNixOs(): Promise<boolean> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function patchelf(dest: vscode.Uri): Promise<void> {
|
||||||
|
await vscode.window.withProgress(
|
||||||
|
{
|
||||||
|
location: vscode.ProgressLocation.Notification,
|
||||||
|
title: "Patching rust-analyzer for NixOS",
|
||||||
|
},
|
||||||
|
async (progress, _) => {
|
||||||
|
const expression = `
|
||||||
|
{srcStr, pkgs ? import <nixpkgs> {}}:
|
||||||
|
pkgs.stdenv.mkDerivation {
|
||||||
|
name = "rust-analyzer";
|
||||||
|
src = /. + srcStr;
|
||||||
|
phases = [ "installPhase" "fixupPhase" ];
|
||||||
|
installPhase = "cp $src $out";
|
||||||
|
fixupPhase = ''
|
||||||
|
chmod 755 $out
|
||||||
|
patchelf --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" $out
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
const origFile = vscode.Uri.file(dest.fsPath + "-orig");
|
||||||
|
await vscode.workspace.fs.rename(dest, origFile, { overwrite: true });
|
||||||
|
try {
|
||||||
|
progress.report({ message: "Patching executable", increment: 20 });
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
const handle = exec(
|
||||||
|
`nix-build -E - --argstr srcStr '${origFile.fsPath}' -o '${dest.fsPath}'`,
|
||||||
|
(err, stdout, stderr) => {
|
||||||
|
if (err != null) {
|
||||||
|
reject(Error(stderr));
|
||||||
|
} else {
|
||||||
|
resolve(stdout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
handle.stdin?.write(expression);
|
||||||
|
handle.stdin?.end();
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
await vscode.workspace.fs.delete(origFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import * as path from "path";
|
import * as Is from "vscode-languageclient/lib/common/utils/is";
|
||||||
import * as os from "os";
|
import * as os from "os";
|
||||||
|
import * as path from "path";
|
||||||
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";
|
||||||
|
@ -47,7 +48,7 @@ export class Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
private refreshLogging() {
|
private refreshLogging() {
|
||||||
log.setEnabled(this.traceExtension);
|
log.setEnabled(this.traceExtension ?? false);
|
||||||
log.info("Extension version:", this.package.version);
|
log.info("Extension version:", this.package.version);
|
||||||
|
|
||||||
const cfg = Object.entries(this.cfg).filter(([_, val]) => !(val instanceof Function));
|
const cfg = Object.entries(this.cfg).filter(([_, val]) => !(val instanceof Function));
|
||||||
|
@ -163,18 +164,24 @@ export class Config {
|
||||||
* ```
|
* ```
|
||||||
* So this getter handles this quirk by not requiring the caller to use postfix `!`
|
* So this getter handles this quirk by not requiring the caller to use postfix `!`
|
||||||
*/
|
*/
|
||||||
private get<T>(path: string): T {
|
private get<T>(path: string): T | undefined {
|
||||||
return this.cfg.get<T>(path)!;
|
return substituteVSCodeVariables(this.cfg.get<T>(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
get serverPath() {
|
get serverPath() {
|
||||||
return this.get<null | string>("server.path") ?? this.get<null | string>("serverPath");
|
return this.get<null | string>("server.path") ?? this.get<null | string>("serverPath");
|
||||||
}
|
}
|
||||||
|
|
||||||
get serverExtraEnv(): Env {
|
get serverExtraEnv(): Env {
|
||||||
const extraEnv =
|
const extraEnv =
|
||||||
this.get<{ [key: string]: string | number } | null>("server.extraEnv") ?? {};
|
this.get<{ [key: string]: string | number } | null>("server.extraEnv") ?? {};
|
||||||
return Object.fromEntries(
|
return substituteVariablesInEnv(
|
||||||
Object.entries(extraEnv).map(([k, v]) => [k, typeof v !== "string" ? v.toString() : v])
|
Object.fromEntries(
|
||||||
|
Object.entries(extraEnv).map(([k, v]) => [
|
||||||
|
k,
|
||||||
|
typeof v !== "string" ? v.toString() : v,
|
||||||
|
])
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
get traceExtension() {
|
get traceExtension() {
|
||||||
|
@ -216,13 +223,13 @@ export class Config {
|
||||||
if (sourceFileMap !== "auto") {
|
if (sourceFileMap !== "auto") {
|
||||||
// "/rustc/<id>" used by suggestions only.
|
// "/rustc/<id>" used by suggestions only.
|
||||||
const { ["/rustc/<id>"]: _, ...trimmed } =
|
const { ["/rustc/<id>"]: _, ...trimmed } =
|
||||||
this.get<Record<string, string>>("debug.sourceFileMap");
|
this.get<Record<string, string>>("debug.sourceFileMap") ?? {};
|
||||||
sourceFileMap = trimmed;
|
sourceFileMap = trimmed;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
engine: this.get<string>("debug.engine"),
|
engine: this.get<string>("debug.engine"),
|
||||||
engineSettings: this.get<object>("debug.engineSettings"),
|
engineSettings: this.get<object>("debug.engineSettings") ?? {},
|
||||||
openDebugPane: this.get<boolean>("debug.openDebugPane"),
|
openDebugPane: this.get<boolean>("debug.openDebugPane"),
|
||||||
sourceFileMap: sourceFileMap,
|
sourceFileMap: sourceFileMap,
|
||||||
};
|
};
|
||||||
|
@ -247,37 +254,27 @@ export class Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const VarRegex = new RegExp(/\$\{(.+?)\}/g);
|
export function substituteVSCodeVariables<T>(resp: T): T {
|
||||||
|
if (Is.string(resp)) {
|
||||||
export function substituteVSCodeVariableInString(val: string): string {
|
return substituteVSCodeVariableInString(resp) as T;
|
||||||
return val.replace(VarRegex, (substring: string, varName) => {
|
} else if (resp && Is.array<any>(resp)) {
|
||||||
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 resp.map((val) => {
|
||||||
return substituteVSCodeVariables(val);
|
return substituteVSCodeVariables(val);
|
||||||
});
|
}) as T;
|
||||||
} else if (resp && typeof resp === "object") {
|
} else if (resp && typeof resp === "object") {
|
||||||
const res: { [key: string]: any } = {};
|
const res: { [key: string]: any } = {};
|
||||||
for (const key in resp) {
|
for (const key in resp) {
|
||||||
const val = resp[key];
|
const val = resp[key];
|
||||||
res[key] = substituteVSCodeVariables(val);
|
res[key] = substituteVSCodeVariables(val);
|
||||||
}
|
}
|
||||||
return res;
|
return res as T;
|
||||||
} else if (typeof resp === "function") {
|
} else if (Is.func(resp)) {
|
||||||
return null;
|
throw new Error("Unexpected function type in substitution");
|
||||||
}
|
}
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: Merge this with `substituteVSCodeVariables` above
|
||||||
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
|
||||||
|
@ -355,6 +352,17 @@ export function substituteVariablesInEnv(env: Env): Env {
|
||||||
return resolvedEnv;
|
return resolvedEnv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const VarRegex = new RegExp(/\$\{(.+?)\}/g);
|
||||||
|
function substituteVSCodeVariableInString(val: string): string {
|
||||||
|
return val.replace(VarRegex, (substring: string, varName) => {
|
||||||
|
if (Is.string(varName)) {
|
||||||
|
return computeVscodeVar(varName) || substring;
|
||||||
|
} else {
|
||||||
|
return substring;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function computeVscodeVar(varName: string): string | null {
|
function computeVscodeVar(varName: string): string | null {
|
||||||
const workspaceFolder = () => {
|
const workspaceFolder = () => {
|
||||||
const folders = vscode.workspace.workspaceFolders ?? [];
|
const folders = vscode.workspace.workspaceFolders ?? [];
|
||||||
|
|
|
@ -2,7 +2,7 @@ import * as vscode from "vscode";
|
||||||
import * as lc from "vscode-languageclient/node";
|
import * as lc from "vscode-languageclient/node";
|
||||||
import * as ra from "./lsp_ext";
|
import * as ra from "./lsp_ext";
|
||||||
|
|
||||||
import { Config, substituteVariablesInEnv, substituteVSCodeVariables } from "./config";
|
import { Config, substituteVSCodeVariables } from "./config";
|
||||||
import { createClient } from "./client";
|
import { createClient } from "./client";
|
||||||
import { isRustDocument, isRustEditor, log, RustEditor } from "./util";
|
import { isRustDocument, isRustEditor, log, RustEditor } from "./util";
|
||||||
import { ServerStatusParams } from "./lsp_ext";
|
import { ServerStatusParams } from "./lsp_ext";
|
||||||
|
@ -152,9 +152,7 @@ export class Ctx {
|
||||||
throw new Error(message);
|
throw new Error(message);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const newEnv = substituteVariablesInEnv(
|
const newEnv = Object.assign({}, process.env, this.config.serverExtraEnv);
|
||||||
Object.assign({}, process.env, this.config.serverExtraEnv)
|
|
||||||
);
|
|
||||||
const run: lc.Executable = {
|
const run: lc.Executable = {
|
||||||
command: this._serverPath,
|
command: this._serverPath,
|
||||||
options: { env: newEnv },
|
options: { env: newEnv },
|
||||||
|
|
|
@ -84,7 +84,7 @@ async function getDebugConfiguration(
|
||||||
debugEngine = vscode.extensions.getExtension(engineId);
|
debugEngine = vscode.extensions.getExtension(engineId);
|
||||||
if (debugEngine) break;
|
if (debugEngine) break;
|
||||||
}
|
}
|
||||||
} else {
|
} else if (debugOptions.engine) {
|
||||||
debugEngine = vscode.extensions.getExtension(debugOptions.engine);
|
debugEngine = vscode.extensions.getExtension(debugOptions.engine);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -117,7 +117,7 @@ export function isValidExecutable(path: string): boolean {
|
||||||
|
|
||||||
const res = spawnSync(path, ["--version"], { encoding: "utf8" });
|
const res = spawnSync(path, ["--version"], { encoding: "utf8" });
|
||||||
|
|
||||||
const printOutput = res.error && (res.error as any).code !== "ENOENT" ? log.warn : log.debug;
|
const printOutput = res.error ? log.warn : log.info;
|
||||||
printOutput(path, "--version:", res);
|
printOutput(path, "--version:", res);
|
||||||
|
|
||||||
return res.status === 0;
|
return res.status === 0;
|
||||||
|
|
Loading…
Reference in a new issue