mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-11-01 04:18:20 +00:00
Update Node.js, vscode, and ts deps
This commit is contained in:
parent
2fa819c9d3
commit
00726cf697
29 changed files with 2715 additions and 1857 deletions
2
.github/workflows/ci.yaml
vendored
2
.github/workflows/ci.yaml
vendored
|
|
@ -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
|
||||||
|
|
|
||||||
4
.github/workflows/release.yaml
vendored
4
.github/workflows/release.yaml
vendored
|
|
@ -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'
|
||||||
|
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
node_modules
|
|
||||||
.eslintrc.js
|
|
||||||
|
|
@ -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",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
@ -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,
|
|
||||||
};
|
|
||||||
48
editors/code/eslint.config.mts
Normal file
48
editors/code/eslint.config.mts
Normal 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;
|
||||||
3670
editors/code/package-lock.json
generated
3670
editors/code/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -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",
|
||||||
|
|
|
||||||
12
editors/code/prettier.config.mts
Normal file
12
editors/code/prettier.config.mts
Normal 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;
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 (
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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}.`));
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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}`);
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue