Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'editors/code/src/bootstrap.ts')
-rw-r--r--editors/code/src/bootstrap.ts111
1 files changed, 95 insertions, 16 deletions
diff --git a/editors/code/src/bootstrap.ts b/editors/code/src/bootstrap.ts
index 42dd0760d6..daead47e94 100644
--- a/editors/code/src/bootstrap.ts
+++ b/editors/code/src/bootstrap.ts
@@ -4,6 +4,7 @@ import type { Config } from "./config";
import { type Env, log } from "./util";
import type { PersistentState } from "./persistent_state";
import { exec, spawnSync } from "child_process";
+import { TextDecoder } from "node:util";
export async function bootstrap(
context: vscode.ExtensionContext,
@@ -50,26 +51,35 @@ async function getServer(
}
return explicitPath;
}
- if (packageJson.releaseTag === null) return "rust-analyzer";
- if (vscode.workspace.workspaceFolders?.length === 1) {
- // otherwise check if there is a toolchain override for the current vscode workspace
- // and if the toolchain of this override has a rust-analyzer component
- // if so, use the rust-analyzer component
- const toolchainTomlExists = await fileExists(
- vscode.Uri.joinPath(vscode.workspace.workspaceFolders[0]!.uri, "rust-toolchain.toml"),
- );
- if (toolchainTomlExists) {
- const res = spawnSync("rustup", ["which", "rust-analyzer"], {
- encoding: "utf8",
- env: { ...process.env },
- cwd: vscode.workspace.workspaceFolders[0]!.uri.fsPath,
- });
- if (!res.error && res.status === 0) {
- return res.stdout.trim();
+ let toolchainServerPath = undefined;
+ if (vscode.workspace.workspaceFolders) {
+ for (const workspaceFolder of vscode.workspace.workspaceFolders) {
+ // otherwise check if there is a toolchain override for the current vscode workspace
+ // and if the toolchain of this override has a rust-analyzer component
+ // if so, use the rust-analyzer component
+ const toolchainUri = vscode.Uri.joinPath(workspaceFolder.uri, "rust-toolchain.toml");
+ if (await hasToolchainFileWithRaDeclared(toolchainUri)) {
+ const res = spawnSync("rustup", ["which", "rust-analyzer"], {
+ encoding: "utf8",
+ env: { ...process.env },
+ cwd: workspaceFolder.uri.fsPath,
+ });
+ if (!res.error && res.status === 0) {
+ toolchainServerPath = earliestToolchainPath(
+ toolchainServerPath,
+ res.stdout.trim(),
+ raVersionResolver,
+ );
+ }
}
}
}
+ if (toolchainServerPath) {
+ return toolchainServerPath;
+ }
+
+ if (packageJson.releaseTag === null) return "rust-analyzer";
// finally, use the bundled one
const ext = process.platform === "win32" ? ".exe" : "";
@@ -102,6 +112,57 @@ async function getServer(
return undefined;
}
+// Given a path to a rust-analyzer executable, resolve its version and return it.
+function raVersionResolver(path: string): string | undefined {
+ const res = spawnSync(path, ["--version"], {
+ encoding: "utf8",
+ });
+ if (!res.error && res.status === 0) {
+ return res.stdout;
+ } else {
+ return undefined;
+ }
+}
+
+// Given a path to two rust-analyzer executables, return the earliest one by date.
+function earliestToolchainPath(
+ path0: string | undefined,
+ path1: string,
+ raVersionResolver: (path: string) => string | undefined,
+): string {
+ if (path0) {
+ if (orderFromPath(path0, raVersionResolver) < orderFromPath(path1, raVersionResolver)) {
+ return path0;
+ } else {
+ return path1;
+ }
+ } else {
+ return path1;
+ }
+}
+
+// Further to extracting a date for comparison, determine the order of a toolchain as follows:
+// Highest - nightly
+// Medium - versioned
+// Lowest - stable
+// Example paths:
+// nightly - /Users/myuser/.rustup/toolchains/nightly-2022-11-22-aarch64-apple-darwin/bin/rust-analyzer
+// versioned - /Users/myuser/.rustup/toolchains/1.72.1-aarch64-apple-darwin/bin/rust-analyzer
+// stable - /Users/myuser/.rustup/toolchains/stable-aarch64-apple-darwin/bin/rust-analyzer
+function orderFromPath(
+ path: string,
+ raVersionResolver: (path: string) => string | undefined,
+): string {
+ const raVersion = raVersionResolver(path);
+ const raDate = raVersion?.match(/^rust-analyzer .*\(.* (\d{4}-\d{2}-\d{2})\)$/);
+ if (raDate?.length === 2) {
+ const precedence = path.includes("nightly-") ? "0" : "1";
+ return "0-" + raDate[1] + "/" + precedence;
+ } else {
+ return "2";
+ }
+}
+
async function fileExists(uri: vscode.Uri) {
return await vscode.workspace.fs.stat(uri).then(
() => true,
@@ -109,6 +170,19 @@ async function fileExists(uri: vscode.Uri) {
);
}
+async function hasToolchainFileWithRaDeclared(uri: vscode.Uri): Promise<boolean> {
+ try {
+ const toolchainFileContents = new TextDecoder().decode(
+ await vscode.workspace.fs.readFile(uri),
+ );
+ return (
+ toolchainFileContents.match(/components\s*=\s*\[.*\"rust-analyzer\".*\]/g)?.length === 1
+ );
+ } catch (e) {
+ return false;
+ }
+}
+
export function isValidExecutable(path: string, extraEnv: Env): boolean {
log.debug("Checking availability of a binary at", path);
@@ -207,3 +281,8 @@ async function patchelf(dest: vscode.Uri): Promise<void> {
},
);
}
+
+export const _private = {
+ earliestToolchainPath,
+ orderFromPath,
+};