about summary refs log tree commit diff
path: root/src/tools/rust-analyzer/editors/code
diff options
context:
space:
mode:
authorLaurențiu Nicola <lnicola@dend.ro>2023-06-05 12:04:23 +0300
committerLaurențiu Nicola <lnicola@dend.ro>2023-06-05 12:04:23 +0300
commitb8a7d439db0cfd765ed4bfedd2bbaeeee58b05a5 (patch)
tree5adcbc6cf50af3bebc2cd4f42d5252a4d728690e /src/tools/rust-analyzer/editors/code
parent51f714c8c5021fe25442e46798b1cbef2f2249ed (diff)
parentaa9bc8612514d216f84eec218dfd19ab83f3598a (diff)
downloadrust-b8a7d439db0cfd765ed4bfedd2bbaeeee58b05a5.tar.gz
rust-b8a7d439db0cfd765ed4bfedd2bbaeeee58b05a5.zip
Merge commit 'aa9bc8612514d216f84eec218dfd19ab83f3598a' into sync-from-ra
Diffstat (limited to 'src/tools/rust-analyzer/editors/code')
-rw-r--r--src/tools/rust-analyzer/editors/code/package.json208
-rw-r--r--src/tools/rust-analyzer/editors/code/rustdoc.markdown.injection.tmGrammar.json36
-rw-r--r--src/tools/rust-analyzer/editors/code/src/client.ts98
-rw-r--r--src/tools/rust-analyzer/editors/code/src/commands.ts149
-rw-r--r--src/tools/rust-analyzer/editors/code/src/config.ts9
-rw-r--r--src/tools/rust-analyzer/editors/code/src/ctx.ts103
-rw-r--r--src/tools/rust-analyzer/editors/code/src/debug.ts9
-rw-r--r--src/tools/rust-analyzer/editors/code/src/dependencies_provider.ts148
-rw-r--r--src/tools/rust-analyzer/editors/code/src/lsp_ext.ts45
-rw-r--r--src/tools/rust-analyzer/editors/code/src/main.ts7
-rw-r--r--src/tools/rust-analyzer/editors/code/src/run.ts2
-rw-r--r--src/tools/rust-analyzer/editors/code/src/tasks.ts2
-rw-r--r--src/tools/rust-analyzer/editors/code/src/toolchain.ts7
-rw-r--r--src/tools/rust-analyzer/editors/code/src/util.ts13
14 files changed, 768 insertions, 68 deletions
diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json
index c5eb08748bf..ee1f832d323 100644
--- a/src/tools/rust-analyzer/editors/code/package.json
+++ b/src/tools/rust-analyzer/editors/code/package.json
@@ -44,7 +44,8 @@
         "anser": "^2.1.1",
         "d3": "^7.6.1",
         "d3-graphviz": "^5.0.2",
-        "vscode-languageclient": "^8.0.2"
+        "vscode-languageclient": "^8.0.2",
+        "@hpcc-js/wasm": "2.5.0"
     },
     "devDependencies": {
         "@types/node": "~16.11.7",
@@ -120,6 +121,11 @@
                 "category": "rust-analyzer (debug command)"
             },
             {
+                "command": "rust-analyzer.interpretFunction",
+                "title": "Interpret Function",
+                "category": "rust-analyzer (debug command)"
+            },
+            {
                 "command": "rust-analyzer.viewFileText",
                 "title": "View File Text (as seen by the server)",
                 "category": "rust-analyzer (debug command)"
@@ -200,12 +206,17 @@
                 "category": "rust-analyzer"
             },
             {
+                "command": "rust-analyzer.rebuildProcMacros",
+                "title": "Rebuild proc macros and build scripts",
+                "category": "rust-analyzer"
+            },
+            {
                 "command": "rust-analyzer.addProject",
                 "title": "Add current file's crate to workspace",
                 "category": "rust-analyzer"
             },
             {
-                "command": "rust-analyzer.reload",
+                "command": "rust-analyzer.restartServer",
                 "title": "Restart server",
                 "category": "rust-analyzer"
             },
@@ -273,6 +284,11 @@
                 "command": "rust-analyzer.clearFlycheck",
                 "title": "Clear flycheck diagnostics",
                 "category": "rust-analyzer"
+            },
+            {
+                "command": "rust-analyzer.revealDependency",
+                "title": "Reveal File",
+                "category": "rust-analyzer"
             }
         ],
         "keybindings": [
@@ -444,6 +460,16 @@
                         "type": "string"
                     }
                 },
+                "rust-analyzer.showUnlinkedFileNotification": {
+                    "markdownDescription": "Whether to show a notification for unlinked files asking the user to add the corresponding Cargo.toml to the linked projects setting.",
+                    "default": true,
+                    "type": "boolean"
+                },
+                "rust-analyzer.showDependenciesExplorer": {
+                    "markdownDescription": "Whether to show the dependencies view.",
+                    "default": true,
+                    "type": "boolean"
+                },
                 "$generated-start": {},
                 "rust-analyzer.assist.emitMustUse": {
                     "markdownDescription": "Whether to insert #[must_use] when generating `as_` methods\nfor enum variants.",
@@ -527,6 +553,11 @@
                     "default": true,
                     "type": "boolean"
                 },
+                "rust-analyzer.cargo.cfgs": {
+                    "markdownDescription": "List of cfg options to enable with the given values.",
+                    "default": {},
+                    "type": "object"
+                },
                 "rust-analyzer.cargo.extraArgs": {
                     "markdownDescription": "Extra arguments that are passed to every cargo invocation.",
                     "default": [],
@@ -591,7 +622,7 @@
                     ]
                 },
                 "rust-analyzer.cargo.unsetTest": {
-                    "markdownDescription": "Unsets `#[cfg(test)]` for the specified crates.",
+                    "markdownDescription": "Unsets the implicit `#[cfg(test)]` for the specified crates.",
                     "default": [
                         "core"
                     ],
@@ -870,6 +901,11 @@
                     "default": true,
                     "type": "boolean"
                 },
+                "rust-analyzer.highlightRelated.closureCaptures.enable": {
+                    "markdownDescription": "Enables highlighting of all captures of a closure while the cursor is on the `|` or move keyword of a closure.",
+                    "default": true,
+                    "type": "boolean"
+                },
                 "rust-analyzer.highlightRelated.exitPoints.enable": {
                     "markdownDescription": "Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`).",
                     "default": true,
@@ -926,10 +962,89 @@
                     "type": "boolean"
                 },
                 "rust-analyzer.hover.links.enable": {
-                    "markdownDescription": "Use markdown syntax for links in hover.",
+                    "markdownDescription": "Use markdown syntax for links on hover.",
+                    "default": true,
+                    "type": "boolean"
+                },
+                "rust-analyzer.hover.memoryLayout.alignment": {
+                    "markdownDescription": "How to render the align information in a memory layout hover.",
+                    "default": "hexadecimal",
+                    "anyOf": [
+                        {
+                            "type": "null"
+                        },
+                        {
+                            "type": "string",
+                            "enum": [
+                                "both",
+                                "decimal",
+                                "hexadecimal"
+                            ],
+                            "enumDescriptions": [
+                                "Render as 12 (0xC)",
+                                "Render as 12",
+                                "Render as 0xC"
+                            ]
+                        }
+                    ]
+                },
+                "rust-analyzer.hover.memoryLayout.enable": {
+                    "markdownDescription": "Whether to show memory layout data on hover.",
                     "default": true,
                     "type": "boolean"
                 },
+                "rust-analyzer.hover.memoryLayout.niches": {
+                    "markdownDescription": "How to render the niche information in a memory layout hover.",
+                    "default": false,
+                    "type": [
+                        "null",
+                        "boolean"
+                    ]
+                },
+                "rust-analyzer.hover.memoryLayout.offset": {
+                    "markdownDescription": "How to render the offset information in a memory layout hover.",
+                    "default": "hexadecimal",
+                    "anyOf": [
+                        {
+                            "type": "null"
+                        },
+                        {
+                            "type": "string",
+                            "enum": [
+                                "both",
+                                "decimal",
+                                "hexadecimal"
+                            ],
+                            "enumDescriptions": [
+                                "Render as 12 (0xC)",
+                                "Render as 12",
+                                "Render as 0xC"
+                            ]
+                        }
+                    ]
+                },
+                "rust-analyzer.hover.memoryLayout.size": {
+                    "markdownDescription": "How to render the size information in a memory layout hover.",
+                    "default": "both",
+                    "anyOf": [
+                        {
+                            "type": "null"
+                        },
+                        {
+                            "type": "string",
+                            "enum": [
+                                "both",
+                                "decimal",
+                                "hexadecimal"
+                            ],
+                            "enumDescriptions": [
+                                "Render as 12 (0xC)",
+                                "Render as 12",
+                                "Render as 0xC"
+                            ]
+                        }
+                    ]
+                },
                 "rust-analyzer.imports.granularity.enforce": {
                     "markdownDescription": "Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file.",
                     "default": false,
@@ -1003,6 +1118,11 @@
                     "type": "integer",
                     "minimum": 0
                 },
+                "rust-analyzer.inlayHints.closureCaptureHints.enable": {
+                    "markdownDescription": "Whether to show inlay hints for closure captures.",
+                    "default": false,
+                    "type": "boolean"
+                },
                 "rust-analyzer.inlayHints.closureReturnTypeHints.enable": {
                     "markdownDescription": "Whether to show inlay type hints for return types of closures.",
                     "default": "never",
@@ -1018,6 +1138,23 @@
                         "Only show type hints for return types of closures with blocks."
                     ]
                 },
+                "rust-analyzer.inlayHints.closureStyle": {
+                    "markdownDescription": "Closure notation in type and chaining inlay hints.",
+                    "default": "impl_fn",
+                    "type": "string",
+                    "enum": [
+                        "impl_fn",
+                        "rust_analyzer",
+                        "with_id",
+                        "hide"
+                    ],
+                    "enumDescriptions": [
+                        "`impl_fn`: `impl FnMut(i32, u64) -> i8`",
+                        "`rust_analyzer`: `|i32, u64| -> i8`",
+                        "`with_id`: `{closure#14352}`, where that id is the unique number of the closure in r-a internals",
+                        "`hide`: Shows `...` for every closure type"
+                    ]
+                },
                 "rust-analyzer.inlayHints.discriminantHints.enable": {
                     "markdownDescription": "Whether to show enum variant discriminant hints.",
                     "default": "never",
@@ -1066,8 +1203,8 @@
                     "enumDescriptions": [
                         "Always show adjustment hints as prefix (`*expr`).",
                         "Always show adjustment hints as postfix (`expr.*`).",
-                        "Show prefix or postfix depending on which uses less parenthesis, prefering prefix.",
-                        "Show prefix or postfix depending on which uses less parenthesis, prefering postfix."
+                        "Show prefix or postfix depending on which uses less parenthesis, preferring prefix.",
+                        "Show prefix or postfix depending on which uses less parenthesis, preferring postfix."
                     ]
                 },
                 "rust-analyzer.inlayHints.lifetimeElisionHints.enable": {
@@ -1242,6 +1379,11 @@
                     ],
                     "minimum": 0
                 },
+                "rust-analyzer.lru.query.capacities": {
+                    "markdownDescription": "Sets the LRU capacity of the specified queries.",
+                    "default": {},
+                    "type": "object"
+                },
                 "rust-analyzer.notifications.cargoTomlNotFound": {
                     "markdownDescription": "Whether to show `can't find Cargo.toml` error message.",
                     "default": true,
@@ -1272,7 +1414,7 @@
                     "type": "object"
                 },
                 "rust-analyzer.procMacro.server": {
-                    "markdownDescription": "Internal config, path to proc-macro server executable (typically,\nthis is rust-analyzer itself, but we override this in tests).",
+                    "markdownDescription": "Internal config, path to proc-macro server executable.",
                     "default": null,
                     "type": [
                         "null",
@@ -1337,6 +1479,11 @@
                     "default": true,
                     "type": "boolean"
                 },
+                "rust-analyzer.semanticHighlighting.nonStandardTokens": {
+                    "markdownDescription": "Whether the server is allowed to emit non-standard tokens and modifiers.",
+                    "default": true,
+                    "type": "boolean"
+                },
                 "rust-analyzer.semanticHighlighting.operator.enable": {
                     "markdownDescription": "Use semantic tokens for operators.\n\nWhen disabled, rust-analyzer will emit semantic tokens only for operator tokens when\nthey are tagged with modifiers.",
                     "default": true,
@@ -1348,7 +1495,7 @@
                     "type": "boolean"
                 },
                 "rust-analyzer.semanticHighlighting.punctuation.enable": {
-                    "markdownDescription": "Use semantic tokens for punctuations.\n\nWhen disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when\nthey are tagged with modifiers or have a special role.",
+                    "markdownDescription": "Use semantic tokens for punctuation.\n\nWhen disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when\nthey are tagged with modifiers or have a special role.",
                     "default": false,
                     "type": "boolean"
                 },
@@ -1358,7 +1505,7 @@
                     "type": "boolean"
                 },
                 "rust-analyzer.semanticHighlighting.punctuation.specialization.enable": {
-                    "markdownDescription": "Use specialized semantic tokens for punctuations.\n\nWhen enabled, rust-analyzer will emit special token types for punctuation tokens instead\nof the generic `punctuation` token type.",
+                    "markdownDescription": "Use specialized semantic tokens for punctuation.\n\nWhen enabled, rust-analyzer will emit special token types for punctuation tokens instead\nof the generic `punctuation` token type.",
                     "default": false,
                     "type": "boolean"
                 },
@@ -1461,6 +1608,18 @@
                         "endColumn": 6
                     }
                 ]
+            },
+            {
+                "name": "rust-panic",
+                "patterns": [
+                    {
+                        "regexp": "^thread '.*' panicked at '(.*)', (.*):(\\d*):(\\d*)$",
+                        "message": 1,
+                        "file": 2,
+                        "line": 3,
+                        "column": 4
+                    }
+                ]
             }
         ],
         "languages": [
@@ -1487,6 +1646,16 @@
                 "language": "ra_syntax_tree",
                 "scopeName": "source.ra_syntax_tree",
                 "path": "ra_syntax_tree.tmGrammar.json"
+            },
+            {
+                "scopeName": "rustdoc.markdown.injection",
+                "path": "rustdoc.markdown.injection.tmGrammar.json",
+                "injectTo": [
+                    "source.rust"
+                ],
+                "embeddedLanguages": {
+                    "meta.embedded.block.markdown": "text.html.markdown"
+                }
             }
         ],
         "problemMatchers": [
@@ -1511,6 +1680,16 @@
                 "pattern": "$rustc-json"
             },
             {
+                "name": "rust-panic",
+                "owner": "rust-panic",
+                "source": "panic",
+                "fileLocation": [
+                    "autoDetect",
+                    "${workspaceRoot}"
+                ],
+                "pattern": "$rust-panic"
+            },
+            {
                 "name": "rustc-watch",
                 "owner": "rustc",
                 "source": "rustc",
@@ -1876,7 +2055,7 @@
                     "when": "inRustProject"
                 },
                 {
-                    "command": "rust-analyzer.reload",
+                    "command": "rust-analyzer.restartServer",
                     "when": "inRustProject"
                 },
                 {
@@ -1913,6 +2092,15 @@
                 }
             ]
         },
+        "views": {
+            "explorer": [
+                {
+                    "id": "rustDependencies",
+                    "name": "Rust Dependencies",
+                    "when": "inRustProject && config.rust-analyzer.showDependenciesExplorer"
+                }
+            ]
+        },
         "jsonValidation": [
             {
                 "fileMatch": "rust-project.json",
diff --git a/src/tools/rust-analyzer/editors/code/rustdoc.markdown.injection.tmGrammar.json b/src/tools/rust-analyzer/editors/code/rustdoc.markdown.injection.tmGrammar.json
new file mode 100644
index 00000000000..3ee49bff31e
--- /dev/null
+++ b/src/tools/rust-analyzer/editors/code/rustdoc.markdown.injection.tmGrammar.json
@@ -0,0 +1,36 @@
+{
+    "scopeName": "rustdoc.markdown.injection",
+    "injectionSelector": "L:source.rust",
+    "patterns": [
+        {
+            "include": "#doc-comment-line"
+        },
+        {
+            "include": "#doc-comment-block"
+        }
+    ],
+    "repository": {
+        "doc-comment-line": {
+            "name": "comment.line.documentation.rust",
+            "begin": "^\\s*//(/|!)",
+            "while": "^\\s*//(/|!)",
+            "contentName": "meta.embedded.block.markdown",
+            "patterns": [
+                {
+                    "include": "text.html.markdown"
+                }
+            ]
+        },
+        "doc-comment-block": {
+            "name": "comment.block.documentation.rust",
+            "begin": "/\\*(\\*|!)",
+            "end": "\\s*\\*/",
+            "contentName": "meta.embedded.block.markdown",
+            "patterns": [
+                {
+                    "include": "text.html.markdown"
+                }
+            ]
+        }
+    }
+}
diff --git a/src/tools/rust-analyzer/editors/code/src/client.ts b/src/tools/rust-analyzer/editors/code/src/client.ts
index 565cb9c6432..f721fcce766 100644
--- a/src/tools/rust-analyzer/editors/code/src/client.ts
+++ b/src/tools/rust-analyzer/editors/code/src/client.ts
@@ -8,6 +8,7 @@ import * as diagnostics from "./diagnostics";
 import { WorkspaceEdit } from "vscode";
 import { Config, prepareVSCodeConfig } from "./config";
 import { randomUUID } from "crypto";
+import { sep as pathSeparator } from "path";
 
 export interface Env {
     [name: string]: string;
@@ -69,7 +70,8 @@ export async function createClient(
     outputChannel: vscode.OutputChannel,
     initializationOptions: vscode.WorkspaceConfiguration,
     serverOptions: lc.ServerOptions,
-    config: Config
+    config: Config,
+    unlinkedFiles: vscode.Uri[]
 ): Promise<lc.LanguageClient> {
     const clientOptions: lc.LanguageClientOptions = {
         documentSelector: [{ scheme: "file", language: "rust" }],
@@ -119,6 +121,69 @@ export async function createClient(
                 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 (
+                        value === "unlinked-file" &&
+                        !unlinkedFiles.includes(uri) &&
+                        diag.message !== "file not included in module tree"
+                    ) {
+                        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:)
@@ -138,14 +203,6 @@ export async function createClient(
                                 .substring(0, index)
                                 .replace(/^ -->[^\n]+\n/m, "");
                         }
-                        let value;
-                        if (errorCode) {
-                            if (typeof diag.code === "string" || typeof diag.code === "number") {
-                                value = diag.code;
-                            } else {
-                                value = diag.code?.value;
-                            }
-                        }
                         diag.code = {
                             target: vscode.Uri.from({
                                 scheme: diagnostics.URI_SCHEME,
@@ -153,7 +210,8 @@ export async function createClient(
                                 fragment: uri.toString(),
                                 query: idx.toString(),
                             }),
-                            value: value ?? "Click for full compiler diagnostic",
+                            value:
+                                errorCode && value ? value : "Click for full compiler diagnostic",
                         };
                     }
                 });
@@ -308,6 +366,7 @@ export async function createClient(
 
     // To turn on all proposed features use: client.registerProposedFeatures();
     client.registerFeature(new ExperimentalFeatures());
+    client.registerFeature(new OverrideFeatures());
 
     return client;
 }
@@ -343,6 +402,25 @@ class ExperimentalFeatures implements lc.StaticFeature {
     dispose(): void {}
 }
 
+class OverrideFeatures implements lc.StaticFeature {
+    getState(): lc.FeatureState {
+        return { kind: "static" };
+    }
+    fillClientCapabilities(capabilities: lc.ClientCapabilities): void {
+        // Force disable `augmentsSyntaxTokens`, VSCode's textmate grammar is somewhat incomplete
+        // making the experience generally worse
+        const caps = capabilities.textDocument?.semanticTokens;
+        if (caps) {
+            caps.augmentsSyntaxTokens = false;
+        }
+    }
+    initialize(
+        _capabilities: lc.ServerCapabilities,
+        _documentSelector: lc.DocumentSelector | undefined
+    ): void {}
+    dispose(): void {}
+}
+
 function isCodeActionWithoutEditsAndCommands(value: any): boolean {
     const candidate: lc.CodeAction = value;
     return (
diff --git a/src/tools/rust-analyzer/editors/code/src/commands.ts b/src/tools/rust-analyzer/editors/code/src/commands.ts
index 8a953577e99..98ccd50dc04 100644
--- a/src/tools/rust-analyzer/editors/code/src/commands.ts
+++ b/src/tools/rust-analyzer/editors/code/src/commands.ts
@@ -8,10 +8,18 @@ import { applySnippetWorkspaceEdit, applySnippetTextEdits } from "./snippets";
 import { spawnSync } from "child_process";
 import { RunnableQuickPick, selectRunnable, createTask, createArgs } from "./run";
 import { AstInspector } from "./ast_inspector";
-import { isRustDocument, isCargoTomlDocument, sleep, isRustEditor } from "./util";
+import {
+    isRustDocument,
+    isCargoTomlDocument,
+    sleep,
+    isRustEditor,
+    RustEditor,
+    RustDocument,
+} from "./util";
 import { startDebugSession, makeDebugConfig } from "./debug";
 import { LanguageClient } from "vscode-languageclient/node";
 import { LINKED_COMMANDS } from "./client";
+import { DependencyId } from "./dependencies_provider";
 
 export * from "./ast_inspector";
 export * from "./run";
@@ -89,7 +97,13 @@ export function shuffleCrateGraph(ctx: CtxInit): Cmd {
 
 export function triggerParameterHints(_: CtxInit): Cmd {
     return async () => {
-        await vscode.commands.executeCommand("editor.action.triggerParameterHints");
+        const parameterHintsEnabled = vscode.workspace
+            .getConfiguration("editor")
+            .get<boolean>("parameterHints.enabled");
+
+        if (parameterHintsEnabled) {
+            await vscode.commands.executeCommand("editor.action.triggerParameterHints");
+        }
     };
 }
 
@@ -260,6 +274,71 @@ export function openCargoToml(ctx: CtxInit): Cmd {
     };
 }
 
+export function revealDependency(ctx: CtxInit): Cmd {
+    return async (editor: RustEditor) => {
+        if (!ctx.dependencies?.isInitialized()) {
+            return;
+        }
+        const documentPath = editor.document.uri.fsPath;
+        const dep = ctx.dependencies?.getDependency(documentPath);
+        if (dep) {
+            await ctx.treeView?.reveal(dep, { select: true, expand: true });
+        } else {
+            await revealParentChain(editor.document, ctx);
+        }
+    };
+}
+
+/**
+ * This function calculates the parent chain of a given file until it reaches it crate root contained in ctx.dependencies.
+ * This is need because the TreeView is Lazy, so at first it only has the root dependencies: For example if we have the following crates:
+ * - core
+ * - alloc
+ * - std
+ *
+ * if I want to reveal alloc/src/str.rs, I have to:
+
+ * 1. reveal every children of alloc
+ * - core
+ * - alloc\
+ * &emsp;|-beches\
+ * &emsp;|-src\
+ * &emsp;|- ...
+ * - std
+ * 2. reveal every children of src:
+ * core
+ * alloc\
+ * &emsp;|-beches\
+ * &emsp;|-src\
+ * &emsp;&emsp;|- lib.rs\
+ * &emsp;&emsp;|- str.rs <------- FOUND IT!\
+ * &emsp;&emsp;|- ...\
+ * &emsp;|- ...\
+ * std
+ */
+async function revealParentChain(document: RustDocument, ctx: CtxInit) {
+    let documentPath = document.uri.fsPath;
+    const maxDepth = documentPath.split(path.sep).length - 1;
+    const parentChain: DependencyId[] = [{ id: documentPath.toLowerCase() }];
+    do {
+        documentPath = path.dirname(documentPath);
+        parentChain.push({ id: documentPath.toLowerCase() });
+        if (parentChain.length >= maxDepth) {
+            // this is an odd case that can happen when we change a crate version but we'd still have
+            // a open file referencing the old version
+            return;
+        }
+    } while (!ctx.dependencies?.contains(documentPath));
+    parentChain.reverse();
+    for (const idx in parentChain) {
+        await ctx.treeView?.reveal(parentChain[idx], { select: true, expand: true });
+    }
+}
+
+export async function execRevealDependency(e: RustEditor): Promise<void> {
+    await vscode.commands.executeCommand("rust-analyzer.revealDependency", e);
+}
+
 export function ssr(ctx: CtxInit): Cmd {
     return async () => {
         const editor = vscode.window.activeTextEditor;
@@ -416,8 +495,20 @@ export function syntaxTree(ctx: CtxInit): Cmd {
 function viewHirOrMir(ctx: CtxInit, xir: "hir" | "mir"): Cmd {
     const viewXir = xir === "hir" ? "viewHir" : "viewMir";
     const requestType = xir === "hir" ? ra.viewHir : ra.viewMir;
+    const uri = `rust-analyzer-${xir}://${viewXir}/${xir}.rs`;
+    const scheme = `rust-analyzer-${xir}`;
+    return viewFileUsingTextDocumentContentProvider(ctx, requestType, uri, scheme, true);
+}
+
+function viewFileUsingTextDocumentContentProvider(
+    ctx: CtxInit,
+    requestType: lc.RequestType<lc.TextDocumentPositionParams, string, void>,
+    uri: string,
+    scheme: string,
+    shouldUpdate: boolean
+): Cmd {
     const tdcp = new (class implements vscode.TextDocumentContentProvider {
-        readonly uri = vscode.Uri.parse(`rust-analyzer-${xir}://${viewXir}/${xir}.rs`);
+        readonly uri = vscode.Uri.parse(uri);
         readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
         constructor() {
             vscode.workspace.onDidChangeTextDocument(
@@ -433,14 +524,14 @@ function viewHirOrMir(ctx: CtxInit, xir: "hir" | "mir"): Cmd {
         }
 
         private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
-            if (isRustDocument(event.document)) {
+            if (isRustDocument(event.document) && shouldUpdate) {
                 // We need to order this after language server updates, but there's no API for that.
                 // Hence, good old sleep().
                 void sleep(10).then(() => this.eventEmitter.fire(this.uri));
             }
         }
         private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) {
-            if (editor && isRustEditor(editor)) {
+            if (editor && isRustEditor(editor) && shouldUpdate) {
                 this.eventEmitter.fire(this.uri);
             }
         }
@@ -467,9 +558,7 @@ function viewHirOrMir(ctx: CtxInit, xir: "hir" | "mir"): Cmd {
         }
     })();
 
-    ctx.pushExtCleanup(
-        vscode.workspace.registerTextDocumentContentProvider(`rust-analyzer-${xir}`, tdcp)
-    );
+    ctx.pushExtCleanup(vscode.workspace.registerTextDocumentContentProvider(scheme, tdcp));
 
     return async () => {
         const document = await vscode.workspace.openTextDocument(tdcp.uri);
@@ -495,6 +584,20 @@ export function viewMir(ctx: CtxInit): Cmd {
     return viewHirOrMir(ctx, "mir");
 }
 
+// Opens the virtual file that will show the MIR of the function containing the cursor position
+//
+// The contents of the file come from the `TextDocumentContentProvider`
+export function interpretFunction(ctx: CtxInit): Cmd {
+    const uri = `rust-analyzer-interpret-function://interpretFunction/result.log`;
+    return viewFileUsingTextDocumentContentProvider(
+        ctx,
+        ra.interpretFunction,
+        uri,
+        `rust-analyzer-interpret-function`,
+        false
+    );
+}
+
 export function viewFileText(ctx: CtxInit): Cmd {
     const tdcp = new (class implements vscode.TextDocumentContentProvider {
         readonly uri = vscode.Uri.parse("rust-analyzer-file-text://viewFileText/file.rs");
@@ -663,21 +766,26 @@ function crateGraph(ctx: CtxInit, full: boolean): Cmd {
             </head>
             <body>
                 <script type="text/javascript" src="${uri}/d3/dist/d3.min.js"></script>
-                <script type="text/javascript" src="${uri}/@hpcc-js/wasm/dist/index.min.js"></script>
+                <script type="text/javascript" src="${uri}/@hpcc-js/wasm/dist/graphviz.umd.js"></script>
                 <script type="text/javascript" src="${uri}/d3-graphviz/build/d3-graphviz.min.js"></script>
                 <div id="graph"></div>
                 <script>
+                    let dot = \`${dot}\`;
                     let graph = d3.select("#graph")
-                                  .graphviz()
+                                  .graphviz({ useWorker: false, useSharedWorker: false })
                                   .fit(true)
                                   .zoomScaleExtent([0.1, Infinity])
-                                  .renderDot(\`${dot}\`);
+                                  .renderDot(dot);
 
                     d3.select(window).on("click", (event) => {
                         if (event.ctrlKey) {
                             graph.resetZoom(d3.transition().duration(100));
                         }
                     });
+                    d3.select(window).on("copy", (event) => {
+                        event.clipboardData.setData("text/plain", dot);
+                        event.preventDefault();
+                    });
                 </script>
             </body>
             `;
@@ -699,7 +807,7 @@ export function viewFullCrateGraph(ctx: CtxInit): Cmd {
 // The contents of the file come from the `TextDocumentContentProvider`
 export function expandMacro(ctx: CtxInit): Cmd {
     function codeFormat(expanded: ra.ExpandedMacro): string {
-        let result = `// Recursive expansion of ${expanded.name}! macro\n`;
+        let result = `// Recursive expansion of ${expanded.name} macro\n`;
         result += "// " + "=".repeat(result.length - 3);
         result += "\n\n";
         result += expanded.expansion;
@@ -749,6 +857,10 @@ export function reloadWorkspace(ctx: CtxInit): Cmd {
     return async () => ctx.client.sendRequest(ra.reloadWorkspace);
 }
 
+export function rebuildProcMacros(ctx: CtxInit): Cmd {
+    return async () => ctx.client.sendRequest(ra.rebuildProcMacros);
+}
+
 export function addProject(ctx: CtxInit): Cmd {
     return async () => {
         const discoverProjectCommand = ctx.config.discoverProjectCommand;
@@ -757,12 +869,13 @@ export function addProject(ctx: CtxInit): Cmd {
         }
 
         const workspaces: JsonProject[] = await Promise.all(
-            vscode.workspace.workspaceFolders!.map(async (folder): Promise<JsonProject> => {
-                const rustDocuments = vscode.workspace.textDocuments.filter(isRustDocument);
-                return discoverWorkspace(rustDocuments, discoverProjectCommand, {
-                    cwd: folder.uri.fsPath,
-                });
-            })
+            vscode.workspace.textDocuments
+                .filter(isRustDocument)
+                .map(async (file): Promise<JsonProject> => {
+                    return discoverWorkspace([file], discoverProjectCommand, {
+                        cwd: path.dirname(file.uri.fsPath),
+                    });
+                })
         );
 
         ctx.addToDiscoveredWorkspaces(workspaces);
diff --git a/src/tools/rust-analyzer/editors/code/src/config.ts b/src/tools/rust-analyzer/editors/code/src/config.ts
index da7c74c28ba..c6d2bcc2b2a 100644
--- a/src/tools/rust-analyzer/editors/code/src/config.ts
+++ b/src/tools/rust-analyzer/editors/code/src/config.ts
@@ -21,7 +21,6 @@ export class Config {
         "serverPath",
         "server",
         "files",
-        "lens", // works as lens.*
     ].map((opt) => `${this.rootSection}.${opt}`);
 
     readonly package: {
@@ -70,7 +69,7 @@ export class Config {
         if (!requiresReloadOpt) return;
 
         if (this.restartServerOnConfigChange) {
-            await vscode.commands.executeCommand("rust-analyzer.reload");
+            await vscode.commands.executeCommand("rust-analyzer.restartServer");
             return;
         }
 
@@ -78,7 +77,7 @@ export class Config {
         const userResponse = await vscode.window.showInformationMessage(message, "Restart now");
 
         if (userResponse) {
-            const command = "rust-analyzer.reload";
+            const command = "rust-analyzer.restartServer";
             await vscode.commands.executeCommand(command);
         }
     }
@@ -285,6 +284,10 @@ export class Config {
     get useRustcErrorCode() {
         return this.get<boolean>("diagnostics.useRustcErrorCode");
     }
+
+    get showDependenciesExplorer() {
+        return this.get<boolean>("showDependenciesExplorer");
+    }
 }
 
 // the optional `cb?` parameter is meant to be used to add additional
diff --git a/src/tools/rust-analyzer/editors/code/src/ctx.ts b/src/tools/rust-analyzer/editors/code/src/ctx.ts
index c2dca733df8..a72b5391ff1 100644
--- a/src/tools/rust-analyzer/editors/code/src/ctx.ts
+++ b/src/tools/rust-analyzer/editors/code/src/ctx.ts
@@ -1,11 +1,13 @@
 import * as vscode from "vscode";
 import * as lc from "vscode-languageclient/node";
 import * as ra from "./lsp_ext";
+import * as path from "path";
 
 import { Config, prepareVSCodeConfig } from "./config";
 import { createClient } from "./client";
 import {
     executeDiscoverProject,
+    isDocumentInWorkspace,
     isRustDocument,
     isRustEditor,
     LazyOutputChannel,
@@ -13,6 +15,13 @@ import {
     RustEditor,
 } from "./util";
 import { ServerStatusParams } from "./lsp_ext";
+import {
+    Dependency,
+    DependencyFile,
+    RustDependenciesProvider,
+    DependencyId,
+} from "./dependencies_provider";
+import { execRevealDependency } from "./commands";
 import { PersistentState } from "./persistent_state";
 import { bootstrap } from "./bootstrap";
 import { ExecOptions } from "child_process";
@@ -82,11 +91,22 @@ export class Ctx {
     private state: PersistentState;
     private commandFactories: Record<string, CommandFactory>;
     private commandDisposables: Disposable[];
+    private unlinkedFiles: vscode.Uri[];
+    private _dependencies: RustDependenciesProvider | undefined;
+    private _treeView: vscode.TreeView<Dependency | DependencyFile | DependencyId> | undefined;
 
     get client() {
         return this._client;
     }
 
+    get treeView() {
+        return this._treeView;
+    }
+
+    get dependencies() {
+        return this._dependencies;
+    }
+
     constructor(
         readonly extCtx: vscode.ExtensionContext,
         commandFactories: Record<string, CommandFactory>,
@@ -94,12 +114,11 @@ export class Ctx {
     ) {
         extCtx.subscriptions.push(this);
         this.statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
-        this.statusBar.show();
         this.workspace = workspace;
         this.clientSubscriptions = [];
         this.commandDisposables = [];
         this.commandFactories = commandFactories;
-
+        this.unlinkedFiles = [];
         this.state = new PersistentState(extCtx.globalState);
         this.config = new Config(extCtx);
 
@@ -191,12 +210,13 @@ export class Ctx {
             const discoverProjectCommand = this.config.discoverProjectCommand;
             if (discoverProjectCommand) {
                 const workspaces: JsonProject[] = await Promise.all(
-                    vscode.workspace.workspaceFolders!.map(async (folder): Promise<JsonProject> => {
-                        const rustDocuments = vscode.workspace.textDocuments.filter(isRustDocument);
-                        return discoverWorkspace(rustDocuments, discoverProjectCommand, {
-                            cwd: folder.uri.fsPath,
-                        });
-                    })
+                    vscode.workspace.textDocuments
+                        .filter(isRustDocument)
+                        .map(async (file): Promise<JsonProject> => {
+                            return discoverWorkspace([file], discoverProjectCommand, {
+                                cwd: path.dirname(file.uri.fsPath),
+                            });
+                        })
                 );
 
                 this.addToDiscoveredWorkspaces(workspaces);
@@ -218,7 +238,8 @@ export class Ctx {
                 this.outputChannel,
                 initializationOptions,
                 serverOptions,
-                this.config
+                this.config,
+                this.unlinkedFiles
             );
             this.pushClientCleanup(
                 this._client.onNotification(ra.serverStatus, (params) =>
@@ -242,6 +263,56 @@ export class Ctx {
         }
         await client.start();
         this.updateCommands();
+
+        if (this.config.showDependenciesExplorer) {
+            this.prepareTreeDependenciesView(client);
+        }
+    }
+
+    private prepareTreeDependenciesView(client: lc.LanguageClient) {
+        const ctxInit: CtxInit = {
+            ...this,
+            client: client,
+        };
+        this._dependencies = new RustDependenciesProvider(ctxInit);
+        this._treeView = vscode.window.createTreeView("rustDependencies", {
+            treeDataProvider: this._dependencies,
+            showCollapseAll: true,
+        });
+
+        this.pushExtCleanup(this._treeView);
+        vscode.window.onDidChangeActiveTextEditor(async (e) => {
+            // we should skip documents that belong to the current workspace
+            if (this.shouldRevealDependency(e)) {
+                try {
+                    await execRevealDependency(e);
+                } catch (reason) {
+                    await vscode.window.showErrorMessage(`Dependency error: ${reason}`);
+                }
+            }
+        });
+
+        this.treeView?.onDidChangeVisibility(async (e) => {
+            if (e.visible) {
+                const activeEditor = vscode.window.activeTextEditor;
+                if (this.shouldRevealDependency(activeEditor)) {
+                    try {
+                        await execRevealDependency(activeEditor);
+                    } catch (reason) {
+                        await vscode.window.showErrorMessage(`Dependency error: ${reason}`);
+                    }
+                }
+            }
+        });
+    }
+
+    private shouldRevealDependency(e: vscode.TextEditor | undefined): e is RustEditor {
+        return (
+            e !== undefined &&
+            isRustEditor(e) &&
+            !isDocumentInWorkspace(e.document) &&
+            (this.treeView?.visible || false)
+        );
     }
 
     async restart() {
@@ -335,6 +406,7 @@ export class Ctx {
     setServerStatus(status: ServerStatusParams | { health: "stopped" }) {
         let icon = "";
         const statusBar = this.statusBar;
+        statusBar.show();
         statusBar.tooltip = new vscode.MarkdownString("", true);
         statusBar.tooltip.isTrusted = true;
         switch (status.health) {
@@ -343,6 +415,7 @@ export class Ctx {
                 statusBar.color = undefined;
                 statusBar.backgroundColor = undefined;
                 statusBar.command = "rust-analyzer.stopServer";
+                this.dependencies?.refresh();
                 break;
             case "warning":
                 if (status.message) {
@@ -378,12 +451,17 @@ export class Ctx {
         if (statusBar.tooltip.value) {
             statusBar.tooltip.appendText("\n\n");
         }
+        statusBar.tooltip.appendMarkdown("\n\n[Open logs](command:rust-analyzer.openLogs)");
         statusBar.tooltip.appendMarkdown(
             "\n\n[Reload Workspace](command:rust-analyzer.reloadWorkspace)"
         );
-        statusBar.tooltip.appendMarkdown("\n\n[Open logs](command:rust-analyzer.openLogs)");
-        statusBar.tooltip.appendMarkdown("\n\n[Restart server](command:rust-analyzer.startServer)");
-        statusBar.tooltip.appendMarkdown("[Stop server](command:rust-analyzer.stopServer)");
+        statusBar.tooltip.appendMarkdown(
+            "\n\n[Rebuild Proc Macros](command:rust-analyzer.rebuildProcMacros)"
+        );
+        statusBar.tooltip.appendMarkdown(
+            "\n\n[Restart server](command:rust-analyzer.restartServer)"
+        );
+        statusBar.tooltip.appendMarkdown("\n\n[Stop server](command:rust-analyzer.stopServer)");
         if (!status.quiescent) icon = "$(sync~spin) ";
         statusBar.text = `${icon}rust-analyzer`;
     }
@@ -400,4 +478,5 @@ export class Ctx {
 export interface Disposable {
     dispose(): void;
 }
+
 export type Cmd = (...args: any[]) => unknown;
diff --git a/src/tools/rust-analyzer/editors/code/src/debug.ts b/src/tools/rust-analyzer/editors/code/src/debug.ts
index 268b70b4fbb..cffee1de6af 100644
--- a/src/tools/rust-analyzer/editors/code/src/debug.ts
+++ b/src/tools/rust-analyzer/editors/code/src/debug.ts
@@ -118,8 +118,8 @@ async function getDebugConfiguration(
         return path.normalize(p).replace(wsFolder, "${workspaceFolder" + workspaceQualifier + "}");
     }
 
-    const executable = await getDebugExecutable(runnable);
     const env = prepareEnv(runnable, ctx.config.runnableEnv);
+    const executable = await getDebugExecutable(runnable, env);
     let sourceFileMap = debugOptions.sourceFileMap;
     if (sourceFileMap === "auto") {
         // let's try to use the default toolchain
@@ -156,8 +156,11 @@ async function getDebugConfiguration(
     return debugConfig;
 }
 
-async function getDebugExecutable(runnable: ra.Runnable): Promise<string> {
-    const cargo = new Cargo(runnable.args.workspaceRoot || ".", debugOutput);
+async function getDebugExecutable(
+    runnable: ra.Runnable,
+    env: Record<string, string>
+): Promise<string> {
+    const cargo = new Cargo(runnable.args.workspaceRoot || ".", debugOutput, env);
     const executable = await cargo.executableFromArgs(runnable.args.cargoArgs);
 
     // if we are here, there were no compilation errors.
diff --git a/src/tools/rust-analyzer/editors/code/src/dependencies_provider.ts b/src/tools/rust-analyzer/editors/code/src/dependencies_provider.ts
new file mode 100644
index 00000000000..8900aa9a5f3
--- /dev/null
+++ b/src/tools/rust-analyzer/editors/code/src/dependencies_provider.ts
@@ -0,0 +1,148 @@
+import * as vscode from "vscode";
+import * as fspath from "path";
+import * as fs from "fs";
+import { CtxInit } from "./ctx";
+import * as ra from "./lsp_ext";
+import { FetchDependencyListResult } from "./lsp_ext";
+
+export class RustDependenciesProvider
+    implements vscode.TreeDataProvider<Dependency | DependencyFile>
+{
+    dependenciesMap: { [id: string]: Dependency | DependencyFile };
+    ctx: CtxInit;
+
+    constructor(ctx: CtxInit) {
+        this.dependenciesMap = {};
+        this.ctx = ctx;
+    }
+
+    private _onDidChangeTreeData: vscode.EventEmitter<
+        Dependency | DependencyFile | undefined | null | void
+    > = new vscode.EventEmitter<Dependency | undefined | null | void>();
+
+    readonly onDidChangeTreeData: vscode.Event<
+        Dependency | DependencyFile | undefined | null | void
+    > = this._onDidChangeTreeData.event;
+
+    getDependency(filePath: string): Dependency | DependencyFile | undefined {
+        return this.dependenciesMap[filePath.toLowerCase()];
+    }
+
+    contains(filePath: string): boolean {
+        return filePath.toLowerCase() in this.dependenciesMap;
+    }
+
+    isInitialized(): boolean {
+        return Object.keys(this.dependenciesMap).length !== 0;
+    }
+
+    refresh(): void {
+        this.dependenciesMap = {};
+        this._onDidChangeTreeData.fire();
+    }
+
+    getParent?(
+        element: Dependency | DependencyFile
+    ): vscode.ProviderResult<Dependency | DependencyFile> {
+        if (element instanceof Dependency) return undefined;
+        return element.parent;
+    }
+
+    getTreeItem(element: Dependency | DependencyFile): vscode.TreeItem | Thenable<vscode.TreeItem> {
+        if (element.id! in this.dependenciesMap) return this.dependenciesMap[element.id!];
+        return element;
+    }
+
+    getChildren(
+        element?: Dependency | DependencyFile
+    ): vscode.ProviderResult<Dependency[] | DependencyFile[]> {
+        return new Promise((resolve, _reject) => {
+            if (!vscode.workspace.workspaceFolders) {
+                void vscode.window.showInformationMessage("No dependency in empty workspace");
+                return Promise.resolve([]);
+            }
+            if (element) {
+                const files = fs.readdirSync(element.dependencyPath).map((fileName) => {
+                    const filePath = fspath.join(element.dependencyPath, fileName);
+                    const collapsibleState = fs.lstatSync(filePath).isDirectory()
+                        ? vscode.TreeItemCollapsibleState.Collapsed
+                        : vscode.TreeItemCollapsibleState.None;
+                    const dep = new DependencyFile(fileName, filePath, element, collapsibleState);
+                    this.dependenciesMap[dep.dependencyPath.toLowerCase()] = dep;
+                    return dep;
+                });
+                return resolve(files);
+            } else {
+                return resolve(this.getRootDependencies());
+            }
+        });
+    }
+
+    private async getRootDependencies(): Promise<Dependency[]> {
+        const dependenciesResult: FetchDependencyListResult = await this.ctx.client.sendRequest(
+            ra.fetchDependencyList,
+            {}
+        );
+        const crates = dependenciesResult.crates;
+
+        return crates
+            .map((crate) => {
+                const dep = this.toDep(crate.name || "unknown", crate.version || "", crate.path);
+                this.dependenciesMap[dep.dependencyPath.toLowerCase()] = dep;
+                return dep;
+            })
+            .sort((a, b) => {
+                return a.label.localeCompare(b.label);
+            });
+    }
+
+    private toDep(moduleName: string, version: string, path: string): Dependency {
+        return new Dependency(
+            moduleName,
+            version,
+            vscode.Uri.parse(path).fsPath,
+            vscode.TreeItemCollapsibleState.Collapsed
+        );
+    }
+}
+
+export class Dependency extends vscode.TreeItem {
+    constructor(
+        public readonly label: string,
+        private version: string,
+        readonly dependencyPath: string,
+        public readonly collapsibleState: vscode.TreeItemCollapsibleState
+    ) {
+        super(label, collapsibleState);
+        this.resourceUri = vscode.Uri.file(dependencyPath);
+        this.id = this.resourceUri.fsPath.toLowerCase();
+        this.description = this.version;
+        if (this.version) {
+            this.tooltip = `${this.label}-${this.version}`;
+        } else {
+            this.tooltip = this.label;
+        }
+    }
+}
+
+export class DependencyFile extends vscode.TreeItem {
+    constructor(
+        readonly label: string,
+        readonly dependencyPath: string,
+        readonly parent: Dependency | DependencyFile,
+        public readonly collapsibleState: vscode.TreeItemCollapsibleState
+    ) {
+        super(vscode.Uri.file(dependencyPath), collapsibleState);
+        this.id = this.resourceUri!.fsPath.toLowerCase();
+        const isDir = fs.lstatSync(this.resourceUri!.fsPath).isDirectory();
+        if (!isDir) {
+            this.command = {
+                command: "vscode.open",
+                title: "Open File",
+                arguments: [this.resourceUri],
+            };
+        }
+    }
+}
+
+export type DependencyId = { id: string };
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 872d7199b83..b72804e510c 100644
--- a/src/tools/rust-analyzer/editors/code/src/lsp_ext.ts
+++ b/src/tools/rust-analyzer/editors/code/src/lsp_ext.ts
@@ -10,12 +10,9 @@ export const hover = new lc.RequestType<
     HoverParams,
     (lc.Hover & { actions: CommandLinkGroup[] }) | null,
     void
->("textDocument/hover");
-export type HoverParams = { position: lc.Position | lc.Range } & Omit<
-    lc.TextDocumentPositionParams,
-    "position"
-> &
-    lc.WorkDoneProgressParams;
+>(lc.HoverRequest.method);
+export type HoverParams = { position: lc.Position | lc.Range } & Omit<lc.HoverParams, "position">;
+
 export type CommandLink = {
     /**
      * A tooltip for the command, when represented in the UI.
@@ -43,6 +40,7 @@ export const relatedTests = new lc.RequestType<lc.TextDocumentPositionParams, Te
     "rust-analyzer/relatedTests"
 );
 export const reloadWorkspace = new lc.RequestType0<null, void>("rust-analyzer/reloadWorkspace");
+export const rebuildProcMacros = new lc.RequestType0<null, void>("rust-analyzer/rebuildProcMacros");
 
 export const runFlycheck = new lc.NotificationType<{
     textDocument: lc.TextDocumentIdentifier | null;
@@ -63,12 +61,47 @@ export const viewHir = new lc.RequestType<lc.TextDocumentPositionParams, string,
 export const viewMir = new lc.RequestType<lc.TextDocumentPositionParams, string, void>(
     "rust-analyzer/viewMir"
 );
+export const interpretFunction = new lc.RequestType<lc.TextDocumentPositionParams, string, void>(
+    "rust-analyzer/interpretFunction"
+);
 export const viewItemTree = new lc.RequestType<ViewItemTreeParams, string, void>(
     "rust-analyzer/viewItemTree"
 );
 
 export type AnalyzerStatusParams = { textDocument?: lc.TextDocumentIdentifier };
 
+export interface FetchDependencyListParams {}
+
+export interface FetchDependencyListResult {
+    crates: {
+        name: string | undefined;
+        version: string | undefined;
+        path: string;
+    }[];
+}
+
+export const fetchDependencyList = new lc.RequestType<
+    FetchDependencyListParams,
+    FetchDependencyListResult,
+    void
+>("rust-analyzer/fetchDependencyList");
+
+export interface FetchDependencyGraphParams {}
+
+export interface FetchDependencyGraphResult {
+    crates: {
+        name: string;
+        version: string;
+        path: string;
+    }[];
+}
+
+export const fetchDependencyGraph = new lc.RequestType<
+    FetchDependencyGraphParams,
+    FetchDependencyGraphResult,
+    void
+>("rust-analyzer/fetchDependencyGraph");
+
 export type ExpandMacroParams = {
     textDocument: lc.TextDocumentIdentifier;
     position: lc.Position;
diff --git a/src/tools/rust-analyzer/editors/code/src/main.ts b/src/tools/rust-analyzer/editors/code/src/main.ts
index d5de00561b1..be9bc9d363c 100644
--- a/src/tools/rust-analyzer/editors/code/src/main.ts
+++ b/src/tools/rust-analyzer/editors/code/src/main.ts
@@ -120,13 +120,11 @@ function createCommands(): Record<string, CommandFactory> {
             enabled: commands.onEnter,
             disabled: (_) => () => vscode.commands.executeCommand("default:type", { text: "\n" }),
         },
-        reload: {
+        restartServer: {
             enabled: (ctx) => async () => {
-                void vscode.window.showInformationMessage("Reloading rust-analyzer...");
                 await ctx.restart();
             },
             disabled: (ctx) => async () => {
-                void vscode.window.showInformationMessage("Reloading rust-analyzer...");
                 await ctx.start();
             },
         },
@@ -153,6 +151,7 @@ function createCommands(): Record<string, CommandFactory> {
         memoryUsage: { enabled: commands.memoryUsage },
         shuffleCrateGraph: { enabled: commands.shuffleCrateGraph },
         reloadWorkspace: { enabled: commands.reloadWorkspace },
+        rebuildProcMacros: { enabled: commands.rebuildProcMacros },
         addProject: { enabled: commands.addProject },
         matchingBrace: { enabled: commands.matchingBrace },
         joinLines: { enabled: commands.joinLines },
@@ -160,6 +159,7 @@ function createCommands(): Record<string, CommandFactory> {
         syntaxTree: { enabled: commands.syntaxTree },
         viewHir: { enabled: commands.viewHir },
         viewMir: { enabled: commands.viewMir },
+        interpretFunction: { enabled: commands.interpretFunction },
         viewFileText: { enabled: commands.viewFileText },
         viewItemTree: { enabled: commands.viewItemTree },
         viewCrateGraph: { enabled: commands.viewCrateGraph },
@@ -190,5 +190,6 @@ function createCommands(): Record<string, CommandFactory> {
         showReferences: { enabled: commands.showReferences },
         triggerParameterHints: { enabled: commands.triggerParameterHints },
         openLogs: { enabled: commands.openLogs },
+        revealDependency: { enabled: commands.revealDependency },
     };
 }
diff --git a/src/tools/rust-analyzer/editors/code/src/run.ts b/src/tools/rust-analyzer/editors/code/src/run.ts
index 35627e2fc6b..623fb102953 100644
--- a/src/tools/rust-analyzer/editors/code/src/run.ts
+++ b/src/tools/rust-analyzer/editors/code/src/run.ts
@@ -157,7 +157,7 @@ export async function createTask(runnable: ra.Runnable, config: Config): Promise
 
     cargoTask.presentationOptions.clear = true;
     // Sadly, this doesn't prevent focus stealing if the terminal is currently
-    // hidden, and will become revealed due to task exucution.
+    // hidden, and will become revealed due to task execution.
     cargoTask.presentationOptions.focus = false;
 
     return cargoTask;
diff --git a/src/tools/rust-analyzer/editors/code/src/tasks.ts b/src/tools/rust-analyzer/editors/code/src/tasks.ts
index e6239deeb21..d6509d9aa6e 100644
--- a/src/tools/rust-analyzer/editors/code/src/tasks.ts
+++ b/src/tools/rust-analyzer/editors/code/src/tasks.ts
@@ -128,7 +128,7 @@ export async function buildCargoTask(
         name,
         TASK_SOURCE,
         exec,
-        ["$rustc"]
+        ["$rustc", "$rust-panic"]
     );
 }
 
diff --git a/src/tools/rust-analyzer/editors/code/src/toolchain.ts b/src/tools/rust-analyzer/editors/code/src/toolchain.ts
index eb70b88871e..917a1d6b099 100644
--- a/src/tools/rust-analyzer/editors/code/src/toolchain.ts
+++ b/src/tools/rust-analyzer/editors/code/src/toolchain.ts
@@ -18,7 +18,11 @@ export interface ArtifactSpec {
 }
 
 export class Cargo {
-    constructor(readonly rootFolder: string, readonly output: vscode.OutputChannel) {}
+    constructor(
+        readonly rootFolder: string,
+        readonly output: vscode.OutputChannel,
+        readonly env: Record<string, string>
+    ) {}
 
     // Made public for testing purposes
     static artifactSpec(args: readonly string[]): ArtifactSpec {
@@ -102,6 +106,7 @@ export class Cargo {
             const cargo = cp.spawn(path, cargoArgs, {
                 stdio: ["ignore", "pipe", "pipe"],
                 cwd: this.rootFolder,
+                env: this.env,
             });
 
             cargo.on("error", (err) => reject(new Error(`could not launch cargo: ${err}`)));
diff --git a/src/tools/rust-analyzer/editors/code/src/util.ts b/src/tools/rust-analyzer/editors/code/src/util.ts
index 922fbcbcf35..b6b779e2660 100644
--- a/src/tools/rust-analyzer/editors/code/src/util.ts
+++ b/src/tools/rust-analyzer/editors/code/src/util.ts
@@ -112,6 +112,19 @@ export function isRustEditor(editor: vscode.TextEditor): editor is RustEditor {
     return isRustDocument(editor.document);
 }
 
+export function isDocumentInWorkspace(document: RustDocument): boolean {
+    const workspaceFolders = vscode.workspace.workspaceFolders;
+    if (!workspaceFolders) {
+        return false;
+    }
+    for (const folder of workspaceFolders) {
+        if (document.uri.fsPath.startsWith(folder.uri.fsPath)) {
+            return true;
+        }
+    }
+    return false;
+}
+
 export function isValidExecutable(path: string): boolean {
     log.debug("Checking availability of a binary at", path);