diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index bd6b5a870e3d..a46c0089f666 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -15,7 +15,7 @@ use helix_view::{ graphics::Rect, theme, tree::Layout, - Align, Editor, + Align, Document, Editor, }; use serde_json::json; use tui::backend::Backend; @@ -737,7 +737,7 @@ impl Application { )); } } - Notification::PublishDiagnostics(mut params) => { + Notification::PublishDiagnostics(params) => { let uri = match helix_core::Uri::try_from(params.uri) { Ok(uri) => uri, Err(err) => { @@ -763,41 +763,24 @@ impl Application { true }); + let diagnostics: Vec<(lsp::Diagnostic, LanguageServerId)> = params + .diagnostics + .into_iter() + .map(|d| (d, server_id)) + .collect(); + let mut unchanged_diag_sources = Vec::new(); if let Some(doc) = &doc { - let lang_conf = doc.language.clone(); - - if let Some(lang_conf) = &lang_conf { - if let Some(old_diagnostics) = self.editor.diagnostics.get(&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_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) { - unchanged_diag_sources.push(source.clone()) - } - } - } + if let Some(old_diagnostics) = self.editor.diagnostics.get(&uri) { + unchanged_diag_sources = get_unchanged_diagnostic_sources( + doc, + &diagnostics, + old_diagnostics, + server_id, + ); } } - 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. @@ -810,7 +793,7 @@ impl Application { current_diagnostics // Sort diagnostics first by severity and then by line numbers. } - Entry::Vacant(v) => v.insert(diagnostics.collect()), + Entry::Vacant(v) => v.insert(diagnostics), }; // Sort diagnostics first by severity and then by line numbers. @@ -1257,3 +1240,30 @@ impl Application { errs } } + +pub fn get_unchanged_diagnostic_sources( + doc: &Document, + diagnostics: &[(lsp::Diagnostic, LanguageServerId)], + old_diagnostics: &[(lsp::Diagnostic, LanguageServerId)], + server_id: LanguageServerId, +) -> Vec { + let mut unchanged_diag_sources = Vec::new(); + let lang_conf = doc.language.clone(); + + if let Some(lang_conf) = &lang_conf { + for source in &lang_conf.persistent_diagnostic_sources { + let new_diagnostics = diagnostics + .iter() + .filter(|d| d.0.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.map(|x| &x.0).eq(old_diagnostics) { + unchanged_diag_sources.push(source.clone()) + } + } + } + + unchanged_diag_sources +} diff --git a/helix-term/src/commands/lsp.rs b/helix-term/src/commands/lsp.rs index 0c05ab2c7170..86bdc40498ae 100644 --- a/helix-term/src/commands/lsp.rs +++ b/helix-term/src/commands/lsp.rs @@ -2,8 +2,8 @@ use futures_util::{stream::FuturesOrdered, FutureExt}; use helix_lsp::{ block_on, lsp::{ - self, CodeAction, CodeActionOrCommand, CodeActionTriggerKind, DiagnosticSeverity, - NumberOrString, + self, CodeAction, CodeActionOrCommand, CodeActionTriggerKind, Diagnostic, + DiagnosticSeverity, NumberOrString, }, util::{diagnostic_to_lsp_diagnostic, lsp_range_to_range, range_to_lsp_range}, Client, LanguageServerId, OffsetEncoding, @@ -26,6 +26,7 @@ use helix_view::{ }; use crate::{ + application::get_unchanged_diagnostic_sources, compositor::{self, Compositor}, job::Callback, ui::{self, overlay::overlaid, FileLocation, Picker, Popup, PromptEvent}, @@ -36,7 +37,7 @@ use std::{ collections::{btree_map::Entry, BTreeMap, HashMap, HashSet}, fmt::{Display, Write}, future::Future, - path::Path, + path::{Path, PathBuf}, }; /// Gets the first language server that is attached to a document which supports a specific feature. @@ -1459,17 +1460,23 @@ pub fn pull_diagnostic_for_current_doc(editor: &Editor, jobs: &mut crate::job::J let server_id = language_server.id(); let parse_diagnostic = |editor: &mut Editor, - path, + path: PathBuf, report: Vec, result_id: Option| { + let uri = helix_core::Uri::try_from(path.clone()).unwrap(); + let mut diagnostics: Vec<(Diagnostic, LanguageServerId)> = + report.into_iter().map(|d| (d, server_id)).collect(); + + let old_diagnostics = editor.diagnostics.get(&uri).cloned(); + if let Some(doc) = editor.document_by_path_mut(&path) { - let diagnostics: Vec = report + let new_diagnostics: Vec = diagnostics .iter() .map(|d| { Document::lsp_diagnostic_to_diagnostic( doc.text(), doc.language_config(), - d, + &d.0, server_id, offset_encoding, ) @@ -1478,13 +1485,25 @@ pub fn pull_diagnostic_for_current_doc(editor: &Editor, jobs: &mut crate::job::J .collect(); doc.previous_diagnostic_id = result_id; - // TODO: Should i get unchanged_sources? - doc.replace_diagnostics(diagnostics, &[], Some(server_id)); + + let mut unchanged_diag_sources = Vec::new(); + if let Some(old_diagnostics) = old_diagnostics { + unchanged_diag_sources = get_unchanged_diagnostic_sources( + doc, + &diagnostics, + &old_diagnostics, + server_id, + ); + } + + doc.replace_diagnostics( + new_diagnostics, + &unchanged_diag_sources, + Some(server_id), + ); } - let uri = helix_core::Uri::try_from(path).unwrap(); // TODO: Maybe share code with application.rs:802 - let mut diagnostics = report.into_iter().map(|d| (d, server_id)).collect(); match editor.diagnostics.entry(uri) { Entry::Occupied(o) => { let current_diagnostics = o.into_mut();