mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-02 22:54:58 +00:00
Auto merge of #13633 - Veykril:vscode-full-diagnostics, r=Veykril
feat: Allow viewing the full compiler diagnostic in a readonly textview  Also adds a VSCode only config that replaces the split diagnostic message with the first relevant part of the diagnostic output  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
This commit is contained in:
commit
791cb87cdf
6 changed files with 86 additions and 13 deletions
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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.",
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -179,7 +179,8 @@ export class Ctx {
|
|||
this.traceOutputChannel,
|
||||
this.outputChannel,
|
||||
initializationOptions,
|
||||
serverOptions
|
||||
serverOptions,
|
||||
this.config
|
||||
);
|
||||
this.pushClientCleanup(
|
||||
this._client.onNotification(ra.serverStatus, (params) =>
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue