on master help sign in
Send active diagnostics to LSP when requesting code actions. (#2005)

* Send active diagnostics to LSP when requesting code actions. This allows for e.g. clangd to properly send the quickfix code actions corresponding to those diagnostics as options. The LSP spec v3.16.0 introduced an opaque `data` member that would allow the server to persist arbitrary data between the diagnostic and the code actions request, but this is not supported yet by this commit. * Reuse existing range_to_lsp_range functionality

commit33b7483db53684cdd0a36f873662f8d6ff163201 authorRobin Jadoul <robin.jadoul@gmail.com> dateSun 17 Apr 2022, 05:05 (5 years ago) parent2eca290
3 files changed+54−9
helix-lsp/src/client.rs+2−1 helix-lsp/src/lib.rs+30 helix-term/src/commands/lsp.rs+22−8
helix-lsp/src/client.rs+2−1
@@ -842,11 +842,12 @@ impl Client {
842842 &self,
843843 text_document: lsp::TextDocumentIdentifier,
844844 range: lsp::Range,
845+ context: lsp::CodeActionContext,
845846 ) -> impl Future<Output = Result<Value>> {
846847 let params = lsp::CodeActionParams {
847848 text_document,
848849 range,
849− context: lsp::CodeActionContext::default(),
850+ context,
850851 work_done_progress_params: lsp::WorkDoneProgressParams::default(),
851852 partial_result_params: lsp::PartialResultParams::default(),
852853 };
helix-lsp/src/lib.rs+30
@@ -58,6 +58,36 @@ pub mod util {
5858 use super::*;
5959 use helix_core::{Range, Rope, Transaction};
6060
61+ /// Converts a diagnostic in the document to [`lsp::Diagnostic`].
62+ ///
63+ /// Panics when [`pos_to_lsp_pos`] would for an invalid range on the diagnostic.
64+ pub fn diagnostic_to_lsp_diagnostic(
65+ doc: &Rope,
66+ diag: &helix_core::diagnostic::Diagnostic,
67+ offset_encoding: OffsetEncoding,
68+ ) -> lsp::Diagnostic {
69+ use helix_core::diagnostic::Severity::*;
70+
71+ let range = Range::new(diag.range.start, diag.range.end);
72+ let severity = diag.severity.map(|s| match s {
73+ Hint => lsp::DiagnosticSeverity::HINT,
74+ Info => lsp::DiagnosticSeverity::INFORMATION,
75+ Warning => lsp::DiagnosticSeverity::WARNING,
76+ Error => lsp::DiagnosticSeverity::ERROR,
77+ });
78+
79+ // TODO: add support for Diagnostic.data
80+ lsp::Diagnostic::new(
81+ range_to_lsp_range(doc, range, offset_encoding),
82+ severity,
83+ None,
84+ None,
85+ diag.message.to_owned(),
86+ None,
87+ None,
88+ )
89+ }
90+
6191 /// Converts [`lsp::Position`] to a position in the document.
6292 ///
6393 /// Returns `None` if position exceeds document length or an operation overflows.
helix-term/src/commands/lsp.rs+22−8
@@ -1,6 +1,6 @@
11use helix_lsp::{
22 block_on, lsp,
3− util::{lsp_pos_to_pos, lsp_range_to_range, range_to_lsp_range},
3+ util::{diagnostic_to_lsp_diagnostic, lsp_pos_to_pos, lsp_range_to_range, range_to_lsp_range},
44 OffsetEncoding,
55};
66
@@ -192,15 +192,29 @@ pub fn code_action(cx: &mut Context) {
192192
193193 let language_server = language_server!(cx.editor, doc);
194194
195− let range = range_to_lsp_range(
196− doc.text(),
197− doc.selection(view.id).primary(),
198− language_server.offset_encoding(),
199− );
200−
201− let future = language_server.code_actions(doc.identifier(), range);
195+ let selection_range = doc.selection(view.id).primary();
202196 let offset_encoding = language_server.offset_encoding();
203197
198+ let range = range_to_lsp_range(doc.text(), selection_range, offset_encoding);
199+
200+ let future = language_server.code_actions(
201+ doc.identifier(),
202+ range,
203+ // Filter and convert overlapping diagnostics
204+ lsp::CodeActionContext {
205+ diagnostics: doc
206+ .diagnostics()
207+ .iter()
208+ .filter(|&diag| {
209+ selection_range
210+ .overlaps(&helix_core::Range::new(diag.range.start, diag.range.end))
211+ })
212+ .map(|diag| diagnostic_to_lsp_diagnostic(doc.text(), diag, offset_encoding))
213+ .collect(),
214+ only: None,
215+ },
216+ );
217+
204218 cx.callback(
205219 future,
206220 move |editor, compositor, response: Option<lsp::CodeActionResponse>| {