Unnamed repository; edit this file 'description' to name the repository.
Auto merge of #13633 - Veykril:vscode-full-diagnostics, r=Veykril
feat: Allow viewing the full compiler diagnostic in a readonly textview ![Code_y1qrash9gg](https://user-images.githubusercontent.com/3757771/202780459-f751f65d-2b1b-4dc3-9685-100d65ebf6a0.gif) Also adds a VSCode only config that replaces the split diagnostic message with the first relevant part of the diagnostic output ![Code_7k4qsMkx5e](https://user-images.githubusercontent.com/3757771/202780346-cf9137d9-eb77-46b7-aed6-c73a2e41e1c7.png) This only affects diagnostics generated by primary spans and has no effect on other clients than VSCode. Fixes https://github.com/rust-lang/rust-analyzer/issues/13574
bors 2022-11-19
parent bee27eb · parent 8452844 · commit 791cb87
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs22
-rw-r--r--editors/code/package.json5
-rw-r--r--editors/code/src/client.ts42
-rw-r--r--editors/code/src/config.ts3
-rw-r--r--editors/code/src/ctx.ts3
-rw-r--r--editors/code/src/main.ts24
6 files changed, 86 insertions, 13 deletions
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index 189ac2fbf5..beb23c54c9 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -359,14 +359,15 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
.iter()
.flat_map(|primary_span| {
let primary_location = primary_location(config, workspace_root, primary_span, snap);
-
- let mut message = message.clone();
- if needs_primary_span_label {
- if let Some(primary_span_label) = &primary_span.label {
- format_to!(message, "\n{}", primary_span_label);
+ let message = {
+ let mut message = message.clone();
+ if needs_primary_span_label {
+ if let Some(primary_span_label) = &primary_span.label {
+ format_to!(message, "\n{}", primary_span_label);
+ }
}
- }
-
+ message
+ };
// Each primary diagnostic span may result in multiple LSP diagnostics.
let mut diagnostics = Vec::new();
@@ -417,7 +418,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
message: message.clone(),
related_information: Some(information_for_additional_diagnostic),
tags: if tags.is_empty() { None } else { Some(tags.clone()) },
- data: None,
+ data: Some(serde_json::json!({ "rendered": rd.rendered })),
};
diagnostics.push(MappedRustDiagnostic {
url: secondary_location.uri,
@@ -449,7 +450,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
}
},
tags: if tags.is_empty() { None } else { Some(tags.clone()) },
- data: None,
+ data: Some(serde_json::json!({ "rendered": rd.rendered })),
},
fix: None,
});
@@ -534,7 +535,8 @@ mod tests {
Config::new(workspace_root.to_path_buf(), ClientCapabilities::default()),
);
let snap = state.snapshot();
- let actual = map_rust_diagnostic_to_lsp(&config, &diagnostic, workspace_root, &snap);
+ let mut actual = map_rust_diagnostic_to_lsp(&config, &diagnostic, workspace_root, &snap);
+ actual.iter_mut().for_each(|diag| diag.diagnostic.data = None);
expect.assert_debug_eq(&actual)
}
diff --git a/editors/code/package.json b/editors/code/package.json
index 0c78165960..b1c3473b82 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -396,6 +396,11 @@
"default": true,
"type": "boolean"
},
+ "rust-analyzer.diagnostics.previewRustcOutput": {
+ "markdownDescription": "Whether to show the main part of the rendered rustc output of a diagnostic message.",
+ "default": false,
+ "type": "boolean"
+ },
"$generated-start": {},
"rust-analyzer.assist.emitMustUse": {
"markdownDescription": "Whether to insert #[must_use] when generating `as_` methods\nfor enum variants.",
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index fb667619c8..23e039722e 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -4,7 +4,7 @@ import * as ra from "../src/lsp_ext";
import * as Is from "vscode-languageclient/lib/common/utils/is";
import { assert } from "./util";
import { WorkspaceEdit } from "vscode";
-import { substituteVSCodeVariables } from "./config";
+import { Config, substituteVSCodeVariables } from "./config";
import { randomUUID } from "crypto";
export interface Env {
@@ -66,7 +66,8 @@ export async function createClient(
traceOutputChannel: vscode.OutputChannel,
outputChannel: vscode.OutputChannel,
initializationOptions: vscode.WorkspaceConfiguration,
- serverOptions: lc.ServerOptions
+ serverOptions: lc.ServerOptions,
+ config: Config
): Promise<lc.LanguageClient> {
const clientOptions: lc.LanguageClientOptions = {
documentSelector: [{ scheme: "file", language: "rust" }],
@@ -99,6 +100,43 @@ export async function createClient(
}
},
},
+ async handleDiagnostics(
+ uri: vscode.Uri,
+ diagnostics: vscode.Diagnostic[],
+ next: lc.HandleDiagnosticsSignature
+ ) {
+ const preview = config.previewRustcOutput;
+ diagnostics.forEach((diag, idx) => {
+ // Abuse the fact that VSCode leaks the LSP diagnostics data field through the
+ // Diagnostic class, if they ever break this we are out of luck and have to go
+ // back to the worst diagnostics experience ever:)
+
+ // We encode the rendered output of a rustc diagnostic in the rendered field of
+ // the data payload of the lsp diagnostic. If that field exists, overwrite the
+ // diagnostic code such that clicking it opens the diagnostic in a readonly
+ // text editor for easy inspection
+ const rendered = (diag as unknown as { data?: { rendered?: string } }).data
+ ?.rendered;
+ if (rendered) {
+ if (preview) {
+ const index = rendered.match(/^(note|help):/m)?.index || 0;
+ diag.message = rendered
+ .substring(0, index)
+ .replace(/^ -->[^\n]+\n/m, "");
+ }
+ diag.code = {
+ target: vscode.Uri.from({
+ scheme: "rust-analyzer-diagnostics-view",
+ path: "/diagnostic message",
+ fragment: uri.toString(),
+ query: idx.toString(),
+ }),
+ value: "Click for full compiler diagnostic",
+ };
+ }
+ });
+ return next(uri, diagnostics);
+ },
async provideHover(
document: vscode.TextDocument,
position: vscode.Position,
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index 632a7d86fa..d8dbd1df16 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -238,6 +238,9 @@ export class Config {
gotoTypeDef: this.get<boolean>("hover.actions.gotoTypeDef.enable"),
};
}
+ get previewRustcOutput() {
+ return this.get<boolean>("diagnostics.previewRustcOutput");
+ }
}
const VarRegex = new RegExp(/\$\{(.+?)\}/g);
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index 3e366525ee..d6cee5c8fc 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -179,7 +179,8 @@ export class Ctx {
this.traceOutputChannel,
this.outputChannel,
initializationOptions,
- serverOptions
+ serverOptions,
+ this.config
);
this.pushClientCleanup(
this._client.onNotification(ra.serverStatus, (params) =>
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index e76b657c1b..25f1e83d10 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -48,6 +48,30 @@ async function activateServer(ctx: Ctx): Promise<RustAnalyzerExtensionApi> {
ctx.pushExtCleanup(activateTaskProvider(ctx.config));
}
+ ctx.pushExtCleanup(
+ vscode.workspace.registerTextDocumentContentProvider(
+ "rust-analyzer-diagnostics-view",
+ new (class implements vscode.TextDocumentContentProvider {
+ async provideTextDocumentContent(uri: vscode.Uri): Promise<string> {
+ const diags = ctx.client?.diagnostics?.get(
+ vscode.Uri.parse(uri.fragment, true)
+ );
+ if (!diags) {
+ return "Unable to find original rustc diagnostic";
+ }
+
+ const diag = diags[parseInt(uri.query)];
+ if (!diag) {
+ return "Unable to find original rustc diagnostic";
+ }
+ const rendered = (diag as unknown as { data?: { rendered?: string } }).data
+ ?.rendered;
+ return rendered ?? "Unable to find original rustc diagnostic";
+ }
+ })()
+ )
+ );
+
vscode.workspace.onDidChangeWorkspaceFolders(
async (_) => ctx.onWorkspaceFolderChanges(),
null,