Update Node.js, vscode, and ts deps

This commit is contained in:
BenjaminBrienen 2025-02-26 17:17:07 +01:00
parent 2fa819c9d3
commit 00726cf697
29 changed files with 2715 additions and 1857 deletions

View file

@ -1,2 +0,0 @@
node_modules
.eslintrc.js

View file

@ -1,46 +0,0 @@
module.exports = {
env: {
es6: true,
node: true,
},
extends: ["prettier"],
parser: "@typescript-eslint/parser",
parserOptions: {
project: true,
tsconfigRootDir: __dirname,
sourceType: "module",
},
plugins: ["@typescript-eslint"],
rules: {
camelcase: ["error"],
eqeqeq: ["error", "always", { null: "ignore" }],
curly: ["error", "multi-line"],
"no-console": ["error", { allow: ["warn", "error"] }],
"prefer-const": "error",
"@typescript-eslint/member-delimiter-style": [
"error",
{
multiline: {
delimiter: "semi",
requireLast: true,
},
singleline: {
delimiter: "semi",
requireLast: false,
},
},
],
"@typescript-eslint/semi": ["error", "always"],
"@typescript-eslint/no-unnecessary-type-assertion": "error",
"@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/consistent-type-imports": [
"error",
{
prefer: "type-imports",
fixStyle: "inline-type-imports",
},
],
"@typescript-eslint/no-import-type-side-effects": "error",
},
};

View file

@ -1,5 +0,0 @@
module.exports = {
// use 100 because it's Rustfmt's default
// https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#max_width
printWidth: 100,
};

View file

@ -0,0 +1,48 @@
import eslintConfigPrettier from "eslint-config-prettier";
import stylistic from "@stylistic/eslint-plugin";
import eslint from "@eslint/js";
import tseslint from "typescript-eslint";
import stylisticJs from "@stylistic/eslint-plugin-js";
import { type FlatESLintConfig } from "eslint-define-config";
const config: FlatESLintConfig[] = [
eslintConfigPrettier,
eslint.configs.recommended,
stylisticJs.configs["disable-legacy"],
...tseslint.configs.recommended,
stylistic.configs.customize({
indent: 4,
quotes: "double",
semi: true,
braceStyle: "1tbs",
arrowParens: true,
}),
{
rules: {
"no-console": "warn",
"@typescript-eslint/no-unused-vars": [
"error",
{
args: "all",
argsIgnorePattern: "^_",
caughtErrors: "all",
caughtErrorsIgnorePattern: "^_",
destructuredArrayIgnorePattern: "^_",
varsIgnorePattern: "^_",
ignoreRestSiblings: true,
},
],
// the following stylistic lints conflict with prettier
"@stylistic/operator-linebreak": "off",
"@stylistic/indent-binary-ops": "off",
"@stylistic/indent": "off",
"@stylistic/brace-style": "off",
"@stylistic/quotes": "off",
},
},
{
ignores: ["out/", ".vscode-test/", "node_modules/"],
},
];
export default config;

File diff suppressed because it is too large Load diff

View file

@ -7,6 +7,7 @@
"version": "0.5.0-dev",
"releaseTag": null,
"publisher": "rust-lang",
"type": "commonjs",
"repository": {
"url": "https://github.com/rust-lang/rust-analyzer.git",
"type": "git"
@ -27,45 +28,51 @@
}
},
"engines": {
"vscode": "^1.83.0"
"vscode": "^1.93.0"
},
"enabledApiProposals": [],
"scripts": {
"vscode:prepublish": "npm run build-base -- --minify",
"package": "vsce package -o rust-analyzer.vsix",
"build-base": "esbuild ./src/main.ts --bundle --outfile=out/main.js --external:vscode --format=cjs --platform=node --target=node16",
"build-base": "esbuild ./src/main.ts --bundle --outfile=out/main.js --external:vscode --format=cjs --platform=node --target=node20",
"build": "npm run build-base -- --sourcemap",
"watch": "npm run build-base -- --sourcemap --watch",
"format": "prettier --write .",
"format:check": "prettier --check .",
"lint": "eslint -c .eslintrc.js --ext ts ./src ./tests",
"format": "node --experimental-strip-types node_modules/prettier/bin/prettier.cjs . --write",
"format:check": "node --experimental-strip-types node_modules/prettier/bin/prettier.cjs . --check",
"lint": "eslint .",
"lint:fix": "npm run lint -- --fix",
"typecheck": "tsc",
"pretest": "npm run typecheck && npm run build",
"test": "node ./out/tests/runTests.js"
},
"dependencies": {
"@hpcc-js/wasm": "^2.13.0",
"anser": "^2.1.1",
"d3": "^7.8.5",
"d3-graphviz": "^5.0.2",
"@hpcc-js/wasm": "^2.22.4",
"anser": "^2.3.2",
"d3": "^7.9.0",
"d3-graphviz": "^5.6.0",
"jiti": "^2.4.2",
"vscode-languageclient": "^9.0.1"
},
"devDependencies": {
"@tsconfig/strictest": "^2.0.1",
"@types/node": "~16.11.7",
"@types/vscode": "~1.83",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"@vscode/test-electron": "^2.3.8",
"@vscode/vsce": "^3.0.0",
"@eslint/js": "^9.21.0",
"@stylistic/eslint-plugin": "^4.1.0",
"@stylistic/eslint-plugin-js": "^4.1.0",
"@tsconfig/strictest": "^2.0.5",
"@types/node": "~22.13.4",
"@types/vscode": "~1.93.0",
"@typescript-eslint/eslint-plugin": "^8.25.0",
"@typescript-eslint/parser": "^8.25.0",
"@vscode/test-electron": "^2.4.1",
"@vscode/vsce": "^3.2.2",
"esbuild": "^0.25.0",
"eslint": "^8.44.0",
"eslint-config-prettier": "^8.8.0",
"ovsx": "^0.8.2",
"prettier": "^3.0.0",
"tslib": "^2.6.0",
"typescript": "^5.6.0"
"eslint": "^9.21.0",
"eslint-config-prettier": "^10.0.2",
"eslint-define-config": "^2.1.0",
"ovsx": "0.10.1",
"prettier": "^3.5.2",
"tslib": "^2.8.1",
"typescript": "^5.7.3",
"typescript-eslint": "^8.25.0"
},
"activationEvents": [
"workspaceContains:Cargo.toml",

View file

@ -0,0 +1,12 @@
import { type Config } from "prettier";
const config: Config = {
// use 4 because it's Rustfmt's default
// https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#%5C34%20%5C%20%5C(default%5C)%5C%3A
tabWidth: 4,
// use 100 because it's Rustfmt's default
// https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#max_width
printWidth: 100,
};
export default config;

View file

@ -177,9 +177,9 @@ async function hasToolchainFileWithRaDeclared(uri: vscode.Uri): Promise<boolean>
await vscode.workspace.fs.readFile(uri),
);
return (
toolchainFileContents.match(/components\s*=\s*\[.*\"rust-analyzer\".*\]/g)?.length === 1
toolchainFileContents.match(/components\s*=\s*\[.*"rust-analyzer".*\]/g)?.length === 1
);
} catch (e) {
} catch (_) {
return false;
}
}

View file

@ -18,282 +18,263 @@ export async function createClient(
config: Config,
unlinkedFiles: vscode.Uri[],
): Promise<lc.LanguageClient> {
const raMiddleware: lc.Middleware = {
workspace: {
// HACK: This is a workaround, when the client has been disposed, VSCode
// continues to emit events to the client and the default one for this event
// attempt to restart the client for no reason
async didChangeWatchedFile(event, next) {
if (client.isRunning()) {
await next(event);
}
},
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 prepareVSCodeConfig(val);
});
} else {
return resp;
}
},
},
async handleDiagnostics(
uri: vscode.Uri,
diagnosticList: vscode.Diagnostic[],
next: lc.HandleDiagnosticsSignature,
) {
const preview = config.previewRustcOutput;
const errorCode = config.useRustcErrorCode;
diagnosticList.forEach((diag, idx) => {
const value =
typeof diag.code === "string" || typeof diag.code === "number"
? diag.code
: diag.code?.value;
if (
// FIXME: We currently emit this diagnostic way too early, before we have
// loaded the project fully
// value === "unlinked-file" &&
value === "temporary-disabled" &&
!unlinkedFiles.includes(uri) &&
(diag.message === "file not included in crate hierarchy" ||
diag.message.startsWith("This file is not included in any crates"))
) {
const config = vscode.workspace.getConfiguration("rust-analyzer");
if (config.get("showUnlinkedFileNotification")) {
unlinkedFiles.push(uri);
const folder = vscode.workspace.getWorkspaceFolder(uri)?.uri.fsPath;
if (folder) {
const parentBackslash = uri.fsPath.lastIndexOf(pathSeparator + "src");
const parent = uri.fsPath.substring(0, parentBackslash);
if (parent.startsWith(folder)) {
const path = vscode.Uri.file(parent + pathSeparator + "Cargo.toml");
void vscode.workspace.fs.stat(path).then(async () => {
const choice = await vscode.window.showInformationMessage(
`This rust file does not belong to a loaded cargo project. It looks like it might belong to the workspace at ${path.path}, do you want to add it to the linked Projects?`,
"Yes",
"No",
"Don't show this again",
);
switch (choice) {
case undefined:
break;
case "No":
break;
case "Yes": {
const pathToInsert =
"." +
parent.substring(folder.length) +
pathSeparator +
"Cargo.toml";
const value = config
// eslint-disable-next-line @typescript-eslint/no-explicit-any
.get<any[]>("linkedProjects")
?.concat(pathToInsert);
await config.update("linkedProjects", value, false);
break;
}
case "Don't show this again":
await config.update(
"showUnlinkedFileNotification",
false,
false,
);
break;
}
});
}
}
}
}
// Abuse the fact that VSCode leaks the LSP diagnostics data field through the
// Diagnostic class, if they ever break this we are out of luck and have to go
// back to the worst diagnostics experience ever:)
// We encode the rendered output of a rustc diagnostic in the rendered field of
// the data payload of the lsp diagnostic. If that field exists, overwrite the
// diagnostic code such that clicking it opens the diagnostic in a readonly
// text editor for easy inspection
const rendered = (diag as unknown as { data?: { rendered?: string } }).data
?.rendered;
if (rendered) {
if (preview) {
const decolorized = anser.ansiToText(rendered);
const index = decolorized.match(/^(note|help):/m)?.index || rendered.length;
diag.message = decolorized
.substring(0, index)
.replace(/^ -->[^\n]+\n/m, "");
}
diag.code = {
target: vscode.Uri.from({
scheme: diagnostics.URI_SCHEME,
path: `/diagnostic message [${idx.toString()}]`,
fragment: uri.toString(),
query: idx.toString(),
}),
value: errorCode && value ? value : "Click for full compiler diagnostic",
};
}
});
return next(uri, diagnosticList);
},
async provideHover(
document: vscode.TextDocument,
position: vscode.Position,
token: vscode.CancellationToken,
_next: lc.ProvideHoverSignature,
) {
const editor = vscode.window.activeTextEditor;
const positionOrRange = editor?.selection?.contains(position)
? client.code2ProtocolConverter.asRange(editor.selection)
: client.code2ProtocolConverter.asPosition(position);
const params = {
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
position: positionOrRange,
};
return client.sendRequest(ra.hover, params, token).then(
(result) => {
if (!result) return null;
const hover = client.protocol2CodeConverter.asHover(result);
if (result.actions) {
hover.contents.push(renderHoverActions(result.actions));
}
return hover;
},
(error) => {
client.handleFailedRequest(lc.HoverRequest.type, token, error, null);
return Promise.resolve(null);
},
);
},
// Using custom handling of CodeActions to support action groups and snippet edits.
// Note that this means we have to re-implement lazy edit resolving ourselves as well.
async provideCodeActions(
document: vscode.TextDocument,
range: vscode.Range,
context: vscode.CodeActionContext,
token: vscode.CancellationToken,
_next: lc.ProvideCodeActionsSignature,
) {
const params: lc.CodeActionParams = {
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
range: client.code2ProtocolConverter.asRange(range),
context: await client.code2ProtocolConverter.asCodeActionContext(context, token),
};
const callback = async (
values: (lc.Command | lc.CodeAction)[] | null,
): Promise<(vscode.Command | vscode.CodeAction)[] | undefined> => {
if (values === null) return undefined;
const result: (vscode.CodeAction | vscode.Command)[] = [];
const groups = new Map<string, { index: number; items: vscode.CodeAction[] }>();
for (const item of values) {
// In our case we expect to get code edits only from diagnostics
if (lc.CodeAction.is(item)) {
assert(!item.command, "We don't expect to receive commands in CodeActions");
const action = await client.protocol2CodeConverter.asCodeAction(
item,
token,
);
result.push(action);
continue;
}
assert(
isCodeActionWithoutEditsAndCommands(item),
"We don't expect edits or commands here",
);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const kind = client.protocol2CodeConverter.asCodeActionKind((item as any).kind);
const action = new vscode.CodeAction(item.title, kind);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const group = (item as any).group;
action.command = {
command: "rust-analyzer.resolveCodeAction",
title: item.title,
arguments: [item],
};
// Set a dummy edit, so that VS Code doesn't try to resolve this.
action.edit = new WorkspaceEdit();
if (group) {
let entry = groups.get(group);
if (!entry) {
entry = { index: result.length, items: [] };
groups.set(group, entry);
result.push(action);
}
entry.items.push(action);
} else {
result.push(action);
}
}
for (const [group, { index, items }] of groups) {
if (items.length === 1) {
const item = unwrapUndefinable(items[0]);
result[index] = item;
} else {
const action = new vscode.CodeAction(group);
const item = unwrapUndefinable(items[0]);
action.kind = item.kind;
action.command = {
command: "rust-analyzer.applyActionGroup",
title: "",
arguments: [
items.map((item) => {
return {
label: item.title,
arguments: item.command!.arguments![0],
};
}),
],
};
// Set a dummy edit, so that VS Code doesn't try to resolve this.
action.edit = new WorkspaceEdit();
result[index] = action;
}
}
return result;
};
return client
.sendRequest(lc.CodeActionRequest.type, params, token)
.then(callback, (_error) => undefined);
},
};
const clientOptions: lc.LanguageClientOptions = {
documentSelector: [{ scheme: "file", language: "rust" }],
initializationOptions,
diagnosticCollectionName: "rustc",
traceOutputChannel,
outputChannel,
middleware: {
workspace: {
// HACK: This is a workaround, when the client has been disposed, VSCode
// continues to emit events to the client and the default one for this event
// attempt to restart the client for no reason
async didChangeWatchedFile(event, next) {
if (client.isRunning()) {
await next(event);
}
},
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 prepareVSCodeConfig(val);
});
} else {
return resp;
}
},
},
async handleDiagnostics(
uri: vscode.Uri,
diagnosticList: vscode.Diagnostic[],
next: lc.HandleDiagnosticsSignature,
) {
const preview = config.previewRustcOutput;
const errorCode = config.useRustcErrorCode;
diagnosticList.forEach((diag, idx) => {
const value =
typeof diag.code === "string" || typeof diag.code === "number"
? diag.code
: diag.code?.value;
if (
// FIXME: We currently emit this diagnostic way too early, before we have
// loaded the project fully
// value === "unlinked-file" &&
value === "temporary-disabled" &&
!unlinkedFiles.includes(uri) &&
(diag.message === "file not included in crate hierarchy" ||
diag.message.startsWith("This file is not included in any crates"))
) {
const config = vscode.workspace.getConfiguration("rust-analyzer");
if (config.get("showUnlinkedFileNotification")) {
unlinkedFiles.push(uri);
const folder = vscode.workspace.getWorkspaceFolder(uri)?.uri.fsPath;
if (folder) {
const parentBackslash = uri.fsPath.lastIndexOf(
pathSeparator + "src",
);
const parent = uri.fsPath.substring(0, parentBackslash);
if (parent.startsWith(folder)) {
const path = vscode.Uri.file(
parent + pathSeparator + "Cargo.toml",
);
void vscode.workspace.fs.stat(path).then(async () => {
const choice = await vscode.window.showInformationMessage(
`This rust file does not belong to a loaded cargo project. It looks like it might belong to the workspace at ${path.path}, do you want to add it to the linked Projects?`,
"Yes",
"No",
"Don't show this again",
);
switch (choice) {
case undefined:
break;
case "No":
break;
case "Yes":
const pathToInsert =
"." +
parent.substring(folder.length) +
pathSeparator +
"Cargo.toml";
await config.update(
"linkedProjects",
config
.get<any[]>("linkedProjects")
?.concat(pathToInsert),
false,
);
break;
case "Don't show this again":
await config.update(
"showUnlinkedFileNotification",
false,
false,
);
break;
}
});
}
}
}
}
// Abuse the fact that VSCode leaks the LSP diagnostics data field through the
// Diagnostic class, if they ever break this we are out of luck and have to go
// back to the worst diagnostics experience ever:)
// We encode the rendered output of a rustc diagnostic in the rendered field of
// the data payload of the lsp diagnostic. If that field exists, overwrite the
// diagnostic code such that clicking it opens the diagnostic in a readonly
// text editor for easy inspection
const rendered = (diag as unknown as { data?: { rendered?: string } }).data
?.rendered;
if (rendered) {
if (preview) {
const decolorized = anser.ansiToText(rendered);
const index =
decolorized.match(/^(note|help):/m)?.index || rendered.length;
diag.message = decolorized
.substring(0, index)
.replace(/^ -->[^\n]+\n/m, "");
}
diag.code = {
target: vscode.Uri.from({
scheme: diagnostics.URI_SCHEME,
path: `/diagnostic message [${idx.toString()}]`,
fragment: uri.toString(),
query: idx.toString(),
}),
value:
errorCode && value ? value : "Click for full compiler diagnostic",
};
}
});
return next(uri, diagnosticList);
},
async provideHover(
document: vscode.TextDocument,
position: vscode.Position,
token: vscode.CancellationToken,
_next: lc.ProvideHoverSignature,
) {
const editor = vscode.window.activeTextEditor;
const positionOrRange = editor?.selection?.contains(position)
? client.code2ProtocolConverter.asRange(editor.selection)
: client.code2ProtocolConverter.asPosition(position);
return client
.sendRequest(
ra.hover,
{
textDocument:
client.code2ProtocolConverter.asTextDocumentIdentifier(document),
position: positionOrRange,
},
token,
)
.then(
(result) => {
if (!result) return null;
const hover = client.protocol2CodeConverter.asHover(result);
if (!!result.actions) {
hover.contents.push(renderHoverActions(result.actions));
}
return hover;
},
(error) => {
client.handleFailedRequest(lc.HoverRequest.type, token, error, null);
return Promise.resolve(null);
},
);
},
// Using custom handling of CodeActions to support action groups and snippet edits.
// Note that this means we have to re-implement lazy edit resolving ourselves as well.
async provideCodeActions(
document: vscode.TextDocument,
range: vscode.Range,
context: vscode.CodeActionContext,
token: vscode.CancellationToken,
_next: lc.ProvideCodeActionsSignature,
) {
const params: lc.CodeActionParams = {
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
range: client.code2ProtocolConverter.asRange(range),
context: await client.code2ProtocolConverter.asCodeActionContext(
context,
token,
),
};
return client.sendRequest(lc.CodeActionRequest.type, params, token).then(
async (values) => {
if (values === null) return undefined;
const result: (vscode.CodeAction | vscode.Command)[] = [];
const groups = new Map<
string,
{ index: number; items: vscode.CodeAction[] }
>();
for (const item of values) {
// In our case we expect to get code edits only from diagnostics
if (lc.CodeAction.is(item)) {
assert(
!item.command,
"We don't expect to receive commands in CodeActions",
);
const action = await client.protocol2CodeConverter.asCodeAction(
item,
token,
);
result.push(action);
continue;
}
assert(
isCodeActionWithoutEditsAndCommands(item),
"We don't expect edits or commands here",
);
const kind = client.protocol2CodeConverter.asCodeActionKind(
(item as any).kind,
);
const action = new vscode.CodeAction(item.title, kind);
const group = (item as any).group;
action.command = {
command: "rust-analyzer.resolveCodeAction",
title: item.title,
arguments: [item],
};
// Set a dummy edit, so that VS Code doesn't try to resolve this.
action.edit = new WorkspaceEdit();
if (group) {
let entry = groups.get(group);
if (!entry) {
entry = { index: result.length, items: [] };
groups.set(group, entry);
result.push(action);
}
entry.items.push(action);
} else {
result.push(action);
}
}
for (const [group, { index, items }] of groups) {
if (items.length === 1) {
const item = unwrapUndefinable(items[0]);
result[index] = item;
} else {
const action = new vscode.CodeAction(group);
const item = unwrapUndefinable(items[0]);
action.kind = item.kind;
action.command = {
command: "rust-analyzer.applyActionGroup",
title: "",
arguments: [
items.map((item) => {
return {
label: item.title,
arguments: item.command!.arguments![0],
};
}),
],
};
// Set a dummy edit, so that VS Code doesn't try to resolve this.
action.edit = new WorkspaceEdit();
result[index] = action;
}
}
return result;
},
(_error) => undefined,
);
},
},
middleware: raMiddleware,
markdown: {
supportHtml: true,
},
@ -319,9 +300,11 @@ class ExperimentalFeatures implements lc.StaticFeature {
constructor(config: Config) {
this.testExplorer = config.testExplorer || false;
}
getState(): lc.FeatureState {
return { kind: "static" };
}
fillClientCapabilities(capabilities: lc.ClientCapabilities): void {
capabilities.experimental = {
snippetTextEdit: true,
@ -345,11 +328,14 @@ class ExperimentalFeatures implements lc.StaticFeature {
...capabilities.experimental,
};
}
initialize(
_capabilities: lc.ServerCapabilities,
_documentSelector: lc.DocumentSelector | undefined,
): void {}
dispose(): void {}
clear(): void {}
}
@ -357,6 +343,7 @@ class OverrideFeatures implements lc.StaticFeature {
getState(): lc.FeatureState {
return { kind: "static" };
}
fillClientCapabilities(capabilities: lc.ClientCapabilities): void {
// Force disable `augmentsSyntaxTokens`, VSCode's textmate grammar is somewhat incomplete
// making the experience generally worse
@ -365,14 +352,18 @@ class OverrideFeatures implements lc.StaticFeature {
caps.augmentsSyntaxTokens = false;
}
}
initialize(
_capabilities: lc.ServerCapabilities,
_documentSelector: lc.DocumentSelector | undefined,
): void {}
dispose(): void {}
clear(): void {}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function isCodeActionWithoutEditsAndCommands(value: any): boolean {
const candidate: lc.CodeAction = value;
return (

View file

@ -78,6 +78,7 @@ export function memoryUsage(ctx: CtxInit): Cmd {
provideTextDocumentContent(_uri: vscode.Uri): vscode.ProviderResult<string> {
if (!vscode.window.activeTextEditor) return "";
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return ctx.client.sendRequest(ra.memoryUsage).then((mem: any) => {
return "Per-query memory usage:\n" + mem + "\n(note: database has been cleared)";
});
@ -161,7 +162,7 @@ export function joinLines(ctx: CtxInit): Cmd {
});
const textEdits = await client.protocol2CodeConverter.asTextEdits(items);
await editor.edit((builder) => {
textEdits.forEach((edit: any) => {
textEdits.forEach((edit: vscode.TextEdit) => {
builder.replace(edit.range, edit.newText);
});
});
@ -209,6 +210,7 @@ export function onEnter(ctx: CtxInit): Cmd {
),
position: client.code2ProtocolConverter.asPosition(editor.selection.active),
})
// eslint-disable-next-line @typescript-eslint/no-explicit-any
.catch((_error: any) => {
// client.handleFailedRequest(OnEnterRequest.type, error, null);
return null;
@ -528,6 +530,7 @@ function viewFileUsingTextDocumentContentProvider(
void sleep(10).then(() => this.eventEmitter.fire(this.uri));
}
}
private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) {
if (editor && isRustEditor(editor) && shouldUpdate) {
this.eventEmitter.fire(this.uri);
@ -620,6 +623,7 @@ export function viewFileText(ctx: CtxInit): Cmd {
void sleep(10).then(() => this.eventEmitter.fire(this.uri));
}
}
private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) {
if (editor && isRustEditor(editor)) {
this.eventEmitter.fire(this.uri);
@ -683,6 +687,7 @@ export function viewItemTree(ctx: CtxInit): Cmd {
void sleep(10).then(() => this.eventEmitter.fire(this.uri));
}
}
private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) {
if (editor && isRustEditor(editor)) {
this.eventEmitter.fire(this.uri);
@ -1001,9 +1006,8 @@ export function resolveCodeAction(ctx: CtxInit): Cmd {
...itemEdit,
documentChanges: itemEdit.documentChanges?.filter((change) => "kind" in change),
};
const fileSystemEdit = await client.protocol2CodeConverter.asWorkspaceEdit(
lcFileSystemEdit,
);
const fileSystemEdit =
await client.protocol2CodeConverter.asWorkspaceEdit(lcFileSystemEdit);
await vscode.workspace.applyEdit(fileSystemEdit);
// replace all text edits so that we can convert snippet text edits into `vscode.SnippetTextEdit`s

View file

@ -13,12 +13,7 @@ export type RunnableEnvCfgItem = {
};
export type RunnableEnvCfg = Record<string, string> | RunnableEnvCfgItem[];
type ShowStatusBar =
| "always"
| "never"
| {
documentSelector: vscode.DocumentSelector;
};
type ShowStatusBar = "always" | "never" | { documentSelector: vscode.DocumentSelector };
export class Config {
readonly extensionId = "rust-lang.rust-analyzer";
@ -145,13 +140,13 @@ export class Config {
{
// Parent doc single-line comment
// e.g. //!|
beforeText: /^\s*\/{2}\!.*$/,
beforeText: /^\s*\/{2}!.*$/,
action: { indentAction, appendText: "//! " },
},
{
// Begins an auto-closed multi-line comment (standard or parent doc)
// e.g. /** | */ or /*! | */
beforeText: /^\s*\/\*(\*|\!)(?!\/)([^\*]|\*(?!\/))*$/,
beforeText: /^\s*\/\*(\*|!)(?!\/)([^*]|\*(?!\/))*$/,
afterText: /^\s*\*\/$/,
action: {
indentAction: vscode.IndentAction.IndentOutdent,
@ -161,19 +156,19 @@ export class Config {
{
// Begins a multi-line comment (standard or parent doc)
// e.g. /** ...| or /*! ...|
beforeText: /^\s*\/\*(\*|\!)(?!\/)([^\*]|\*(?!\/))*$/,
beforeText: /^\s*\/\*(\*|!)(?!\/)([^*]|\*(?!\/))*$/,
action: { indentAction, appendText: " * " },
},
{
// Continues a multi-line comment
// e.g. * ...|
beforeText: /^(\ \ )*\ \*(\ ([^\*]|\*(?!\/))*)?$/,
beforeText: /^( {2})* \*( ([^*]|\*(?!\/))*)?$/,
action: { indentAction, appendText: "* " },
},
{
// Dedents after closing a multi-line comment
// e.g. */|
beforeText: /^(\ \ )*\ \*\/\s*$/,
beforeText: /^( {2})* \*\/\s*$/,
action: { indentAction, removeText: 1 },
},
];
@ -227,9 +222,11 @@ export class Config {
),
);
}
get checkOnSave() {
return this.get<boolean>("checkOnSave") ?? false;
}
async toggleCheckOnSave() {
const config = this.cfg.inspect<boolean>("checkOnSave") ?? { key: "checkOnSave" };
let overrideInLanguage;
@ -269,8 +266,10 @@ export class Config {
}
runnablesExtraEnv(label: string): Record<string, string> | undefined {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const item = this.get<any>("runnables.extraEnv") ?? this.get<any>("runnableEnv");
if (!item) return undefined;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const fixRecord = (r: Record<string, any>) => {
for (const key in r) {
if (typeof r[key] !== "string") {
@ -339,6 +338,7 @@ export class Config {
gotoTypeDef: this.get<boolean>("hover.actions.gotoTypeDef.enable"),
};
}
get previewRustcOutput() {
return this.get<boolean>("diagnostics.previewRustcOutput");
}
@ -370,6 +370,7 @@ export class Config {
get askBeforeUpdateTest() {
return this.get<boolean>("runnables.askBeforeUpdateTest");
}
async setAskBeforeUpdateTest(value: boolean) {
await this.cfg.update("runnables.askBeforeUpdateTest", value, true);
}
@ -378,11 +379,13 @@ export class Config {
export function prepareVSCodeConfig<T>(resp: T): T {
if (Is.string(resp)) {
return substituteVSCodeVariableInString(resp) as T;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} else if (resp && Is.array<any>(resp)) {
return resp.map((val) => {
return prepareVSCodeConfig(val);
}) as T;
} else if (resp && typeof resp === "object") {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const res: { [key: string]: any } = {};
for (const key in resp) {
const val = resp[key];
@ -489,8 +492,7 @@ function computeVscodeVar(varName: string): string | null {
// TODO: support for remote workspaces?
const fsPath: string =
folder === undefined
? // no workspace opened
""
? "" // no workspace opened
: // 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

View file

@ -34,13 +34,8 @@ import type { RustAnalyzerExtensionApi } from "./main";
export type Workspace =
| { kind: "Empty" }
| {
kind: "Workspace Folder";
}
| {
kind: "Detached Files";
files: vscode.TextDocument[];
};
| { kind: "Workspace Folder" }
| { kind: "Detached Files"; files: vscode.TextDocument[] };
export function fetchWorkspace(): Workspace {
const folders = (vscode.workspace.workspaceFolders || []).filter(
@ -53,10 +48,7 @@ export function fetchWorkspace(): Workspace {
return folders.length === 0
? rustDocuments.length === 0
? { kind: "Empty" }
: {
kind: "Detached Files",
files: rustDocuments,
}
: { kind: "Detached Files", files: rustDocuments }
: { kind: "Workspace Folder" };
}
@ -89,6 +81,7 @@ export class Ctx implements RustAnalyzerExtensionApi {
private _dependencyTreeView:
| vscode.TreeView<Dependency | DependencyFile | DependencyId>
| undefined;
private _syntaxTreeProvider: SyntaxTreeProvider | undefined;
private _syntaxTreeView: vscode.TreeView<SyntaxElement> | undefined;
private lastStatus: ServerStatusParams | { health: "stopped" } = { health: "stopped" };
@ -267,7 +260,7 @@ export class Ctx implements RustAnalyzerExtensionApi {
let message = "bootstrap error. ";
message +=
'See the logs in "OUTPUT > Rust Analyzer Client" (should open automatically). ';
'See the logs in "OUTPUT > Rust Analyzer Client" (should open automatically).';
message +=
'To enable verbose logs, click the gear icon in the "OUTPUT" tab and select "Debug".';
@ -476,9 +469,11 @@ export class Ctx implements RustAnalyzerExtensionApi {
this.lastStatus = status;
this.updateStatusBarItem();
}
refreshServerStatus() {
this.updateStatusBarItem();
}
private updateStatusBarItem() {
let icon = "";
const status = this.lastStatus;
@ -533,19 +528,14 @@ export class Ctx implements RustAnalyzerExtensionApi {
const toggleCheckOnSave = this.config.checkOnSave ? "Disable" : "Enable";
statusBar.tooltip.appendMarkdown(
`[Extension Info](command:rust-analyzer.serverVersion "Show version and server binary info"): Version ${this.version}, Server Version ${this._serverVersion}` +
"\n\n---\n\n" +
'[$(terminal) Open Logs](command:rust-analyzer.openLogs "Open the server logs")' +
"\n\n" +
`[$(settings) ${toggleCheckOnSave} Check on Save](command:rust-analyzer.toggleCheckOnSave "Temporarily ${toggleCheckOnSave.toLowerCase()} check on save functionality")` +
"\n\n" +
'[$(refresh) Reload Workspace](command:rust-analyzer.reloadWorkspace "Reload and rediscover workspaces")' +
"\n\n" +
'[$(symbol-property) Rebuild Build Dependencies](command:rust-analyzer.rebuildProcMacros "Rebuild build scripts and proc-macros")' +
"\n\n" +
'[$(stop-circle) Stop server](command:rust-analyzer.stopServer "Stop the server")' +
"\n\n" +
'[$(debug-restart) Restart server](command:rust-analyzer.restartServer "Restart the server")',
`[Extension Info](command:rust-analyzer.serverVersion "Show version and server binary info"): Version ${this.version}, Server Version ${this._serverVersion}\n\n` +
`---\n\n` +
`[$(terminal) Open Logs](command:rust-analyzer.openLogs "Open the server logs")\n\n` +
`[$(settings) ${toggleCheckOnSave} Check on Save](command:rust-analyzer.toggleCheckOnSave "Temporarily ${toggleCheckOnSave.toLowerCase()} check on save functionality")\n\n` +
`[$(refresh) Reload Workspace](command:rust-analyzer.reloadWorkspace "Reload and rediscover workspaces")\n\n` +
`[$(symbol-property) Rebuild Build Dependencies](command:rust-analyzer.rebuildProcMacros "Rebuild build scripts and proc-macros")\n\n` +
`[$(stop-circle) Stop server](command:rust-analyzer.stopServer "Stop the server")\n\n` +
`[$(debug-restart) Restart server](command:rust-analyzer.restartServer "Restart the server")`,
);
if (!status.quiescent) icon = "$(loading~spin) ";
statusBar.text = `${icon}rust-analyzer`;
@ -580,4 +570,5 @@ export interface Disposable {
dispose(): void;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type Cmd = (...args: any[]) => unknown;

View file

@ -22,6 +22,7 @@ export async function makeDebugConfig(ctx: Ctx, runnable: ra.Runnable): Promise<
if (!debugConfig) return;
const wsLaunchSection = vscode.workspace.getConfiguration("launch", scope);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const configurations = wsLaunchSection.get<any[]>("configurations") || [];
const index = configurations.findIndex((c) => c.name === debugConfig.name);
@ -46,6 +47,7 @@ export async function startDebugSession(ctx: Ctx, runnable: ra.Runnable): Promis
let message = "";
const wsLaunchSection = vscode.workspace.getConfiguration("launch");
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const configurations = wsLaunchSection.get<any[]>("configurations") || [];
// The runnable label is the name of the test with the "test prefix"
@ -121,7 +123,7 @@ async function getDebugConfiguration(
debugOutput.show(true);
}
// folder exists or RA is not active.
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
const workspaceFolders = vscode.workspace.workspaceFolders!;
const isMultiFolderWorkspace = workspaceFolders.length > 1;
const firstWorkspace = workspaceFolders[0];
@ -189,8 +191,9 @@ async function getDebugConfiguration(
sourceFileMap,
);
if (debugConfig.type in debugOptions.engineSettings) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const settingsMap = (debugOptions.engineSettings as any)[debugConfig.type];
for (var key in settingsMap) {
for (const key in settingsMap) {
debugConfig[key] = settingsMap[key];
}
}
@ -409,7 +412,7 @@ function quote(xs: string[]) {
return "'" + s.replace(/(['\\])/g, "\\$1") + "'";
}
if (/["'\s]/.test(s)) {
return '"' + s.replace(/(["\\$`!])/g, "\\$1") + '"';
return `"${s.replace(/(["\\$`!])/g, "\\$1")}"`;
}
return s.replace(/([A-Za-z]:)?([#!"$&'()*,:;<=>?@[\\\]^`{|}])/g, "$1\\$2");
})

View file

@ -104,10 +104,7 @@ export class AnsiDecorationProvider implements vscode.Disposable {
for (const [lineNumber, line] of lines.entries()) {
const totalEscapeLength = 0;
// eslint-disable-next-line camelcase
const parsed = anser.ansiToJson(line, { use_classes: true });
let offset = 0;
for (const span of parsed) {
@ -162,23 +159,23 @@ export class AnsiDecorationProvider implements vscode.Disposable {
// NOTE: This could just be a kebab-case to camelCase conversion, but I think it's
// a short enough list to just write these by hand
static readonly _anserToThemeColor: Record<string, ThemeColor> = {
"ansi-black": "ansiBlack",
"ansi-white": "ansiWhite",
"ansi-red": "ansiRed",
"ansi-green": "ansiGreen",
"ansi-yellow": "ansiYellow",
"ansi-blue": "ansiBlue",
"ansi-magenta": "ansiMagenta",
"ansi-cyan": "ansiCyan",
"ansi-black": new ThemeColor("terminal.ansiBlack"),
"ansi-white": new ThemeColor("terminal.ansiWhite"),
"ansi-red": new ThemeColor("terminal.ansiRed"),
"ansi-green": new ThemeColor("terminal.ansiGreen"),
"ansi-yellow": new ThemeColor("terminal.ansiYellow"),
"ansi-blue": new ThemeColor("terminal.ansiBlue"),
"ansi-magenta": new ThemeColor("terminal.ansiMagenta"),
"ansi-cyan": new ThemeColor("terminal.ansiCyan"),
"ansi-bright-black": "ansiBrightBlack",
"ansi-bright-white": "ansiBrightWhite",
"ansi-bright-red": "ansiBrightRed",
"ansi-bright-green": "ansiBrightGreen",
"ansi-bright-yellow": "ansiBrightYellow",
"ansi-bright-blue": "ansiBrightBlue",
"ansi-bright-magenta": "ansiBrightMagenta",
"ansi-bright-cyan": "ansiBrightCyan",
"ansi-bright-black": new ThemeColor("terminal.ansiBrightBlack"),
"ansi-bright-white": new ThemeColor("terminal.ansiBrightWhite"),
"ansi-bright-red": new ThemeColor("terminal.ansiBrightRed"),
"ansi-bright-green": new ThemeColor("terminal.ansiBrightGreen"),
"ansi-bright-yellow": new ThemeColor("terminal.ansiBrightYellow"),
"ansi-bright-blue": new ThemeColor("terminal.ansiBrightBlue"),
"ansi-bright-magenta": new ThemeColor("terminal.ansiBrightMagenta"),
"ansi-bright-cyan": new ThemeColor("terminal.ansiBrightCyan"),
};
private static _convertColor(

View file

@ -5,6 +5,8 @@ export class RaLanguageClient extends lc.LanguageClient {
override handleFailedRequest<T>(
type: lc.MessageSignature,
token: vscode.CancellationToken | undefined,
// declared as `any` in vscode-languageclient
// eslint-disable-next-line @typescript-eslint/no-explicit-any
error: any,
defaultValue: T,
showNotification?: boolean | undefined,

View file

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-empty-object-type */
/**
* This file mirrors `crates/rust-analyzer/src/lsp_ext.rs` declarations.
*/

View file

@ -214,6 +214,7 @@ function checkConflictingExtensions() {
"both plugins to not work correctly. You should disable one of them.",
"Got it",
)
// eslint-disable-next-line no-console
.then(() => {}, console.error);
}
}

View file

@ -14,6 +14,7 @@ export class PersistentState {
get serverVersion(): string | undefined {
return this.globalState.get("serverVersion");
}
async updateServerVersion(value: string | undefined) {
await this.globalState.update("serverVersion", value);
}

View file

@ -24,9 +24,7 @@ export async function applySnippetWorkspaceEdit(
for (const indel of edits) {
assert(
!(indel instanceof vscode.SnippetTextEdit),
`bad ws edit: snippet received with multiple edits: ${JSON.stringify(
edit,
)}`,
`bad ws edit: snippet received with multiple edits: ${JSON.stringify(edit)}`,
);
builder.replace(indel.range, indel.newText);
}

View file

@ -7,8 +7,10 @@ import * as ra from "./lsp_ext";
export class SyntaxTreeProvider implements vscode.TreeDataProvider<SyntaxElement> {
private _onDidChangeTreeData: vscode.EventEmitter<SyntaxElement | undefined | void> =
new vscode.EventEmitter<SyntaxElement | undefined | void>();
readonly onDidChangeTreeData: vscode.Event<SyntaxElement | undefined | void> =
this._onDidChangeTreeData.event;
ctx: CtxInit;
root: SyntaxNode | undefined;
hideWhitespace: boolean = false;

View file

@ -133,7 +133,7 @@ export const prepareTestExplorer = (
}
if (scope) {
const recursivelyRemove = (tests: vscode.TestItemCollection) => {
for (const [_, test] of tests) {
for (const [, test] of tests) {
if (!testSet.has(test.id)) {
deleteTest(test, tests);
} else {

View file

@ -18,6 +18,22 @@ export interface ArtifactSpec {
filter?: (artifacts: CompilationArtifact[]) => CompilationArtifact[];
}
interface CompilerMessage {
reason: string;
executable?: string;
target: {
crate_types: [string, ...string[]];
kind: [string, ...string[]];
name: string;
};
profile: {
test: boolean;
};
message: {
rendered: string;
};
}
export class Cargo {
constructor(
readonly rootFolder: string,
@ -109,7 +125,7 @@ export class Cargo {
private async runCargo(
cargoArgs: string[],
onStdoutJson: (obj: any) => void,
onStdoutJson: (obj: CompilerMessage) => void,
onStderrString: (data: string) => void,
env?: Record<string, string>,
): Promise<number> {
@ -131,7 +147,7 @@ export class Cargo {
onStdoutJson(message);
});
cargo.on("exit", (exitCode, _) => {
cargo.on("exit", (exitCode) => {
if (exitCode === 0) resolve(exitCode);
else reject(new Error(`exit code: ${exitCode}.`));
});

View file

@ -104,6 +104,7 @@ export function isDocumentInWorkspace(document: RustDocument): boolean {
}
/** Sets ['when'](https://code.visualstudio.com/docs/getstarted/keybindings#_when-clause-contexts) clause contexts */
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function setContextValue(key: string, value: any): Thenable<void> {
return vscode.commands.executeCommand("setContext", key, value);
}
@ -167,27 +168,35 @@ export class LazyOutputChannel implements vscode.OutputChannel {
append(value: string): void {
this.channel.append(value);
}
appendLine(value: string): void {
this.channel.appendLine(value);
}
replace(value: string): void {
this.channel.replace(value);
}
clear(): void {
if (this._channel) {
this._channel.clear();
}
}
show(preserveFocus?: boolean): void;
show(column?: vscode.ViewColumn, preserveFocus?: boolean): void;
show(column?: any, preserveFocus?: any): void {
this.channel.show(column, preserveFocus);
show(columnOrPreserveFocus?: vscode.ViewColumn | boolean, preserveFocus?: boolean): void {
if (typeof columnOrPreserveFocus === "boolean") {
this.channel.show(columnOrPreserveFocus);
} else {
this.channel.show(columnOrPreserveFocus, preserveFocus);
}
}
hide(): void {
if (this._channel) {
this._channel.hide();
}
}
dispose(): void {
if (this._channel) {
this._channel.dispose();
@ -276,6 +285,7 @@ export async function spawnAsync(
stderr: res.stderr,
status: res.status,
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (e: any) {
return {
stdout: e.stdout,

View file

@ -1,6 +1,7 @@
import * as assert from "node:assert/strict";
import { readdir } from "fs/promises";
import * as path from "path";
import { pathToFileURL } from "url";
class Test {
readonly name: string;
@ -67,7 +68,7 @@ export async function run(): Promise<void> {
);
for (const testFile of testFiles) {
try {
const testModule = require(path.resolve(__dirname, testFile));
const testModule = await import(pathToFileURL(path.resolve(__dirname, testFile)).href);
await testModule.getTests(context);
} catch (e) {
error(`${e}`);

View file

@ -114,6 +114,7 @@ function f(task: vscode.Task): {
execution,
};
}
function executionToSimple(
taskExecution: vscode.ProcessExecution | vscode.ShellExecution | vscode.CustomExecution,
): {
@ -122,8 +123,8 @@ function executionToSimple(
const exec = taskExecution as vscode.ProcessExecution | vscode.ShellExecution;
if (exec instanceof vscode.ShellExecution) {
return {
command: typeof exec.command === "string" ? exec.command : exec.command.value,
args: exec.args.map((arg) => {
command: typeof exec.command === "string" ? exec.command : (exec.command?.value ?? ""),
args: (exec.args ?? []).map((arg) => {
if (typeof arg === "string") {
return arg;
}

View file

@ -6,6 +6,6 @@
"src",
"tests",
// these are the eslint-only inclusions
".eslintrc.js"
"eslint.config.mts"
]
}

View file

@ -2,18 +2,17 @@
"extends": "@tsconfig/strictest/tsconfig.json",
"compilerOptions": {
"esModuleInterop": false,
"module": "Node16",
"moduleResolution": "Node16",
"target": "ES2021",
"module": "NodeNext",
"moduleResolution": "nodenext",
"target": "ES2024",
"outDir": "out",
"lib": ["ES2021"],
"lib": ["ES2024"],
"sourceMap": true,
"rootDir": ".",
"newLine": "lf",
// FIXME: https://github.com/rust-lang/rust-analyzer/issues/15253
"exactOptionalPropertyTypes": false
},
"exclude": ["node_modules", ".vscode-test"],
"exclude": ["node_modules", ".vscode-test", "out"],
"include": ["src", "tests"]
}