mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-27 12:29:21 +00:00
Merge commit 'aa9bc86125
' into sync-from-ra
This commit is contained in:
parent
1570299af4
commit
c48062fe2a
598 changed files with 57696 additions and 17615 deletions
|
@ -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",
|
||||
|
@ -119,6 +120,11 @@
|
|||
"title": "View Mir",
|
||||
"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)",
|
||||
|
@ -199,13 +205,18 @@
|
|||
"title": "Reload workspace",
|
||||
"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": [
|
||||
|
@ -1510,6 +1679,16 @@
|
|||
],
|
||||
"pattern": "$rustc-json"
|
||||
},
|
||||
{
|
||||
"name": "rust-panic",
|
||||
"owner": "rust-panic",
|
||||
"source": "panic",
|
||||
"fileLocation": [
|
||||
"autoDetect",
|
||||
"${workspaceRoot}"
|
||||
],
|
||||
"pattern": "$rust-panic"
|
||||
},
|
||||
{
|
||||
"name": "rustc-watch",
|
||||
"owner": "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",
|
||||
|
|
36
editors/code/rustdoc.markdown.injection.tmGrammar.json
Normal file
36
editors/code/rustdoc.markdown.injection.tmGrammar.json
Normal file
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 (
|
||||
|
|
|
@ -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\
|
||||
*  |-beches\
|
||||
*  |-src\
|
||||
*  |- ...
|
||||
* - std
|
||||
* 2. reveal every children of src:
|
||||
* core
|
||||
* alloc\
|
||||
*  |-beches\
|
||||
*  |-src\
|
||||
*   |- lib.rs\
|
||||
*   |- str.rs <------- FOUND IT!\
|
||||
*   |- ...\
|
||||
*  |- ...\
|
||||
* 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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
148
editors/code/src/dependencies_provider.ts
Normal file
148
editors/code/src/dependencies_provider.ts
Normal file
|
@ -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 };
|
|
@ -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;
|
||||
|
|
|
@ -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 },
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -128,7 +128,7 @@ export async function buildCargoTask(
|
|||
name,
|
||||
TASK_SOURCE,
|
||||
exec,
|
||||
["$rustc"]
|
||||
["$rustc", "$rust-panic"]
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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}`)));
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue