diff --git a/editors/code/src/bootstrap.ts b/editors/code/src/bootstrap.ts index cabc740717..b38fa06a85 100644 --- a/editors/code/src/bootstrap.ts +++ b/editors/code/src/bootstrap.ts @@ -1,6 +1,6 @@ import * as vscode from "vscode"; import * as os from "os"; -import { Config, substituteVSCodeVariables } from "./config"; +import { Config } from "./config"; import { log, isValidExecutable } from "./util"; import { PersistentState } from "./persistent_state"; import { exec } from "child_process"; @@ -31,58 +31,12 @@ export async function bootstrap( return path; } - -async function patchelf(dest: vscode.Uri): Promise { - await vscode.window.withProgress( - { - location: vscode.ProgressLocation.Notification, - title: "Patching rust-analyzer for NixOS", - }, - async (progress, _) => { - const expression = ` - {srcStr, pkgs ? import {}}: - 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( context: vscode.ExtensionContext, config: Config, state: PersistentState ): Promise { - const explicitPath = serverPath(config); + const explicitPath = process.env.__RA_LSP_SERVER_DEBUG ?? config.serverPath; if (explicitPath) { if (explicitPath.startsWith("~/")) { return os.homedir() + explicitPath.slice("~".length); @@ -131,9 +85,6 @@ async function getServer( ); return undefined; } -function serverPath(config: Config): string | null { - return process.env.__RA_LSP_SERVER_DEBUG ?? substituteVSCodeVariables(config.serverPath); -} async function isNixOs(): Promise { try { @@ -146,3 +97,48 @@ async function isNixOs(): Promise { return false; } } + +async function patchelf(dest: vscode.Uri): Promise { + await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: "Patching rust-analyzer for NixOS", + }, + async (progress, _) => { + const expression = ` + {srcStr, pkgs ? import {}}: + 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); + } + } + ); +} diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index eb4f965291..ce1142df3a 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -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 path from "path"; import * as vscode from "vscode"; import { Env } from "./client"; import { log } from "./util"; @@ -47,7 +48,7 @@ export class Config { } private refreshLogging() { - log.setEnabled(this.traceExtension); + log.setEnabled(this.traceExtension ?? false); log.info("Extension version:", this.package.version); 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 `!` */ - private get(path: string): T { - return this.cfg.get(path)!; + private get(path: string): T | undefined { + return substituteVSCodeVariables(this.cfg.get(path)); } get serverPath() { return this.get("server.path") ?? this.get("serverPath"); } + get serverExtraEnv(): Env { const extraEnv = this.get<{ [key: string]: string | number } | null>("server.extraEnv") ?? {}; - return Object.fromEntries( - Object.entries(extraEnv).map(([k, v]) => [k, typeof v !== "string" ? v.toString() : v]) + return substituteVariablesInEnv( + Object.fromEntries( + Object.entries(extraEnv).map(([k, v]) => [ + k, + typeof v !== "string" ? v.toString() : v, + ]) + ) ); } get traceExtension() { @@ -216,13 +223,13 @@ export class Config { if (sourceFileMap !== "auto") { // "/rustc/" used by suggestions only. const { ["/rustc/"]: _, ...trimmed } = - this.get>("debug.sourceFileMap"); + this.get>("debug.sourceFileMap") ?? {}; sourceFileMap = trimmed; } return { engine: this.get("debug.engine"), - engineSettings: this.get("debug.engineSettings"), + engineSettings: this.get("debug.engineSettings") ?? {}, openDebugPane: this.get("debug.openDebugPane"), sourceFileMap: sourceFileMap, }; @@ -247,37 +254,27 @@ 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)) { +export function substituteVSCodeVariables(resp: T): T { + if (Is.string(resp)) { + return substituteVSCodeVariableInString(resp) as T; + } else if (resp && Is.array(resp)) { return resp.map((val) => { return substituteVSCodeVariables(val); - }); + }) as T; } 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 res as T; + } else if (Is.func(resp)) { + throw new Error("Unexpected function type in substitution"); } return resp; } + +// FIXME: Merge this with `substituteVSCodeVariables` above export function substituteVariablesInEnv(env: Env): Env { const missingDeps = new Set(); // 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; } +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 { const workspaceFolder = () => { const folders = vscode.workspace.workspaceFolders ?? []; diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index 1860924c6d..8b04182155 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts @@ -2,7 +2,7 @@ import * as vscode from "vscode"; import * as lc from "vscode-languageclient/node"; import * as ra from "./lsp_ext"; -import { Config, substituteVariablesInEnv, substituteVSCodeVariables } from "./config"; +import { Config, substituteVSCodeVariables } from "./config"; import { createClient } from "./client"; import { isRustDocument, isRustEditor, log, RustEditor } from "./util"; import { ServerStatusParams } from "./lsp_ext"; @@ -152,9 +152,7 @@ export class Ctx { throw new Error(message); } ); - const newEnv = substituteVariablesInEnv( - Object.assign({}, process.env, this.config.serverExtraEnv) - ); + const newEnv = Object.assign({}, process.env, this.config.serverExtraEnv); const run: lc.Executable = { command: this._serverPath, options: { env: newEnv }, diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts index bd45599227..268b70b4fb 100644 --- a/editors/code/src/debug.ts +++ b/editors/code/src/debug.ts @@ -84,7 +84,7 @@ async function getDebugConfiguration( debugEngine = vscode.extensions.getExtension(engineId); if (debugEngine) break; } - } else { + } else if (debugOptions.engine) { debugEngine = vscode.extensions.getExtension(debugOptions.engine); } diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts index cd91932bb6..a92c90f7ff 100644 --- a/editors/code/src/util.ts +++ b/editors/code/src/util.ts @@ -117,7 +117,7 @@ export function isValidExecutable(path: string): boolean { 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); return res.status === 0;