mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-28 14:03:35 +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()
|
.iter()
|
||||||
.flat_map(|primary_span| {
|
.flat_map(|primary_span| {
|
||||||
let primary_location = primary_location(config, workspace_root, primary_span, snap);
|
let primary_location = primary_location(config, workspace_root, primary_span, snap);
|
||||||
|
let message = {
|
||||||
let mut message = message.clone();
|
let mut message = message.clone();
|
||||||
if needs_primary_span_label {
|
if needs_primary_span_label {
|
||||||
if let Some(primary_span_label) = &primary_span.label {
|
if let Some(primary_span_label) = &primary_span.label {
|
||||||
format_to!(message, "\n{}", primary_span_label);
|
format_to!(message, "\n{}", primary_span_label);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
message
|
||||||
|
};
|
||||||
// Each primary diagnostic span may result in multiple LSP diagnostics.
|
// Each primary diagnostic span may result in multiple LSP diagnostics.
|
||||||
let mut diagnostics = Vec::new();
|
let mut diagnostics = Vec::new();
|
||||||
|
|
||||||
|
@ -417,7 +418,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
|
||||||
message: message.clone(),
|
message: message.clone(),
|
||||||
related_information: Some(information_for_additional_diagnostic),
|
related_information: Some(information_for_additional_diagnostic),
|
||||||
tags: if tags.is_empty() { None } else { Some(tags.clone()) },
|
tags: if tags.is_empty() { None } else { Some(tags.clone()) },
|
||||||
data: None,
|
data: Some(serde_json::json!({ "rendered": rd.rendered })),
|
||||||
};
|
};
|
||||||
diagnostics.push(MappedRustDiagnostic {
|
diagnostics.push(MappedRustDiagnostic {
|
||||||
url: secondary_location.uri,
|
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()) },
|
tags: if tags.is_empty() { None } else { Some(tags.clone()) },
|
||||||
data: None,
|
data: Some(serde_json::json!({ "rendered": rd.rendered })),
|
||||||
},
|
},
|
||||||
fix: None,
|
fix: None,
|
||||||
});
|
});
|
||||||
|
@ -534,7 +535,8 @@ mod tests {
|
||||||
Config::new(workspace_root.to_path_buf(), ClientCapabilities::default()),
|
Config::new(workspace_root.to_path_buf(), ClientCapabilities::default()),
|
||||||
);
|
);
|
||||||
let snap = state.snapshot();
|
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)
|
expect.assert_debug_eq(&actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -396,6 +396,11 @@
|
||||||
"default": true,
|
"default": true,
|
||||||
"type": "boolean"
|
"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": {},
|
"$generated-start": {},
|
||||||
"rust-analyzer.assist.emitMustUse": {
|
"rust-analyzer.assist.emitMustUse": {
|
||||||
"markdownDescription": "Whether to insert #[must_use] when generating `as_` methods\nfor enum variants.",
|
"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 * as Is from "vscode-languageclient/lib/common/utils/is";
|
||||||
import { assert } from "./util";
|
import { assert } from "./util";
|
||||||
import { WorkspaceEdit } from "vscode";
|
import { WorkspaceEdit } from "vscode";
|
||||||
import { substituteVSCodeVariables } from "./config";
|
import { Config, substituteVSCodeVariables } from "./config";
|
||||||
import { randomUUID } from "crypto";
|
import { randomUUID } from "crypto";
|
||||||
|
|
||||||
export interface Env {
|
export interface Env {
|
||||||
|
@ -66,7 +66,8 @@ export async function createClient(
|
||||||
traceOutputChannel: vscode.OutputChannel,
|
traceOutputChannel: vscode.OutputChannel,
|
||||||
outputChannel: vscode.OutputChannel,
|
outputChannel: vscode.OutputChannel,
|
||||||
initializationOptions: vscode.WorkspaceConfiguration,
|
initializationOptions: vscode.WorkspaceConfiguration,
|
||||||
serverOptions: lc.ServerOptions
|
serverOptions: lc.ServerOptions,
|
||||||
|
config: Config
|
||||||
): Promise<lc.LanguageClient> {
|
): Promise<lc.LanguageClient> {
|
||||||
const clientOptions: lc.LanguageClientOptions = {
|
const clientOptions: lc.LanguageClientOptions = {
|
||||||
documentSelector: [{ scheme: "file", language: "rust" }],
|
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(
|
async provideHover(
|
||||||
document: vscode.TextDocument,
|
document: vscode.TextDocument,
|
||||||
position: vscode.Position,
|
position: vscode.Position,
|
||||||
|
|
|
@ -238,6 +238,9 @@ export class Config {
|
||||||
gotoTypeDef: this.get<boolean>("hover.actions.gotoTypeDef.enable"),
|
gotoTypeDef: this.get<boolean>("hover.actions.gotoTypeDef.enable"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
get previewRustcOutput() {
|
||||||
|
return this.get<boolean>("diagnostics.previewRustcOutput");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const VarRegex = new RegExp(/\$\{(.+?)\}/g);
|
const VarRegex = new RegExp(/\$\{(.+?)\}/g);
|
||||||
|
|
|
@ -179,7 +179,8 @@ export class Ctx {
|
||||||
this.traceOutputChannel,
|
this.traceOutputChannel,
|
||||||
this.outputChannel,
|
this.outputChannel,
|
||||||
initializationOptions,
|
initializationOptions,
|
||||||
serverOptions
|
serverOptions,
|
||||||
|
this.config
|
||||||
);
|
);
|
||||||
this.pushClientCleanup(
|
this.pushClientCleanup(
|
||||||
this._client.onNotification(ra.serverStatus, (params) =>
|
this._client.onNotification(ra.serverStatus, (params) =>
|
||||||
|
|
|
@ -48,6 +48,30 @@ async function activateServer(ctx: Ctx): Promise<RustAnalyzerExtensionApi> {
|
||||||
ctx.pushExtCleanup(activateTaskProvider(ctx.config));
|
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(
|
vscode.workspace.onDidChangeWorkspaceFolders(
|
||||||
async (_) => ctx.onWorkspaceFolderChanges(),
|
async (_) => ctx.onWorkspaceFolderChanges(),
|
||||||
null,
|
null,
|
||||||
|
|
Loading…
Reference in a new issue