@@ -717,7 +717,7 @@ impl Application {
717717 ));
718718 }
719719 }
720− Notification::PublishDiagnostics(params) => {
720+ Notification::PublishDiagnostics(mut params) => {
721721 let path = match params.uri.to_file_path() {
722722 Ok(path) => path,
723723 Err(_) => {
@@ -731,31 +731,69 @@ impl Application {
731731 return;
732732 }
733733 let offset_encoding = language_server.offset_encoding();
734− let doc = self.editor.document_by_path_mut(&path).filter(|doc| {
735− if let Some(version) = params.version {
736− if version != doc.version() {
737− log::info!("Version ({version}) is out of date for {path:?} (expected ({}), dropping PublishDiagnostic notification", doc.version());
738− return false;
734+ // have to inline the function because of borrow checking...
735+ let doc = self.editor.documents.values_mut()
736+ .find(|doc| doc.path().map(|p| p == &path).unwrap_or(false))
737+ .filter(|doc| {
738+ if let Some(version) = params.version {
739+ if version != doc.version() {
740+ log::info!("Version ({version}) is out of date for {path:?} (expected ({}), dropping PublishDiagnostic notification", doc.version());
741+ return false;
742+ }
739743 }
740− }
741−
742− true
743− });
744+ true
745+ });
744746
745747 if let Some(doc) = doc {
746− let lang_conf = doc.language_config();
747− let text = doc.text();
748+ let lang_conf = doc.language.clone();
749+ let text = doc.text().clone();
750+
751+ let mut unchaged_diag_sources_ = Vec::new();
752+ if let Some(lang_conf) = &lang_conf {
753+ if let Some(old_diagnostics) =
754+ self.editor.diagnostics.get(¶ms.uri)
755+ {
756+ if !lang_conf.persistent_diagnostic_sources.is_empty() {
757+ // Sort diagnostics first by severity and then by line numbers.
758+ // Note: The `lsp::DiagnosticSeverity` enum is already defined in decreasing order
759+ params
760+ .diagnostics
761+ .sort_unstable_by_key(|d| (d.severity, d.range.start));
762+ }
763+ for source in &lang_conf.persistent_diagnostic_sources {
764+ let new_diagnostics = params
765+ .diagnostics
766+ .iter()
767+ .filter(|d| d.source.as_ref() == Some(source));
768+ let old_diagnostics = old_diagnostics
769+ .iter()
770+ .filter(|(d, d_server)| {
771+ *d_server == server_id
772+ && d.source.as_ref() == Some(source)
773+ })
774+ .map(|(d, _)| d);
775+ if new_diagnostics.eq(old_diagnostics) {
776+ unchaged_diag_sources_.push(source.clone())
777+ }
778+ }
779+ }
780+ }
748781
749− let diagnostics = params
750− .diagnostics
751− .iter()
752− .filter_map(|diagnostic| {
782+ let unchaged_diag_sources = &unchaged_diag_sources_;
783+ let diagnostics =
784+ params.diagnostics.iter().filter_map(move |diagnostic| {
753785 use helix_core::diagnostic::{Diagnostic, Range, Severity::*};
754786 use lsp::DiagnosticSeverity;
755787
788+ if diagnostic.source.as_ref().map_or(false, |source| {
789+ unchaged_diag_sources.contains(source)
790+ }) {
791+ return None;
792+ }
793+
756794 // TODO: convert inside server
757795 let start = if let Some(start) = lsp_pos_to_pos(
758− text,
796+ &text,
759797 diagnostic.range.start,
760798 offset_encoding,
761799 ) {
@@ -766,7 +804,7 @@ impl Application {
766804 };
767805
768806 let end = if let Some(end) =
769− lsp_pos_to_pos(text, diagnostic.range.end, offset_encoding)
807+ lsp_pos_to_pos(&text, diagnostic.range.end, offset_encoding)
770808 {
771809 end
772810 } else {
@@ -786,7 +824,7 @@ impl Application {
786824 ),
787825 });
788826
789− if let Some(lang_conf) = lang_conf {
827+ if let Some(lang_conf) = &lang_conf {
790828 if let Some(severity) = severity {
791829 if severity < lang_conf.diagnostic_severity {
792830 return None;
@@ -836,38 +874,31 @@ impl Application {
836874 data: diagnostic.data.clone(),
837875 language_server_id: server_id,
838876 })
839− })
840− .collect();
877+ });
841878
842− doc.replace_diagnostics(diagnostics, server_id);
879+ doc.replace_diagnostics(diagnostics, unchaged_diag_sources, server_id);
843880 }
844881
845− let mut diagnostics = params
846− .diagnostics
847− .into_iter()
848− .map(|d| (d, server_id))
849− .collect();
882+ let diagnostics = params.diagnostics.into_iter().map(|d| (d, server_id));
850883
851884 // Insert the original lsp::Diagnostics here because we may have no open document
852885 // for diagnosic message and so we can't calculate the exact position.
853886 // When using them later in the diagnostics picker, we calculate them on-demand.
854− match self.editor.diagnostics.entry(params.uri) {
887+ let diagnostics = match self.editor.diagnostics.entry(params.uri) {
855888 Entry::Occupied(o) => {
856889 let current_diagnostics = o.into_mut();
857890 // there may entries of other language servers, which is why we can't overwrite the whole entry
858891 current_diagnostics.retain(|(_, lsp_id)| *lsp_id != server_id);
859− current_diagnostics.append(&mut diagnostics);
860− // Sort diagnostics first by severity and then by line numbers.
861− // Note: The `lsp::DiagnosticSeverity` enum is already defined in decreasing order
892+ current_diagnostics.extend(diagnostics);
862893 current_diagnostics
863− .sort_unstable_by_key(|(d, _)| (d.severity, d.range.start));
864− }
865− Entry::Vacant(v) => {
866− diagnostics
867− .sort_unstable_by_key(|(d, _)| (d.severity, d.range.start));
868− v.insert(diagnostics);
894+ // Sort diagnostics first by severity and then by line numbers.
869895 }
896+ Entry::Vacant(v) => v.insert(diagnostics.collect()),
870897 };
898+
899+ // Sort diagnostics first by severity and then by line numbers.
900+ // Note: The `lsp::DiagnosticSeverity` enum is already defined in decreasing order
901+ diagnostics.sort_unstable_by_key(|(d, _)| (d.severity, d.range.start));
871902 }
872903 Notification::ShowMessage(params) => {
873904 log::warn!("unhandled window/showMessage: {:?}", params);