Skip to content

Commit be6975f

Browse files
authored
Merge pull request #21359 from Veykril/push-syvnrvtmlsqk
Prompt the user in VSCode to add the rust-anaylzer componenet to the toolchain file
2 parents 38271f5 + f3b5ee4 commit be6975f

File tree

3 files changed

+86
-4
lines changed

3 files changed

+86
-4
lines changed

editors/code/src/bootstrap.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as vscode from "vscode";
22
import * as os from "os";
33
import type { Config } from "./config";
4-
import { type Env, log, spawnAsync } from "./util";
4+
import { type Env, log, RUST_TOOLCHAIN_FILES, spawnAsync } from "./util";
55
import type { PersistentState } from "./persistent_state";
66
import { exec } from "child_process";
77
import { TextDecoder } from "node:util";
@@ -59,8 +59,12 @@ async function getServer(
5959
// otherwise check if there is a toolchain override for the current vscode workspace
6060
// and if the toolchain of this override has a rust-analyzer component
6161
// if so, use the rust-analyzer component
62-
const toolchainUri = vscode.Uri.joinPath(workspaceFolder.uri, "rust-toolchain.toml");
63-
if (await hasToolchainFileWithRaDeclared(toolchainUri)) {
62+
// Check both rust-toolchain.toml and rust-toolchain files
63+
for (const toolchainFile of RUST_TOOLCHAIN_FILES) {
64+
const toolchainUri = vscode.Uri.joinPath(workspaceFolder.uri, toolchainFile);
65+
if (!(await hasToolchainFileWithRaDeclared(toolchainUri))) {
66+
continue;
67+
}
6468
const res = await spawnAsync("rustup", ["which", "rust-analyzer"], {
6569
env: { ...process.env },
6670
cwd: workspaceFolder.uri.fsPath,
@@ -71,6 +75,7 @@ async function getServer(
7175
res.stdout.trim(),
7276
raVersionResolver,
7377
);
78+
break;
7479
}
7580
}
7681
}

editors/code/src/ctx.ts

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import * as vscode from "vscode";
2-
import type * as lc from "vscode-languageclient/node";
2+
import * as lc from "vscode-languageclient/node";
33
import * as ra from "./lsp_ext";
44

55
import { Config, prepareVSCodeConfig } from "./config";
66
import { createClient } from "./client";
77
import {
8+
findRustToolchainFiles,
89
isCargoTomlEditor,
910
isDocumentInWorkspace,
1011
isRustDocument,
@@ -266,6 +267,17 @@ export class Ctx implements RustAnalyzerExtensionApi {
266267
this.outputChannel!.show();
267268
}),
268269
);
270+
this.pushClientCleanup(
271+
this._client.onNotification(
272+
lc.ShowMessageNotification.type,
273+
async (params: lc.ShowMessageParams) => {
274+
// When an MSRV warning is detected and a rust-toolchain file exists,
275+
// show an additional message with actionable guidance about adding
276+
// the rust-analyzer component.
277+
await handleMsrvWarning(params.message);
278+
},
279+
),
280+
);
269281
}
270282
return this._client;
271283
}
@@ -592,3 +604,43 @@ export interface Disposable {
592604

593605
// eslint-disable-next-line @typescript-eslint/no-explicit-any
594606
export type Cmd = (...args: any[]) => unknown;
607+
608+
/**
609+
* Pattern to detect MSRV warning messages from the rust-analyzer server.
610+
*/
611+
const MSRV_WARNING_PATTERN = /using an outdated toolchain version.*rust-analyzer only supports/is;
612+
613+
/**
614+
* Handles the MSRV warning by checking for rust-toolchain files and showing
615+
* an enhanced message if found.
616+
*/
617+
export async function handleMsrvWarning(message: string): Promise<boolean> {
618+
if (!MSRV_WARNING_PATTERN.test(message)) {
619+
return false;
620+
}
621+
622+
const toolchainFiles = await findRustToolchainFiles();
623+
if (toolchainFiles.length === 0) {
624+
return false;
625+
}
626+
627+
const openFile = "Open rust-toolchain file";
628+
const result = await vscode.window.showWarningMessage(
629+
"Your workspace uses a rust-toolchain file with a toolchain too old for the extension shipped rust-analyzer to work properly. " +
630+
"Consider adding the rust-analyzer component to the toolchain file to use a compatible rust-analyzer version. " +
631+
"Add the following to your rust-toolchain file's `[toolchain]` section:\n" +
632+
'components = ["rust-analyzer"]',
633+
{ modal: true },
634+
openFile,
635+
);
636+
637+
if (result === openFile) {
638+
const fileToOpen = toolchainFiles[0];
639+
if (fileToOpen) {
640+
const document = await vscode.workspace.openTextDocument(fileToOpen);
641+
await vscode.window.showTextDocument(document);
642+
}
643+
}
644+
645+
return true;
646+
}

editors/code/src/util.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,3 +328,28 @@ export function normalizeDriveLetter(path: string, isWindowsOS: boolean = isWind
328328

329329
return path;
330330
}
331+
332+
export const RUST_TOOLCHAIN_FILES = ["rust-toolchain.toml", "rust-toolchain"] as const;
333+
334+
export async function findRustToolchainFiles(): Promise<vscode.Uri[]> {
335+
const found: vscode.Uri[] = [];
336+
const workspaceFolders = vscode.workspace.workspaceFolders;
337+
if (!workspaceFolders) {
338+
return found;
339+
}
340+
341+
for (const folder of workspaceFolders) {
342+
for (const filename of RUST_TOOLCHAIN_FILES) {
343+
const toolchainUri = vscode.Uri.joinPath(folder.uri, filename);
344+
try {
345+
await vscode.workspace.fs.stat(toolchainUri);
346+
found.push(toolchainUri);
347+
// Only add the first toolchain file found per workspace folder
348+
break;
349+
} catch {
350+
// File doesn't exist, continue
351+
}
352+
}
353+
}
354+
return found;
355+
}

0 commit comments

Comments
 (0)