mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 21:43:37 +00:00
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
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…
Reference in a new issue