about summary refs log tree commit diff
diff options
context:
space:
mode:
-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 189ac2fbf53..beb23c54c9f 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 0c781659604..b1c3473b825 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 fb667619c86..23e039722ee 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 632a7d86faa..d8dbd1df16d 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 3e366525ee2..d6cee5c8fc6 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 e76b657c1bf..25f1e83d109 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,