From 5931b9851989e067ff09fb8356b20ba5ecf1741e Mon Sep 17 00:00:00 2001 From: Pascal Kuthe Date: Sun, 26 Mar 2023 18:10:09 +0200 Subject: [PATCH] Add config to mark diagnostic sources as persistent --- helix-core/src/syntax.rs | 2 + helix-term/src/application.rs | 105 ++++++++++++++++++++++------------ helix-view/src/document.rs | 21 ++++++- languages.toml | 1 + 4 files changed, 89 insertions(+), 40 deletions(-) diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs index f43b03ade7acf..fda1a2a7a5e5b 100644 --- a/helix-core/src/syntax.rs +++ b/helix-core/src/syntax.rs @@ -154,6 +154,8 @@ pub struct LanguageConfiguration { /// Hardcoded LSP root directories relative to the workspace root, like `examples` or `tools/fuzz`. /// Falling back to the current working directory if none are configured. pub workspace_lsp_roots: Option>, + #[serde(default)] + pub persistent_diagnostic_sources: Vec, } #[derive(Debug, PartialEq, Eq, Hash)] diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index e1a622f9d06b5..3baf1ce4b7c31 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -738,7 +738,7 @@ impl Application { )); } } - Notification::PublishDiagnostics(params) => { + Notification::PublishDiagnostics(mut params) => { let path = match params.uri.to_file_path() { Ok(path) => path, Err(_) => { @@ -752,31 +752,69 @@ impl Application { return; } let offset_encoding = language_server.offset_encoding(); - let doc = self.editor.document_by_path_mut(&path).filter(|doc| { - if let Some(version) = params.version { - if version != doc.version() { - log::info!("Version ({version}) is out of date for {path:?} (expected ({}), dropping PublishDiagnostic notification", doc.version()); - return false; + // have to inline the function because of borrow checking... + let doc = self.editor.documents.values_mut() + .find(|doc| doc.path().map(|p| p == &path).unwrap_or(false)) + .filter(|doc| { + if let Some(version) = params.version { + if version != doc.version() { + log::info!("Version ({version}) is out of date for {path:?} (expected ({}), dropping PublishDiagnostic notification", doc.version()); + return false; + } } - } - - true - }); + true + }); if let Some(doc) = doc { - let lang_conf = doc.language_config(); - let text = doc.text(); + let lang_conf = doc.language.clone(); + let text = doc.text().clone(); + + let mut unchaged_diag_sources_ = Vec::new(); + if let Some(lang_conf) = &lang_conf { + if let Some(old_diagnostics) = + self.editor.diagnostics.get(¶ms.uri) + { + if !lang_conf.persistent_diagnostic_sources.is_empty() { + // Sort diagnostics first by severity and then by line numbers. + // Note: The `lsp::DiagnosticSeverity` enum is already defined in decreasing order + params + .diagnostics + .sort_unstable_by_key(|d| (d.severity, d.range.start)); + } + for source in &lang_conf.persistent_diagnostic_sources { + let new_diagnostics = params + .diagnostics + .iter() + .filter(|d| d.source.as_ref() == Some(source)); + let old_diagnostics = old_diagnostics + .iter() + .filter(|(d, d_server)| { + *d_server == server_id + && d.source.as_ref() == Some(source) + }) + .map(|(d, _)| d); + if new_diagnostics.eq(old_diagnostics) { + unchaged_diag_sources_.push(source.clone()) + } + } + } + } - let diagnostics = params - .diagnostics - .iter() - .filter_map(|diagnostic| { + let unchaged_diag_sources = &unchaged_diag_sources_; + let diagnostics = + params.diagnostics.iter().filter_map(move |diagnostic| { use helix_core::diagnostic::{Diagnostic, Range, Severity::*}; use lsp::DiagnosticSeverity; + if diagnostic.source.as_ref().map_or(false, |source| { + unchaged_diag_sources.contains(source) + }) { + return None; + } + // TODO: convert inside server let start = if let Some(start) = lsp_pos_to_pos( - text, + &text, diagnostic.range.start, offset_encoding, ) { @@ -787,7 +825,7 @@ impl Application { }; let end = if let Some(end) = - lsp_pos_to_pos(text, diagnostic.range.end, offset_encoding) + lsp_pos_to_pos(&text, diagnostic.range.end, offset_encoding) { end } else { @@ -807,7 +845,7 @@ impl Application { ), }); - if let Some(lang_conf) = lang_conf { + if let Some(lang_conf) = &lang_conf { if let Some(severity) = severity { if severity < lang_conf.diagnostic_severity { return None; @@ -857,38 +895,31 @@ impl Application { data: diagnostic.data.clone(), language_server_id: server_id, }) - }) - .collect(); + }); - doc.replace_diagnostics(diagnostics, server_id); + doc.replace_diagnostics(diagnostics, unchaged_diag_sources, server_id); } - let mut diagnostics = params - .diagnostics - .into_iter() - .map(|d| (d, server_id)) - .collect(); + let diagnostics = params.diagnostics.into_iter().map(|d| (d, server_id)); // Insert the original lsp::Diagnostics here because we may have no open document // for diagnosic message and so we can't calculate the exact position. // When using them later in the diagnostics picker, we calculate them on-demand. - match self.editor.diagnostics.entry(params.uri) { + let diagnostics = match self.editor.diagnostics.entry(params.uri) { Entry::Occupied(o) => { let current_diagnostics = o.into_mut(); // there may entries of other language servers, which is why we can't overwrite the whole entry current_diagnostics.retain(|(_, lsp_id)| *lsp_id != server_id); - current_diagnostics.append(&mut diagnostics); - // Sort diagnostics first by severity and then by line numbers. - // Note: The `lsp::DiagnosticSeverity` enum is already defined in decreasing order + current_diagnostics.extend(diagnostics); current_diagnostics - .sort_unstable_by_key(|(d, _)| (d.severity, d.range.start)); - } - Entry::Vacant(v) => { - diagnostics - .sort_unstable_by_key(|(d, _)| (d.severity, d.range.start)); - v.insert(diagnostics); + // Sort diagnostics first by severity and then by line numbers. } + Entry::Vacant(v) => v.insert(diagnostics.collect()), }; + + // Sort diagnostics first by severity and then by line numbers. + // Note: The `lsp::DiagnosticSeverity` enum is already defined in decreasing order + diagnostics.sort_unstable_by_key(|(d, _)| (d.severity, d.range.start)); } Notification::ShowMessage(params) => { log::warn!("unhandled window/showMessage: {:?}", params); diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index b08370f9f371a..ebd621d2a3b73 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -1667,11 +1667,26 @@ impl Document { pub fn replace_diagnostics( &mut self, - mut diagnostics: Vec, + diagnostics: impl IntoIterator, + unchanged_sources: &[String], language_server_id: usize, ) { - self.clear_diagnostics(language_server_id); - self.diagnostics.append(&mut diagnostics); + if unchanged_sources.is_empty() { + self.clear_diagnostics(language_server_id); + } else { + self.diagnostics.retain(|d| { + if d.language_server_id != language_server_id { + return true; + } + + if let Some(source) = &d.source { + unchanged_sources.contains(source) + } else { + false + } + }); + } + self.diagnostics.extend(diagnostics); self.diagnostics .sort_unstable_by_key(|diagnostic| diagnostic.range); } diff --git a/languages.toml b/languages.toml index 0c92df4e7300c..9d3acc5a5ebf4 100644 --- a/languages.toml +++ b/languages.toml @@ -151,6 +151,7 @@ auto-format = true comment-token = "//" language-servers = [ "rust-analyzer" ] indent = { tab-width = 4, unit = " " } +persistent-diagnostic-sources = ["rustc", "clippy"] [language.auto-pairs] '(' = ')'