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

@ -174,7 +174,7 @@ jobs:
- name: Install Nodejs - name: Install Nodejs
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: 18 node-version: 22
if: needs.changes.outputs.typescript == 'true' if: needs.changes.outputs.typescript == 'true'
- name: Install xvfb - name: Install xvfb

View file

@ -69,7 +69,7 @@ jobs:
- name: Install Node.js toolchain - name: Install Node.js toolchain
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: 20 node-version: 22
- name: Install Rust toolchain - name: Install Rust toolchain
run: | run: |
@ -188,7 +188,7 @@ jobs:
- name: Install Nodejs - name: Install Nodejs
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: 20 node-version: 22
- run: echo "TAG=$(date --iso -u)" >> $GITHUB_ENV - run: echo "TAG=$(date --iso -u)" >> $GITHUB_ENV
if: github.ref == 'refs/heads/release' if: github.ref == 'refs/heads/release'

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

View file

@ -18,282 +18,263 @@ export async function createClient(
config: Config, config: Config,
unlinkedFiles: vscode.Uri[], unlinkedFiles: vscode.Uri[],
): Promise<lc.LanguageClient> { ): 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 = { const clientOptions: lc.LanguageClientOptions = {
documentSelector: [{ scheme: "file", language: "rust" }], documentSelector: [{ scheme: "file", language: "rust" }],
initializationOptions, initializationOptions,
diagnosticCollectionName: "rustc", diagnosticCollectionName: "rustc",
traceOutputChannel, traceOutputChannel,
outputChannel, outputChannel,
middleware: { middleware: raMiddleware,
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,
);
},
},
markdown: { markdown: {
supportHtml: true, supportHtml: true,
}, },
@ -319,9 +300,11 @@ class ExperimentalFeatures implements lc.StaticFeature {
constructor(config: Config) { constructor(config: Config) {
this.testExplorer = config.testExplorer || false; this.testExplorer = config.testExplorer || false;
} }
getState(): lc.FeatureState { getState(): lc.FeatureState {
return { kind: "static" }; return { kind: "static" };
} }
fillClientCapabilities(capabilities: lc.ClientCapabilities): void { fillClientCapabilities(capabilities: lc.ClientCapabilities): void {
capabilities.experimental = { capabilities.experimental = {
snippetTextEdit: true, snippetTextEdit: true,
@ -345,11 +328,14 @@ class ExperimentalFeatures implements lc.StaticFeature {
...capabilities.experimental, ...capabilities.experimental,
}; };
} }
initialize( initialize(
_capabilities: lc.ServerCapabilities, _capabilities: lc.ServerCapabilities,
_documentSelector: lc.DocumentSelector | undefined, _documentSelector: lc.DocumentSelector | undefined,
): void {} ): void {}
dispose(): void {} dispose(): void {}
clear(): void {} clear(): void {}
} }
@ -357,6 +343,7 @@ class OverrideFeatures implements lc.StaticFeature {
getState(): lc.FeatureState { getState(): lc.FeatureState {
return { kind: "static" }; return { kind: "static" };
} }
fillClientCapabilities(capabilities: lc.ClientCapabilities): void { fillClientCapabilities(capabilities: lc.ClientCapabilities): void {
// Force disable `augmentsSyntaxTokens`, VSCode's textmate grammar is somewhat incomplete // Force disable `augmentsSyntaxTokens`, VSCode's textmate grammar is somewhat incomplete
// making the experience generally worse // making the experience generally worse
@ -365,14 +352,18 @@ class OverrideFeatures implements lc.StaticFeature {
caps.augmentsSyntaxTokens = false; caps.augmentsSyntaxTokens = false;
} }
} }
initialize( initialize(
_capabilities: lc.ServerCapabilities, _capabilities: lc.ServerCapabilities,
_documentSelector: lc.DocumentSelector | undefined, _documentSelector: lc.DocumentSelector | undefined,
): void {} ): void {}
dispose(): void {} dispose(): void {}
clear(): void {} clear(): void {}
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function isCodeActionWithoutEditsAndCommands(value: any): boolean { function isCodeActionWithoutEditsAndCommands(value: any): boolean {
const candidate: lc.CodeAction = value; const candidate: lc.CodeAction = value;
return ( return (

View file

@ -78,6 +78,7 @@ export function memoryUsage(ctx: CtxInit): Cmd {
provideTextDocumentContent(_uri: vscode.Uri): vscode.ProviderResult<string> { provideTextDocumentContent(_uri: vscode.Uri): vscode.ProviderResult<string> {
if (!vscode.window.activeTextEditor) return ""; if (!vscode.window.activeTextEditor) return "";
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return ctx.client.sendRequest(ra.memoryUsage).then((mem: any) => { return ctx.client.sendRequest(ra.memoryUsage).then((mem: any) => {
return "Per-query memory usage:\n" + mem + "\n(note: database has been cleared)"; 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); const textEdits = await client.protocol2CodeConverter.asTextEdits(items);
await editor.edit((builder) => { await editor.edit((builder) => {
textEdits.forEach((edit: any) => { textEdits.forEach((edit: vscode.TextEdit) => {
builder.replace(edit.range, edit.newText); builder.replace(edit.range, edit.newText);
}); });
}); });
@ -209,6 +210,7 @@ export function onEnter(ctx: CtxInit): Cmd {
), ),
position: client.code2ProtocolConverter.asPosition(editor.selection.active), position: client.code2ProtocolConverter.asPosition(editor.selection.active),
}) })
// eslint-disable-next-line @typescript-eslint/no-explicit-any
.catch((_error: any) => { .catch((_error: any) => {
// client.handleFailedRequest(OnEnterRequest.type, error, null); // client.handleFailedRequest(OnEnterRequest.type, error, null);
return null; return null;
@ -528,6 +530,7 @@ function viewFileUsingTextDocumentContentProvider(
void sleep(10).then(() => this.eventEmitter.fire(this.uri)); void sleep(10).then(() => this.eventEmitter.fire(this.uri));
} }
} }
private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) { private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) {
if (editor && isRustEditor(editor) && shouldUpdate) { if (editor && isRustEditor(editor) && shouldUpdate) {
this.eventEmitter.fire(this.uri); this.eventEmitter.fire(this.uri);
@ -620,6 +623,7 @@ export function viewFileText(ctx: CtxInit): Cmd {
void sleep(10).then(() => this.eventEmitter.fire(this.uri)); void sleep(10).then(() => this.eventEmitter.fire(this.uri));
} }
} }
private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) { private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) {
if (editor && isRustEditor(editor)) { if (editor && isRustEditor(editor)) {
this.eventEmitter.fire(this.uri); this.eventEmitter.fire(this.uri);
@ -683,6 +687,7 @@ export function viewItemTree(ctx: CtxInit): Cmd {
void sleep(10).then(() => this.eventEmitter.fire(this.uri)); void sleep(10).then(() => this.eventEmitter.fire(this.uri));
} }
} }
private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) { private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) {
if (editor && isRustEditor(editor)) { if (editor && isRustEditor(editor)) {
this.eventEmitter.fire(this.uri); this.eventEmitter.fire(this.uri);
@ -1001,9 +1006,8 @@ export function resolveCodeAction(ctx: CtxInit): Cmd {
...itemEdit, ...itemEdit,
documentChanges: itemEdit.documentChanges?.filter((change) => "kind" in change), documentChanges: itemEdit.documentChanges?.filter((change) => "kind" in change),
}; };
const fileSystemEdit = await client.protocol2CodeConverter.asWorkspaceEdit( const fileSystemEdit =
lcFileSystemEdit, await client.protocol2CodeConverter.asWorkspaceEdit(lcFileSystemEdit);
);
await vscode.workspace.applyEdit(fileSystemEdit); await vscode.workspace.applyEdit(fileSystemEdit);
// replace all text edits so that we can convert snippet text edits into `vscode.SnippetTextEdit`s // 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[]; export type RunnableEnvCfg = Record<string, string> | RunnableEnvCfgItem[];
type ShowStatusBar = type ShowStatusBar = "always" | "never" | { documentSelector: vscode.DocumentSelector };
| "always"
| "never"
| {
documentSelector: vscode.DocumentSelector;
};
export class Config { export class Config {
readonly extensionId = "rust-lang.rust-analyzer"; readonly extensionId = "rust-lang.rust-analyzer";
@ -145,13 +140,13 @@ export class Config {
{ {
// Parent doc single-line comment // Parent doc single-line comment
// e.g. //!| // e.g. //!|
beforeText: /^\s*\/{2}\!.*$/, beforeText: /^\s*\/{2}!.*$/,
action: { indentAction, appendText: "//! " }, action: { indentAction, appendText: "//! " },
}, },
{ {
// Begins an auto-closed multi-line comment (standard or parent doc) // Begins an auto-closed multi-line comment (standard or parent doc)
// e.g. /** | */ or /*! | */ // e.g. /** | */ or /*! | */
beforeText: /^\s*\/\*(\*|\!)(?!\/)([^\*]|\*(?!\/))*$/, beforeText: /^\s*\/\*(\*|!)(?!\/)([^*]|\*(?!\/))*$/,
afterText: /^\s*\*\/$/, afterText: /^\s*\*\/$/,
action: { action: {
indentAction: vscode.IndentAction.IndentOutdent, indentAction: vscode.IndentAction.IndentOutdent,
@ -161,19 +156,19 @@ export class Config {
{ {
// Begins a multi-line comment (standard or parent doc) // Begins a multi-line comment (standard or parent doc)
// e.g. /** ...| or /*! ...| // e.g. /** ...| or /*! ...|
beforeText: /^\s*\/\*(\*|\!)(?!\/)([^\*]|\*(?!\/))*$/, beforeText: /^\s*\/\*(\*|!)(?!\/)([^*]|\*(?!\/))*$/,
action: { indentAction, appendText: " * " }, action: { indentAction, appendText: " * " },
}, },
{ {
// Continues a multi-line comment // Continues a multi-line comment
// e.g. * ...| // e.g. * ...|
beforeText: /^(\ \ )*\ \*(\ ([^\*]|\*(?!\/))*)?$/, beforeText: /^( {2})* \*( ([^*]|\*(?!\/))*)?$/,
action: { indentAction, appendText: "* " }, action: { indentAction, appendText: "* " },
}, },
{ {
// Dedents after closing a multi-line comment // Dedents after closing a multi-line comment
// e.g. */| // e.g. */|
beforeText: /^(\ \ )*\ \*\/\s*$/, beforeText: /^( {2})* \*\/\s*$/,
action: { indentAction, removeText: 1 }, action: { indentAction, removeText: 1 },
}, },
]; ];
@ -227,9 +222,11 @@ export class Config {
), ),
); );
} }
get checkOnSave() { get checkOnSave() {
return this.get<boolean>("checkOnSave") ?? false; return this.get<boolean>("checkOnSave") ?? false;
} }
async toggleCheckOnSave() { async toggleCheckOnSave() {
const config = this.cfg.inspect<boolean>("checkOnSave") ?? { key: "checkOnSave" }; const config = this.cfg.inspect<boolean>("checkOnSave") ?? { key: "checkOnSave" };
let overrideInLanguage; let overrideInLanguage;
@ -269,8 +266,10 @@ export class Config {
} }
runnablesExtraEnv(label: string): Record<string, string> | undefined { 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"); const item = this.get<any>("runnables.extraEnv") ?? this.get<any>("runnableEnv");
if (!item) return undefined; if (!item) return undefined;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const fixRecord = (r: Record<string, any>) => { const fixRecord = (r: Record<string, any>) => {
for (const key in r) { for (const key in r) {
if (typeof r[key] !== "string") { if (typeof r[key] !== "string") {
@ -339,6 +338,7 @@ export class Config {
gotoTypeDef: this.get<boolean>("hover.actions.gotoTypeDef.enable"), gotoTypeDef: this.get<boolean>("hover.actions.gotoTypeDef.enable"),
}; };
} }
get previewRustcOutput() { get previewRustcOutput() {
return this.get<boolean>("diagnostics.previewRustcOutput"); return this.get<boolean>("diagnostics.previewRustcOutput");
} }
@ -370,6 +370,7 @@ export class Config {
get askBeforeUpdateTest() { get askBeforeUpdateTest() {
return this.get<boolean>("runnables.askBeforeUpdateTest"); return this.get<boolean>("runnables.askBeforeUpdateTest");
} }
async setAskBeforeUpdateTest(value: boolean) { async setAskBeforeUpdateTest(value: boolean) {
await this.cfg.update("runnables.askBeforeUpdateTest", value, true); await this.cfg.update("runnables.askBeforeUpdateTest", value, true);
} }
@ -378,11 +379,13 @@ export class Config {
export function prepareVSCodeConfig<T>(resp: T): T { export function prepareVSCodeConfig<T>(resp: T): T {
if (Is.string(resp)) { if (Is.string(resp)) {
return substituteVSCodeVariableInString(resp) as T; return substituteVSCodeVariableInString(resp) as T;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} else if (resp && Is.array<any>(resp)) { } else if (resp && Is.array<any>(resp)) {
return resp.map((val) => { return resp.map((val) => {
return prepareVSCodeConfig(val); return prepareVSCodeConfig(val);
}) as T; }) as T;
} else if (resp && typeof resp === "object") { } else if (resp && typeof resp === "object") {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
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];
@ -489,8 +492,7 @@ function computeVscodeVar(varName: string): string | null {
// TODO: support for remote workspaces? // TODO: support for remote workspaces?
const fsPath: string = const fsPath: string =
folder === undefined folder === undefined
? // no workspace opened ? "" // no workspace opened
""
: // could use currently opened document to detect the correct : // could use currently opened document to detect the correct
// workspace. However, that would be determined by the document // workspace. However, that would be determined by the document
// user has opened on Editor startup. Could lead to // user has opened on Editor startup. Could lead to

View file

@ -34,13 +34,8 @@ import type { RustAnalyzerExtensionApi } from "./main";
export type Workspace = export type Workspace =
| { kind: "Empty" } | { kind: "Empty" }
| { | { kind: "Workspace Folder" }
kind: "Workspace Folder"; | { kind: "Detached Files"; files: vscode.TextDocument[] };
}
| {
kind: "Detached Files";
files: vscode.TextDocument[];
};
export function fetchWorkspace(): Workspace { export function fetchWorkspace(): Workspace {
const folders = (vscode.workspace.workspaceFolders || []).filter( const folders = (vscode.workspace.workspaceFolders || []).filter(
@ -53,10 +48,7 @@ export function fetchWorkspace(): Workspace {
return folders.length === 0 return folders.length === 0
? rustDocuments.length === 0 ? rustDocuments.length === 0
? { kind: "Empty" } ? { kind: "Empty" }
: { : { kind: "Detached Files", files: rustDocuments }
kind: "Detached Files",
files: rustDocuments,
}
: { kind: "Workspace Folder" }; : { kind: "Workspace Folder" };
} }
@ -89,6 +81,7 @@ export class Ctx implements RustAnalyzerExtensionApi {
private _dependencyTreeView: private _dependencyTreeView:
| vscode.TreeView<Dependency | DependencyFile | DependencyId> | vscode.TreeView<Dependency | DependencyFile | DependencyId>
| undefined; | undefined;
private _syntaxTreeProvider: SyntaxTreeProvider | undefined; private _syntaxTreeProvider: SyntaxTreeProvider | undefined;
private _syntaxTreeView: vscode.TreeView<SyntaxElement> | undefined; private _syntaxTreeView: vscode.TreeView<SyntaxElement> | undefined;
private lastStatus: ServerStatusParams | { health: "stopped" } = { health: "stopped" }; private lastStatus: ServerStatusParams | { health: "stopped" } = { health: "stopped" };
@ -267,7 +260,7 @@ export class Ctx implements RustAnalyzerExtensionApi {
let message = "bootstrap error. "; let message = "bootstrap error. ";
message += message +=
'See the logs in "OUTPUT > Rust Analyzer Client" (should open automatically). '; 'See the logs in "OUTPUT > Rust Analyzer Client" (should open automatically).';
message += message +=
'To enable verbose logs, click the gear icon in the "OUTPUT" tab and select "Debug".'; '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.lastStatus = status;
this.updateStatusBarItem(); this.updateStatusBarItem();
} }
refreshServerStatus() { refreshServerStatus() {
this.updateStatusBarItem(); this.updateStatusBarItem();
} }
private updateStatusBarItem() { private updateStatusBarItem() {
let icon = ""; let icon = "";
const status = this.lastStatus; const status = this.lastStatus;
@ -533,19 +528,14 @@ export class Ctx implements RustAnalyzerExtensionApi {
const toggleCheckOnSave = this.config.checkOnSave ? "Disable" : "Enable"; const toggleCheckOnSave = this.config.checkOnSave ? "Disable" : "Enable";
statusBar.tooltip.appendMarkdown( statusBar.tooltip.appendMarkdown(
`[Extension Info](command:rust-analyzer.serverVersion "Show version and server binary info"): Version ${this.version}, Server Version ${this._serverVersion}` + `[Extension Info](command:rust-analyzer.serverVersion "Show version and server binary info"): Version ${this.version}, Server Version ${this._serverVersion}\n\n` +
"\n\n---\n\n" + `---\n\n` +
'[$(terminal) Open Logs](command:rust-analyzer.openLogs "Open the server logs")' + `[$(terminal) Open Logs](command:rust-analyzer.openLogs "Open the server logs")\n\n` +
"\n\n" + `[$(settings) ${toggleCheckOnSave} Check on Save](command:rust-analyzer.toggleCheckOnSave "Temporarily ${toggleCheckOnSave.toLowerCase()} check on save functionality")\n\n` +
`[$(settings) ${toggleCheckOnSave} Check on Save](command:rust-analyzer.toggleCheckOnSave "Temporarily ${toggleCheckOnSave.toLowerCase()} check on save functionality")` + `[$(refresh) Reload Workspace](command:rust-analyzer.reloadWorkspace "Reload and rediscover workspaces")\n\n` +
"\n\n" + `[$(symbol-property) Rebuild Build Dependencies](command:rust-analyzer.rebuildProcMacros "Rebuild build scripts and proc-macros")\n\n` +
'[$(refresh) Reload Workspace](command:rust-analyzer.reloadWorkspace "Reload and rediscover workspaces")' + `[$(stop-circle) Stop server](command:rust-analyzer.stopServer "Stop the server")\n\n` +
"\n\n" + `[$(debug-restart) Restart server](command:rust-analyzer.restartServer "Restart the server")`,
'[$(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) "; if (!status.quiescent) icon = "$(loading~spin) ";
statusBar.text = `${icon}rust-analyzer`; statusBar.text = `${icon}rust-analyzer`;
@ -580,4 +570,5 @@ export interface Disposable {
dispose(): void; dispose(): void;
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type Cmd = (...args: any[]) => unknown; 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; if (!debugConfig) return;
const wsLaunchSection = vscode.workspace.getConfiguration("launch", scope); const wsLaunchSection = vscode.workspace.getConfiguration("launch", scope);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const configurations = wsLaunchSection.get<any[]>("configurations") || []; const configurations = wsLaunchSection.get<any[]>("configurations") || [];
const index = configurations.findIndex((c) => c.name === debugConfig.name); 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 = ""; let message = "";
const wsLaunchSection = vscode.workspace.getConfiguration("launch"); const wsLaunchSection = vscode.workspace.getConfiguration("launch");
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const configurations = wsLaunchSection.get<any[]>("configurations") || []; const configurations = wsLaunchSection.get<any[]>("configurations") || [];
// The runnable label is the name of the test with the "test prefix" // The runnable label is the name of the test with the "test prefix"
@ -121,7 +123,7 @@ async function getDebugConfiguration(
debugOutput.show(true); debugOutput.show(true);
} }
// folder exists or RA is not active. // folder exists or RA is not active.
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
const workspaceFolders = vscode.workspace.workspaceFolders!; const workspaceFolders = vscode.workspace.workspaceFolders!;
const isMultiFolderWorkspace = workspaceFolders.length > 1; const isMultiFolderWorkspace = workspaceFolders.length > 1;
const firstWorkspace = workspaceFolders[0]; const firstWorkspace = workspaceFolders[0];
@ -189,8 +191,9 @@ async function getDebugConfiguration(
sourceFileMap, sourceFileMap,
); );
if (debugConfig.type in debugOptions.engineSettings) { if (debugConfig.type in debugOptions.engineSettings) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const settingsMap = (debugOptions.engineSettings as any)[debugConfig.type]; const settingsMap = (debugOptions.engineSettings as any)[debugConfig.type];
for (var key in settingsMap) { for (const key in settingsMap) {
debugConfig[key] = settingsMap[key]; debugConfig[key] = settingsMap[key];
} }
} }
@ -409,7 +412,7 @@ function quote(xs: string[]) {
return "'" + s.replace(/(['\\])/g, "\\$1") + "'"; return "'" + s.replace(/(['\\])/g, "\\$1") + "'";
} }
if (/["'\s]/.test(s)) { if (/["'\s]/.test(s)) {
return '"' + s.replace(/(["\\$`!])/g, "\\$1") + '"'; return `"${s.replace(/(["\\$`!])/g, "\\$1")}"`;
} }
return s.replace(/([A-Za-z]:)?([#!"$&'()*,:;<=>?@[\\\]^`{|}])/g, "$1\\$2"); 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()) { for (const [lineNumber, line] of lines.entries()) {
const totalEscapeLength = 0; const totalEscapeLength = 0;
// eslint-disable-next-line camelcase
const parsed = anser.ansiToJson(line, { use_classes: true }); const parsed = anser.ansiToJson(line, { use_classes: true });
let offset = 0; let offset = 0;
for (const span of parsed) { 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 // 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 // a short enough list to just write these by hand
static readonly _anserToThemeColor: Record<string, ThemeColor> = { static readonly _anserToThemeColor: Record<string, ThemeColor> = {
"ansi-black": "ansiBlack", "ansi-black": new ThemeColor("terminal.ansiBlack"),
"ansi-white": "ansiWhite", "ansi-white": new ThemeColor("terminal.ansiWhite"),
"ansi-red": "ansiRed", "ansi-red": new ThemeColor("terminal.ansiRed"),
"ansi-green": "ansiGreen", "ansi-green": new ThemeColor("terminal.ansiGreen"),
"ansi-yellow": "ansiYellow", "ansi-yellow": new ThemeColor("terminal.ansiYellow"),
"ansi-blue": "ansiBlue", "ansi-blue": new ThemeColor("terminal.ansiBlue"),
"ansi-magenta": "ansiMagenta", "ansi-magenta": new ThemeColor("terminal.ansiMagenta"),
"ansi-cyan": "ansiCyan", "ansi-cyan": new ThemeColor("terminal.ansiCyan"),
"ansi-bright-black": "ansiBrightBlack", "ansi-bright-black": new ThemeColor("terminal.ansiBrightBlack"),
"ansi-bright-white": "ansiBrightWhite", "ansi-bright-white": new ThemeColor("terminal.ansiBrightWhite"),
"ansi-bright-red": "ansiBrightRed", "ansi-bright-red": new ThemeColor("terminal.ansiBrightRed"),
"ansi-bright-green": "ansiBrightGreen", "ansi-bright-green": new ThemeColor("terminal.ansiBrightGreen"),
"ansi-bright-yellow": "ansiBrightYellow", "ansi-bright-yellow": new ThemeColor("terminal.ansiBrightYellow"),
"ansi-bright-blue": "ansiBrightBlue", "ansi-bright-blue": new ThemeColor("terminal.ansiBrightBlue"),
"ansi-bright-magenta": "ansiBrightMagenta", "ansi-bright-magenta": new ThemeColor("terminal.ansiBrightMagenta"),
"ansi-bright-cyan": "ansiBrightCyan", "ansi-bright-cyan": new ThemeColor("terminal.ansiBrightCyan"),
}; };
private static _convertColor( private static _convertColor(

View file

@ -5,6 +5,8 @@ export class RaLanguageClient extends lc.LanguageClient {
override handleFailedRequest<T>( override handleFailedRequest<T>(
type: lc.MessageSignature, type: lc.MessageSignature,
token: vscode.CancellationToken | undefined, token: vscode.CancellationToken | undefined,
// declared as `any` in vscode-languageclient
// eslint-disable-next-line @typescript-eslint/no-explicit-any
error: any, error: any,
defaultValue: T, defaultValue: T,
showNotification?: boolean | undefined, 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. * 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.", "both plugins to not work correctly. You should disable one of them.",
"Got it", "Got it",
) )
// eslint-disable-next-line no-console
.then(() => {}, console.error); .then(() => {}, console.error);
} }
} }

View file

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

View file

@ -24,9 +24,7 @@ export async function applySnippetWorkspaceEdit(
for (const indel of edits) { for (const indel of edits) {
assert( assert(
!(indel instanceof vscode.SnippetTextEdit), !(indel instanceof vscode.SnippetTextEdit),
`bad ws edit: snippet received with multiple edits: ${JSON.stringify( `bad ws edit: snippet received with multiple edits: ${JSON.stringify(edit)}`,
edit,
)}`,
); );
builder.replace(indel.range, indel.newText); 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> { export class SyntaxTreeProvider implements vscode.TreeDataProvider<SyntaxElement> {
private _onDidChangeTreeData: vscode.EventEmitter<SyntaxElement | undefined | void> = private _onDidChangeTreeData: vscode.EventEmitter<SyntaxElement | undefined | void> =
new vscode.EventEmitter<SyntaxElement | undefined | void>(); new vscode.EventEmitter<SyntaxElement | undefined | void>();
readonly onDidChangeTreeData: vscode.Event<SyntaxElement | undefined | void> = readonly onDidChangeTreeData: vscode.Event<SyntaxElement | undefined | void> =
this._onDidChangeTreeData.event; this._onDidChangeTreeData.event;
ctx: CtxInit; ctx: CtxInit;
root: SyntaxNode | undefined; root: SyntaxNode | undefined;
hideWhitespace: boolean = false; hideWhitespace: boolean = false;

View file

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

View file

@ -18,6 +18,22 @@ export interface ArtifactSpec {
filter?: (artifacts: CompilationArtifact[]) => CompilationArtifact[]; 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 { export class Cargo {
constructor( constructor(
readonly rootFolder: string, readonly rootFolder: string,
@ -109,7 +125,7 @@ export class Cargo {
private async runCargo( private async runCargo(
cargoArgs: string[], cargoArgs: string[],
onStdoutJson: (obj: any) => void, onStdoutJson: (obj: CompilerMessage) => void,
onStderrString: (data: string) => void, onStderrString: (data: string) => void,
env?: Record<string, string>, env?: Record<string, string>,
): Promise<number> { ): Promise<number> {
@ -131,7 +147,7 @@ export class Cargo {
onStdoutJson(message); onStdoutJson(message);
}); });
cargo.on("exit", (exitCode, _) => { cargo.on("exit", (exitCode) => {
if (exitCode === 0) resolve(exitCode); if (exitCode === 0) resolve(exitCode);
else reject(new Error(`exit code: ${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 */ /** 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> { export function setContextValue(key: string, value: any): Thenable<void> {
return vscode.commands.executeCommand("setContext", key, value); return vscode.commands.executeCommand("setContext", key, value);
} }
@ -167,27 +168,35 @@ export class LazyOutputChannel implements vscode.OutputChannel {
append(value: string): void { append(value: string): void {
this.channel.append(value); this.channel.append(value);
} }
appendLine(value: string): void { appendLine(value: string): void {
this.channel.appendLine(value); this.channel.appendLine(value);
} }
replace(value: string): void { replace(value: string): void {
this.channel.replace(value); this.channel.replace(value);
} }
clear(): void { clear(): void {
if (this._channel) { if (this._channel) {
this._channel.clear(); this._channel.clear();
} }
} }
show(preserveFocus?: boolean): void;
show(column?: vscode.ViewColumn, preserveFocus?: boolean): void; show(columnOrPreserveFocus?: vscode.ViewColumn | boolean, preserveFocus?: boolean): void {
show(column?: any, preserveFocus?: any): void { if (typeof columnOrPreserveFocus === "boolean") {
this.channel.show(column, preserveFocus); this.channel.show(columnOrPreserveFocus);
} else {
this.channel.show(columnOrPreserveFocus, preserveFocus);
}
} }
hide(): void { hide(): void {
if (this._channel) { if (this._channel) {
this._channel.hide(); this._channel.hide();
} }
} }
dispose(): void { dispose(): void {
if (this._channel) { if (this._channel) {
this._channel.dispose(); this._channel.dispose();
@ -276,6 +285,7 @@ export async function spawnAsync(
stderr: res.stderr, stderr: res.stderr,
status: res.status, status: res.status,
}; };
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (e: any) { } catch (e: any) {
return { return {
stdout: e.stdout, stdout: e.stdout,

View file

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

View file

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

View file

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

View file

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