diff options
Diffstat (limited to 'src/tools/rust-analyzer/editors')
12 files changed, 178 insertions, 81 deletions
diff --git a/src/tools/rust-analyzer/editors/code/README.md b/src/tools/rust-analyzer/editors/code/README.md index 36ab9818822..c02882b4982 100644 --- a/src/tools/rust-analyzer/editors/code/README.md +++ b/src/tools/rust-analyzer/editors/code/README.md @@ -15,19 +15,19 @@ It is recommended over and replaces `rust-lang.rust`. - apply suggestions from errors - ... and many more, check out the [manual] to see them all -[code completion]: https://rust-analyzer.github.io/manual.html#magic-completions -[imports insertion]: https://rust-analyzer.github.io/manual.html#completion-with-autoimport -[definition]: https://rust-analyzer.github.io/manual.html#go-to-definition -[implementation]: https://rust-analyzer.github.io/manual.html#go-to-implementation -[type definition]: https://rust-analyzer.github.io/manual.html#go-to-type-definition -[find all references]: https://rust-analyzer.github.io/manual.html#find-all-references -[workspace symbol search]: https://rust-analyzer.github.io/manual.html#workspace-symbol -[symbol renaming]: https://rust-analyzer.github.io/manual.html#rename -[types and documentation on hover]: https://rust-analyzer.github.io/manual.html#hover -[inlay hints]: https://rust-analyzer.github.io/manual.html#inlay-hints -[semantic syntax highlighting]: https://rust-analyzer.github.io/manual.html#semantic-syntax-highlighting -[assists (code actions)]: https://rust-analyzer.github.io/manual.html#assists-code-actions -[manual]: https://rust-analyzer.github.io/manual.html +[code completion]: https://rust-analyzer.github.io/book/features.html#magic-completions +[imports insertion]: https://rust-analyzer.github.io/book/features.html#completion-with-autoimport +[definition]: https://rust-analyzer.github.io/book/features.html#go-to-definition +[implementation]: https://rust-analyzer.github.io/book/features.html#go-to-implementation +[type definition]: https://rust-analyzer.github.io/book/features.html#go-to-type-definition +[find all references]: https://rust-analyzer.github.io/book/features.html#find-all-references +[workspace symbol search]: https://rust-analyzer.github.io/book/features.html#workspace-symbol +[symbol renaming]: https://rust-analyzer.github.io/book/features.html#rename +[types and documentation on hover]: https://rust-analyzer.github.io/book/features.html#hover +[inlay hints]: https://rust-analyzer.github.io/book/features.html#inlay-hints +[semantic syntax highlighting]: https://rust-analyzer.github.io/book/features.html#semantic-syntax-highlighting +[assists (code actions)]: https://rust-analyzer.github.io/book/assists.html +[manual]: https://rust-analyzer.github.io/book/features.html ## Quick start @@ -41,7 +41,7 @@ It is recommended over and replaces `rust-lang.rust`. This extension provides configurations through VSCode's configuration settings. All configurations are under `rust-analyzer.*`. -See [the manual](https://rust-analyzer.github.io/manual.html#vs-code-2) for more information on VSCode specific configurations. +See [the manual](https://rust-analyzer.github.io/book/editor_features.html#vs-code) for more information on VSCode specific configurations. ## Communication diff --git a/src/tools/rust-analyzer/editors/code/language-configuration.json b/src/tools/rust-analyzer/editors/code/language-configuration.json index 6619d0c85c5..aacd48b0f83 100644 --- a/src/tools/rust-analyzer/editors/code/language-configuration.json +++ b/src/tools/rust-analyzer/editors/code/language-configuration.json @@ -14,9 +14,9 @@ ["(", ")"] ], "autoClosingPairs": [ - { "open": "{", "close": "}" }, - { "open": "[", "close": "]" }, - { "open": "(", "close": ")" }, + { "open": "{", "close": "}", "notIn": ["string"] }, + { "open": "[", "close": "]", "notIn": ["string"] }, + { "open": "(", "close": ")", "notIn": ["string"] }, { "open": "\"", "close": "\"", "notIn": ["string"] }, { "open": "/*", "close": " */", "notIn": ["string"] }, { "open": "`", "close": "`", "notIn": ["string"] }, diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 9df41c7487c..a282eea9997 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -171,6 +171,11 @@ "category": "rust-analyzer" }, { + "command": "rust-analyzer.childModules", + "title": "Locate child modules", + "category": "rust-analyzer" + }, + { "command": "rust-analyzer.joinLines", "title": "Join lines", "category": "rust-analyzer" @@ -538,7 +543,8 @@ "additionalProperties": { "type": [ "string", - "number" + "number", + "null" ] }, "default": null, @@ -606,11 +612,6 @@ "/rustc/<id>": "${env:USERPROFILE}/.rustup/toolchains/<toolchain-id>/lib/rustlib/src/rust" } }, - "rust-analyzer.debug.openDebugPane": { - "markdownDescription": "Whether to open up the `Debug Panel` on debugging start.", - "type": "boolean", - "default": false - }, "rust-analyzer.debug.buildBeforeRestart": { "markdownDescription": "Whether to rebuild the project modules before debugging the same test again", "type": "boolean", @@ -899,6 +900,16 @@ { "title": "cargo", "properties": { + "rust-analyzer.cargo.noDeps": { + "markdownDescription": "Whether to skip fetching dependencies. If set to \"true\", the analysis is performed\nentirely offline, and Cargo metadata for dependencies is not fetched.", + "default": false, + "type": "boolean" + } + } + }, + { + "title": "cargo", + "properties": { "rust-analyzer.cargo.sysroot": { "markdownDescription": "Relative path to the sysroot, or \"discover\" to try to automatically find it via\n\"rustc --print sysroot\".\n\nUnsetting this disables sysroot loading.\n\nThis option does not take effect until rust-analyzer is restarted.", "default": "discover", @@ -1875,7 +1886,7 @@ "title": "imports", "properties": { "rust-analyzer.imports.group.enable": { - "markdownDescription": "Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines.", + "markdownDescription": "Group inserted imports by the [following order](https://rust-analyzer.github.io/book/features.html#auto-import). Groups are separated by newlines.", "default": true, "type": "boolean" } @@ -2823,7 +2834,7 @@ "title": "workspace", "properties": { "rust-analyzer.workspace.discoverConfig": { - "markdownDescription": "Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`].\n\n[`DiscoverWorkspaceConfig`] also requires setting `progress_label` and `files_to_watch`.\n`progress_label` is used for the title in progress indicators, whereas `files_to_watch`\nis used to determine which build system-specific files should be watched in order to\nreload rust-analyzer.\n\nBelow is an example of a valid configuration:\n```json\n\"rust-analyzer.workspace.discoverConfig\": {\n \"command\": [\n \"rust-project\",\n \"develop-json\"\n ],\n \"progressLabel\": \"rust-analyzer\",\n \"filesToWatch\": [\n \"BUCK\"\n ]\n}\n```\n\n## On `DiscoverWorkspaceConfig::command`\n\n**Warning**: This format is provisional and subject to change.\n\n[`DiscoverWorkspaceConfig::command`] *must* return a JSON object\ncorresponding to `DiscoverProjectData::Finished`:\n\n```norun\n#[derive(Debug, Clone, Deserialize, Serialize)]\n#[serde(tag = \"kind\")]\n#[serde(rename_all = \"snake_case\")]\nenum DiscoverProjectData {\n Finished { buildfile: Utf8PathBuf, project: ProjectJsonData },\n Error { error: String, source: Option<String> },\n Progress { message: String },\n}\n```\n\nAs JSON, `DiscoverProjectData::Finished` is:\n\n```json\n{\n // the internally-tagged representation of the enum.\n \"kind\": \"finished\",\n // the file used by a non-Cargo build system to define\n // a package or target.\n \"buildfile\": \"rust-analyzer/BUILD\",\n // the contents of a rust-project.json, elided for brevity\n \"project\": {\n \"sysroot\": \"foo\",\n \"crates\": []\n }\n}\n```\n\nIt is encouraged, but not required, to use the other variants on\n`DiscoverProjectData` to provide a more polished end-user experience.\n\n`DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`,\nwhich will be substituted with the JSON-serialized form of the following\nenum:\n\n```norun\n#[derive(PartialEq, Clone, Debug, Serialize)]\n#[serde(rename_all = \"camelCase\")]\npub enum DiscoverArgument {\n Path(AbsPathBuf),\n Buildfile(AbsPathBuf),\n}\n```\n\nThe JSON representation of `DiscoverArgument::Path` is:\n\n```json\n{\n \"path\": \"src/main.rs\"\n}\n```\n\nSimilarly, the JSON representation of `DiscoverArgument::Buildfile` is:\n\n```\n{\n \"buildfile\": \"BUILD\"\n}\n```\n\n`DiscoverArgument::Path` is used to find and generate a `rust-project.json`,\nand therefore, a workspace, whereas `DiscoverArgument::buildfile` is used to\nto update an existing workspace. As a reference for implementors,\nbuck2's `rust-project` will likely be useful:\nhttps://github.com/facebook/buck2/tree/main/integrations/rust-project.", + "markdownDescription": "Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`].\n\n[`DiscoverWorkspaceConfig`] also requires setting `progress_label` and `files_to_watch`.\n`progress_label` is used for the title in progress indicators, whereas `files_to_watch`\nis used to determine which build system-specific files should be watched in order to\nreload rust-analyzer.\n\nBelow is an example of a valid configuration:\n```json\n\"rust-analyzer.workspace.discoverConfig\": {\n \"command\": [\n \"rust-project\",\n \"develop-json\"\n ],\n \"progressLabel\": \"rust-analyzer\",\n \"filesToWatch\": [\n \"BUCK\"\n ]\n}\n```\n\n## On `DiscoverWorkspaceConfig::command`\n\n**Warning**: This format is provisional and subject to change.\n\n[`DiscoverWorkspaceConfig::command`] *must* return a JSON object\ncorresponding to `DiscoverProjectData::Finished`:\n\n```norun\n#[derive(Debug, Clone, Deserialize, Serialize)]\n#[serde(tag = \"kind\")]\n#[serde(rename_all = \"snake_case\")]\nenum DiscoverProjectData {\n Finished { buildfile: Utf8PathBuf, project: ProjectJsonData },\n Error { error: String, source: Option<String> },\n Progress { message: String },\n}\n```\n\nAs JSON, `DiscoverProjectData::Finished` is:\n\n```json\n{\n // the internally-tagged representation of the enum.\n \"kind\": \"finished\",\n // the file used by a non-Cargo build system to define\n // a package or target.\n \"buildfile\": \"rust-analyzer/BUILD\",\n // the contents of a rust-project.json, elided for brevity\n \"project\": {\n \"sysroot\": \"foo\",\n \"crates\": []\n }\n}\n```\n\nIt is encouraged, but not required, to use the other variants on\n`DiscoverProjectData` to provide a more polished end-user experience.\n\n`DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`,\nwhich will be substituted with the JSON-serialized form of the following\nenum:\n\n```norun\n#[derive(PartialEq, Clone, Debug, Serialize)]\n#[serde(rename_all = \"camelCase\")]\npub enum DiscoverArgument {\n Path(AbsPathBuf),\n Buildfile(AbsPathBuf),\n}\n```\n\nThe JSON representation of `DiscoverArgument::Path` is:\n\n```json\n{\n \"path\": \"src/main.rs\"\n}\n```\n\nSimilarly, the JSON representation of `DiscoverArgument::Buildfile` is:\n\n```json\n{\n \"buildfile\": \"BUILD\"\n}\n```\n\n`DiscoverArgument::Path` is used to find and generate a `rust-project.json`,\nand therefore, a workspace, whereas `DiscoverArgument::buildfile` is used to\nto update an existing workspace. As a reference for implementors,\nbuck2's `rust-project` will likely be useful:\nhttps://github.com/facebook/buck2/tree/main/integrations/rust-project.", "default": null, "anyOf": [ { @@ -3369,6 +3380,10 @@ "when": "inRustProject" }, { + "command": "rust-analyzer.childModules", + "when": "inRustProject" + }, + { "command": "rust-analyzer.joinLines", "when": "inRustProject" }, @@ -3488,6 +3503,7 @@ "views": { "explorer": [ { + "icon": "$(package)", "id": "rustDependencies", "name": "Rust Dependencies", "when": "inRustProject && config.rust-analyzer.showDependenciesExplorer" @@ -3495,6 +3511,7 @@ ], "rustSyntaxTreeContainer": [ { + "icon": "$(file-code)", "id": "rustSyntaxTree", "name": "Rust Syntax Tree", "when": "inRustProject && config.rust-analyzer.showSyntaxTree" @@ -3537,13 +3554,13 @@ { "id": "docs", "title": "Visit the docs!", - "description": "Confused about configurations? Want to learn more about rust-analyzer? Visit the [User Manual](https://rust-analyzer.github.io/manual.html)!", + "description": "Confused about configurations? Want to learn more about rust-analyzer? Visit the [User Manual](https://rust-analyzer.github.io/book/)!", "media": { "image": "./icon.png", "altText": "rust-analyzer logo" }, "completionEvents": [ - "onLink:https://rust-analyzer.github.io/manual.html" + "onLink:https://rust-analyzer.github.io/book/" ] }, { diff --git a/src/tools/rust-analyzer/editors/code/src/bootstrap.ts b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts index bccae73c9a7..bddf195803d 100644 --- a/src/tools/rust-analyzer/editors/code/src/bootstrap.ts +++ b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts @@ -15,7 +15,7 @@ export async function bootstrap( if (!path) { throw new Error( "rust-analyzer Language Server is not available. " + - "Please, ensure its [proper installation](https://rust-analyzer.github.io/manual.html#installation).", + "Please, ensure its [proper installation](https://rust-analyzer.github.io/book/installation.html).", ); } @@ -187,8 +187,16 @@ async function hasToolchainFileWithRaDeclared(uri: vscode.Uri): Promise<boolean> export async function isValidExecutable(path: string, extraEnv: Env): Promise<boolean> { log.debug("Checking availability of a binary at", path); + const newEnv = { ...process.env }; + for (const [k, v] of Object.entries(extraEnv)) { + if (v) { + newEnv[k] = v; + } else if (k in newEnv) { + delete newEnv[k]; + } + } const res = await spawnAsync(path, ["--version"], { - env: { ...process.env, ...extraEnv }, + env: newEnv, }); if (res.error) { diff --git a/src/tools/rust-analyzer/editors/code/src/commands.ts b/src/tools/rust-analyzer/editors/code/src/commands.ts index 4e614d32057..3ac1a933d9e 100644 --- a/src/tools/rust-analyzer/editors/code/src/commands.ts +++ b/src/tools/rust-analyzer/editors/code/src/commands.ts @@ -266,6 +266,43 @@ export function parentModule(ctx: CtxInit): Cmd { }; } +export function childModules(ctx: CtxInit): Cmd { + return async () => { + const editor = vscode.window.activeTextEditor; + if (!editor) return; + if (!(isRustDocument(editor.document) || isCargoTomlDocument(editor.document))) return; + + const client = ctx.client; + + const locations = await client.sendRequest(ra.childModules, { + textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document), + position: client.code2ProtocolConverter.asPosition(editor.selection.active), + }); + if (!locations) return; + + if (locations.length === 1) { + const loc = unwrapUndefinable(locations[0]); + + const uri = client.protocol2CodeConverter.asUri(loc.targetUri); + const range = client.protocol2CodeConverter.asRange(loc.targetRange); + + const doc = await vscode.workspace.openTextDocument(uri); + const e = await vscode.window.showTextDocument(doc); + e.selection = new vscode.Selection(range.start, range.start); + e.revealRange(range, vscode.TextEditorRevealType.InCenter); + } else { + const uri = editor.document.uri.toString(); + const position = client.code2ProtocolConverter.asPosition(editor.selection.active); + await showReferencesImpl( + client, + uri, + position, + locations.map((loc) => lc.Location.create(loc.targetUri, loc.targetRange)), + ); + } + }; +} + export function openCargoToml(ctx: CtxInit): Cmd { return async () => { const editor = ctx.activeRustEditor; diff --git a/src/tools/rust-analyzer/editors/code/src/config.ts b/src/tools/rust-analyzer/editors/code/src/config.ts index 896b3c10cbf..f36e18a73da 100644 --- a/src/tools/rust-analyzer/editors/code/src/config.ts +++ b/src/tools/rust-analyzer/editors/code/src/config.ts @@ -2,7 +2,7 @@ import * as Is from "vscode-languageclient/lib/common/utils/is"; import * as os from "os"; import * as path from "path"; import * as vscode from "vscode"; -import { expectNotUndefined, log, unwrapUndefinable } from "./util"; +import { expectNotUndefined, log, normalizeDriveLetter, unwrapUndefinable } from "./util"; import type { Env } from "./util"; import type { Disposable } from "vscode"; @@ -213,12 +213,13 @@ export class Config { get serverExtraEnv(): Env { const extraEnv = - this.get<{ [key: string]: string | number } | null>("server.extraEnv") ?? {}; + this.get<{ [key: string]: { toString(): string } | null } | null>("server.extraEnv") ?? + {}; return substituteVariablesInEnv( Object.fromEntries( Object.entries(extraEnv).map(([k, v]) => [ k, - typeof v !== "string" ? v.toString() : v, + typeof v === "string" ? v : v?.toString(), ]), ), ); @@ -323,7 +324,6 @@ export class Config { return { engine: this.get<string>("debug.engine"), engineSettings: this.get<object>("debug.engineSettings") ?? {}, - openDebugPane: this.get<boolean>("debug.openDebugPane"), buildBeforeRestart: this.get<boolean>("debug.buildBeforeRestart"), sourceFileMap: sourceFileMap, }; @@ -399,6 +399,7 @@ export function prepareVSCodeConfig<T>(resp: T): T { // FIXME: Merge this with `substituteVSCodeVariables` above export function substituteVariablesInEnv(env: Env): Env { + const depRe = new RegExp(/\${(?<depName>.+?)}/g); const missingDeps = new Set<string>(); // vscode uses `env:ENV_NAME` for env vars resolution, and it's easier // to follow the same convention for our dependency tracking @@ -406,15 +407,16 @@ export function substituteVariablesInEnv(env: Env): Env { const envWithDeps = Object.fromEntries( Object.entries(env).map(([key, value]) => { const deps = new Set<string>(); - const depRe = new RegExp(/\${(?<depName>.+?)}/g); - let match = undefined; - while ((match = depRe.exec(value))) { - const depName = unwrapUndefinable(match.groups?.["depName"]); - deps.add(depName); - // `depName` at this point can have a form of `expression` or - // `prefix:expression` - if (!definedEnvKeys.has(depName)) { - missingDeps.add(depName); + if (value) { + let match = undefined; + while ((match = depRe.exec(value))) { + const depName = unwrapUndefinable(match.groups?.["depName"]); + deps.add(depName); + // `depName` at this point can have a form of `expression` or + // `prefix:expression` + if (!definedEnvKeys.has(depName)) { + missingDeps.add(depName); + } } } return [`env:${key}`, { deps: [...deps], value }]; @@ -455,11 +457,10 @@ export function substituteVariablesInEnv(env: Env): Env { do { leftToResolveSize = toResolve.size; for (const key of toResolve) { - const item = unwrapUndefinable(envWithDeps[key]); - if (item.deps.every((dep) => resolved.has(dep))) { - item.value = item.value.replace(/\${(?<depName>.+?)}/g, (_wholeMatch, depName) => { - const item = unwrapUndefinable(envWithDeps[depName]); - return item.value; + const item = envWithDeps[key]; + if (item && item.deps.every((dep) => resolved.has(dep))) { + item.value = item.value?.replace(/\${(?<depName>.+?)}/g, (_wholeMatch, depName) => { + return envWithDeps[depName]?.value ?? ""; }); resolved.add(key); toResolve.delete(key); @@ -499,7 +500,7 @@ function computeVscodeVar(varName: string): string | null { // user has opened on Editor startup. Could lead to // unpredictable workspace selection in practice. // It's better to pick the first one - folder.uri.fsPath; + normalizeDriveLetter(folder.uri.fsPath); return fsPath; }; // https://code.visualstudio.com/docs/editor/variables-reference diff --git a/src/tools/rust-analyzer/editors/code/src/ctx.ts b/src/tools/rust-analyzer/editors/code/src/ctx.ts index 37a2ee23691..e55754fb9f0 100644 --- a/src/tools/rust-analyzer/editors/code/src/ctx.ts +++ b/src/tools/rust-analyzer/editors/code/src/ctx.ts @@ -190,11 +190,11 @@ export class Ctx implements RustAnalyzerExtensionApi { } if (!this.traceOutputChannel) { - this.traceOutputChannel = new LazyOutputChannel("Rust Analyzer Language Server Trace"); + this.traceOutputChannel = new LazyOutputChannel("rust-analyzer LSP Trace"); this.pushExtCleanup(this.traceOutputChannel); } if (!this.outputChannel) { - this.outputChannel = vscode.window.createOutputChannel("Rust Analyzer Language Server"); + this.outputChannel = vscode.window.createOutputChannel("rust-analyzer Language Server"); this.pushExtCleanup(this.outputChannel); } @@ -213,7 +213,14 @@ export class Ctx implements RustAnalyzerExtensionApi { this.refreshServerStatus(); }, ); - const newEnv = Object.assign({}, process.env, this.config.serverExtraEnv); + const newEnv = { ...process.env }; + for (const [k, v] of Object.entries(this.config.serverExtraEnv)) { + if (v) { + newEnv[k] = v; + } else if (k in newEnv) { + delete newEnv[k]; + } + } const run: lc.Executable = { command: this._serverPath, options: { env: newEnv }, diff --git a/src/tools/rust-analyzer/editors/code/src/debug.ts b/src/tools/rust-analyzer/editors/code/src/debug.ts index 72a9aabc043..adb75c23c70 100644 --- a/src/tools/rust-analyzer/editors/code/src/debug.ts +++ b/src/tools/rust-analyzer/editors/code/src/debug.ts @@ -6,11 +6,9 @@ import type * as ra from "./lsp_ext"; import { Cargo } from "./toolchain"; import type { Ctx } from "./ctx"; import { createTaskFromRunnable, prepareEnv } from "./run"; -import { execute, isCargoRunnableArgs, unwrapUndefinable } from "./util"; +import { execute, isCargoRunnableArgs, unwrapUndefinable, log, normalizeDriveLetter } from "./util"; import type { Config } from "./config"; -const debugOutput = vscode.window.createOutputChannel("Debug"); - // Here we want to keep track on everything that's currently running const activeDebugSessionIds: string[] = []; @@ -56,15 +54,14 @@ export async function startDebugSession(ctx: Ctx, runnable: ra.Runnable): Promis if (-1 !== index) { debugConfig = configurations[index]; message = " (from launch.json)"; - debugOutput.clear(); } else { debugConfig = await getDebugConfiguration(ctx.config, runnable); } if (!debugConfig) return false; - debugOutput.appendLine(`Launching debug configuration${message}:`); - debugOutput.appendLine(JSON.stringify(debugConfig, null, 2)); + log.debug(`Launching debug configuration${message}:`); + log.debug(JSON.stringify(debugConfig, null, 2)); return vscode.debug.startDebugging(undefined, debugConfig); } @@ -118,10 +115,6 @@ async function getDebugConfiguration( return; } - debugOutput.clear(); - if (config.debug.openDebugPane) { - debugOutput.show(true); - } // folder exists or RA is not active. const workspaceFolders = vscode.workspace.workspaceFolders!; @@ -134,20 +127,14 @@ async function getDebugConfiguration( firstWorkspace; const workspace = unwrapUndefinable(maybeWorkspace); - let wsFolder = path.normalize(workspace.uri.fsPath); - if (os.platform() === "win32") { - // in windows, the drive letter can vary in casing for VSCode, so we gotta normalize that first - wsFolder = wsFolder.replace(/^[a-z]:\\/, (c) => c.toUpperCase()); - } + const wsFolder = normalizeDriveLetter(path.normalize(workspace.uri.fsPath)); const workspaceQualifier = isMultiFolderWorkspace ? `:${workspace.name}` : ""; function simplifyPath(p: string): string { // in windows, the drive letter can vary in casing for VSCode, so we gotta normalize that first - if (os.platform() === "win32") { - p = p.replace(/^[a-z]:\\/, (c) => c.toUpperCase()); - } + p = normalizeDriveLetter(path.normalize(p)); // see https://github.com/rust-lang/rust-analyzer/pull/5513#issuecomment-663458818 for why this is needed - return path.normalize(p).replace(wsFolder, `\${workspaceFolder${workspaceQualifier}}`); + return p.replace(wsFolder, `\${workspaceFolder${workspaceQualifier}}`); } const executable = await getDebugExecutable( @@ -232,7 +219,7 @@ async function discoverSourceFileMap( const commitHash = rx.exec(data)?.[1]; if (commitHash) { const rustlib = path.normalize(sysroot + "/lib/rustlib/src/rust"); - return { source: rustlib, destination: rustlib }; + return { source: "/rustc/" + commitHash, destination: rustlib }; } } @@ -321,7 +308,7 @@ async function getDebugExecutable( runnableArgs: ra.CargoRunnableArgs, env: Record<string, string>, ): Promise<string> { - const cargo = new Cargo(runnableArgs.workspaceRoot || ".", debugOutput, env); + const cargo = new Cargo(runnableArgs.workspaceRoot || ".", env); const executable = await cargo.executableFromArgs(runnableArgs); // if we are here, there were no compilation errors. diff --git a/src/tools/rust-analyzer/editors/code/src/lsp_ext.ts b/src/tools/rust-analyzer/editors/code/src/lsp_ext.ts index af5129ac963..20952e93ccc 100644 --- a/src/tools/rust-analyzer/editors/code/src/lsp_ext.ts +++ b/src/tools/rust-analyzer/editors/code/src/lsp_ext.ts @@ -194,6 +194,11 @@ export const parentModule = new lc.RequestType< lc.LocationLink[] | null, void >("experimental/parentModule"); +export const childModules = new lc.RequestType< + lc.TextDocumentPositionParams, + lc.LocationLink[] | null, + void +>("experimental/childModules"); export const runnables = new lc.RequestType<RunnablesParams, Runnable[], void>( "experimental/runnables", ); diff --git a/src/tools/rust-analyzer/editors/code/src/main.ts b/src/tools/rust-analyzer/editors/code/src/main.ts index 451294e26f6..5e500730693 100644 --- a/src/tools/rust-analyzer/editors/code/src/main.ts +++ b/src/tools/rust-analyzer/editors/code/src/main.ts @@ -158,6 +158,7 @@ function createCommands(): Record<string, CommandFactory> { matchingBrace: { enabled: commands.matchingBrace }, joinLines: { enabled: commands.joinLines }, parentModule: { enabled: commands.parentModule }, + childModules: { enabled: commands.childModules }, viewHir: { enabled: commands.viewHir }, viewMir: { enabled: commands.viewMir }, interpretFunction: { enabled: commands.interpretFunction }, @@ -187,7 +188,9 @@ function createCommands(): Record<string, CommandFactory> { openWalkthrough: { enabled: commands.openWalkthrough }, // Internal commands which are invoked by the server. applyActionGroup: { enabled: commands.applyActionGroup }, - applySnippetWorkspaceEdit: { enabled: commands.applySnippetWorkspaceEditCommand }, + applySnippetWorkspaceEdit: { + enabled: commands.applySnippetWorkspaceEditCommand, + }, debugSingle: { enabled: commands.debugSingle }, gotoLocation: { enabled: commands.gotoLocation }, hoverRefCommandProxy: { enabled: commands.hoverRefCommandProxy }, @@ -200,8 +203,12 @@ function createCommands(): Record<string, CommandFactory> { revealDependency: { enabled: commands.revealDependency }, syntaxTreeReveal: { enabled: commands.syntaxTreeReveal }, syntaxTreeCopy: { enabled: commands.syntaxTreeCopy }, - syntaxTreeHideWhitespace: { enabled: commands.syntaxTreeHideWhitespace }, - syntaxTreeShowWhitespace: { enabled: commands.syntaxTreeShowWhitespace }, + syntaxTreeHideWhitespace: { + enabled: commands.syntaxTreeHideWhitespace, + }, + syntaxTreeShowWhitespace: { + enabled: commands.syntaxTreeShowWhitespace, + }, }; } diff --git a/src/tools/rust-analyzer/editors/code/src/toolchain.ts b/src/tools/rust-analyzer/editors/code/src/toolchain.ts index bb061442953..a859ce6ff00 100644 --- a/src/tools/rust-analyzer/editors/code/src/toolchain.ts +++ b/src/tools/rust-analyzer/editors/code/src/toolchain.ts @@ -37,7 +37,6 @@ interface CompilerMessage { export class Cargo { constructor( readonly rootFolder: string, - readonly output: vscode.OutputChannel, readonly env: Record<string, string>, ) {} @@ -93,14 +92,14 @@ export class Cargo { }); } } else if (message.reason === "compiler-message") { - this.output.append(message.message.rendered); + log.info(message.message.rendered); } }, - (stderr) => this.output.append(stderr), + (stderr) => log.error(stderr), env, ); } catch (err) { - this.output.show(true); + log.error(`Cargo invocation has failed: ${err}`); throw new Error(`Cargo invocation has failed: ${err}`); } diff --git a/src/tools/rust-analyzer/editors/code/src/util.ts b/src/tools/rust-analyzer/editors/code/src/util.ts index 93c7bf8d73e..410b055100a 100644 --- a/src/tools/rust-analyzer/editors/code/src/util.ts +++ b/src/tools/rust-analyzer/editors/code/src/util.ts @@ -14,11 +14,11 @@ export function assert(condition: boolean, explanation: string): asserts conditi } export type Env = { - [name: string]: string; + [name: string]: string | undefined; }; class Log { - private readonly output = vscode.window.createOutputChannel("Rust Analyzer Client", { + private readonly output = vscode.window.createOutputChannel("rust-analyzer Extension", { log: true, }); @@ -299,3 +299,32 @@ export async function spawnAsync( }; } } + +export const isWindows = process.platform === "win32"; + +export function isWindowsDriveLetter(code: number): boolean { + // Copied from https://github.com/microsoft/vscode/blob/02c2dba5f2669b924fd290dff7d2ff3460791996/src/vs/base/common/extpath.ts#L265-L267 + return ( + (code >= /* CharCode.A */ 65 && code <= /* CharCode.Z */ 90) || + (code >= /* CharCode.a */ 97 && code <= /* CharCode.z */ 122) + ); +} +export function hasDriveLetter(path: string, isWindowsOS: boolean = isWindows): boolean { + // Copied from https://github.com/microsoft/vscode/blob/02c2dba5f2669b924fd290dff7d2ff3460791996/src/vs/base/common/extpath.ts#L324-L330 + if (isWindowsOS) { + return ( + isWindowsDriveLetter(path.charCodeAt(0)) && + path.charCodeAt(1) === /* CharCode.Colon */ 58 + ); + } + + return false; +} +export function normalizeDriveLetter(path: string, isWindowsOS: boolean = isWindows): string { + // Copied from https://github.com/microsoft/vscode/blob/02c2dba5f2669b924fd290dff7d2ff3460791996/src/vs/base/common/labels.ts#L140-L146 + if (hasDriveLetter(path, isWindowsOS)) { + return path.charAt(0).toUpperCase() + path.slice(1); + } + + return path; +} |
