Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-view/src/editor.rs')
-rw-r--r--helix-view/src/editor.rs149
1 files changed, 114 insertions, 35 deletions
diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs
index 194285a8..d6efd25f 100644
--- a/helix-view/src/editor.rs
+++ b/helix-view/src/editor.rs
@@ -1,10 +1,12 @@
use crate::{
annotations::diagnostics::{DiagnosticFilter, InlineDiagnosticsConfig},
clipboard::ClipboardProvider,
+ diagnostic::DiagnosticProvider,
document::{
- DocumentOpenError, DocumentSavedEventFuture, DocumentSavedEventResult, Mode, SavePoint,
+ self, DocumentOpenError, DocumentSavedEventFuture, DocumentSavedEventResult, Mode,
+ SavePoint,
},
- events::{DocumentDidClose, DocumentDidOpen, DocumentFocusLost},
+ events::{DiagnosticsDidChange, DocumentDidClose, DocumentDidOpen, DocumentFocusLost},
graphics::{CursorKind, Rect},
handlers::Handlers,
info::Info,
@@ -12,7 +14,7 @@ use crate::{
register::Registers,
theme::{self, Theme},
tree::{self, Tree},
- Document, DocumentId, View, ViewId,
+ Diagnostic, Document, DocumentId, View, ViewId,
};
use dap::StackFrame;
use helix_event::dispatch;
@@ -45,7 +47,6 @@ use anyhow::{anyhow, bail, Error};
pub use helix_core::diagnostic::Severity;
use helix_core::{
auto_pairs::AutoPairs,
- diagnostic::DiagnosticProvider,
syntax::{
self,
config::{AutoPairConfig, IndentationHeuristic, LanguageServerFeature, SoftWrap},
@@ -53,7 +54,6 @@ use helix_core::{
Change, LineEnding, Position, Range, Selection, Uri, NATIVE_LINE_ENDING,
};
use helix_dap as dap;
-use helix_lsp::lsp;
use helix_stdx::path::canonicalize;
use serde::{ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer};
@@ -1059,7 +1059,7 @@ pub struct Breakpoint {
use futures_util::stream::{Flatten, Once};
-type Diagnostics = BTreeMap<Uri, Vec<(lsp::Diagnostic, DiagnosticProvider)>>;
+type Diagnostics = BTreeMap<Uri, Vec<Diagnostic>>;
pub struct Editor {
/// Current editing mode.
@@ -1756,7 +1756,7 @@ impl Editor {
}
pub fn new_file_from_stdin(&mut self, action: Action) -> Result<DocumentId, Error> {
- let (stdin, encoding, has_bom) = crate::document::read_to_string(&mut stdin(), None)?;
+ let (stdin, encoding, has_bom) = document::read_to_string(&mut stdin(), None)?;
let doc = Document::from(
helix_core::Rope::default(),
Some((encoding, has_bom)),
@@ -2045,8 +2045,8 @@ impl Editor {
language_servers: &'a helix_lsp::Registry,
diagnostics: &'a Diagnostics,
document: &Document,
- ) -> impl Iterator<Item = helix_core::Diagnostic> + 'a {
- Editor::doc_diagnostics_with_filter(language_servers, diagnostics, document, |_, _| true)
+ ) -> impl Iterator<Item = document::Diagnostic> + 'a {
+ Editor::doc_diagnostics_with_filter(language_servers, diagnostics, document, |_| true)
}
/// Returns all supported diagnostics for the document
@@ -2055,37 +2055,38 @@ impl Editor {
language_servers: &'a helix_lsp::Registry,
diagnostics: &'a Diagnostics,
document: &Document,
- filter: impl Fn(&lsp::Diagnostic, &DiagnosticProvider) -> bool + 'a,
- ) -> impl Iterator<Item = helix_core::Diagnostic> + 'a {
+ filter: impl Fn(&Diagnostic) -> bool + 'a,
+ ) -> impl Iterator<Item = document::Diagnostic> + 'a {
let text = document.text().clone();
let language_config = document.language.clone();
diagnostics
.get(&document.uri())
.map(|diags| {
- diags.iter().filter_map(move |(diagnostic, provider)| {
- let server_id = provider.language_server_id()?;
- let ls = language_servers.get_by_id(server_id)?;
- language_config
- .as_ref()
- .and_then(|c| {
- c.language_servers.iter().find(|features| {
- features.name == ls.name()
- && features.has_feature(LanguageServerFeature::Diagnostics)
- })
- })
- .and_then(|_| {
- if filter(diagnostic, provider) {
- Document::lsp_diagnostic_to_diagnostic(
- &text,
- language_config.as_deref(),
- diagnostic,
- provider.clone(),
- ls.offset_encoding(),
- )
- } else {
- None
- }
- })
+ diags.iter().filter_map(move |diagnostic| {
+ let language_server = diagnostic
+ .provider
+ .language_server_id()
+ .and_then(|id| language_servers.get_by_id(id));
+
+ if let Some((config, server)) = language_config.as_ref().zip(language_server) {
+ config.language_servers.iter().find(|features| {
+ features.name == server.name()
+ && features.has_feature(LanguageServerFeature::Diagnostics)
+ })?;
+ }
+ if diagnostic.severity.is_some_and(|severity| {
+ language_config
+ .as_ref()
+ .is_some_and(|config| severity < config.diagnostic_severity)
+ }) {
+ return None;
+ }
+
+ if filter(diagnostic) {
+ diagnostic.to_document_diagnostic(&text)
+ } else {
+ None
+ }
})
})
.into_iter()
@@ -2266,6 +2267,84 @@ impl Editor {
pub fn get_last_cwd(&mut self) -> Option<&Path> {
self.last_cwd.as_deref()
}
+
+ pub fn handle_diagnostics(
+ &mut self,
+ provider: &DiagnosticProvider,
+ uri: Uri,
+ version: Option<i32>,
+ mut diagnostics: Vec<Diagnostic>,
+ ) {
+ use std::collections::btree_map::Entry;
+
+ let doc = self.documents.values_mut().find(|doc| doc.uri() == uri);
+
+ if let Some((version, doc)) = version.zip(doc.as_ref()) {
+ if version != doc.version() {
+ log::info!("Version ({version}) is out of date for {uri:?} (expected ({})), dropping diagnostics", doc.version());
+ return;
+ }
+ }
+
+ let mut unchanged_diag_sources = Vec::new();
+ if let Some((lang_conf, old_diagnostics)) = doc
+ .as_ref()
+ .and_then(|doc| Some((doc.language_config()?, self.diagnostics.get(&uri)?)))
+ {
+ if !lang_conf.persistent_diagnostic_sources.is_empty() {
+ diagnostics.sort();
+ }
+ for source in &lang_conf.persistent_diagnostic_sources {
+ let new_diagnostics = diagnostics
+ .iter()
+ .filter(|d| d.source.as_ref() == Some(source));
+ let old_diagnostics = old_diagnostics
+ .iter()
+ .filter(|d| &d.provider == provider && d.source.as_ref() == Some(source));
+ if new_diagnostics.eq(old_diagnostics) {
+ unchanged_diag_sources.push(source.clone())
+ }
+ }
+ }
+
+ // Insert the original lsp::Diagnostics here because we may have no open document
+ // for diagnostic message and so we can't calculate the exact position.
+ // When using them later in the diagnostics picker, we calculate them on-demand.
+ let diagnostics = match self.diagnostics.entry(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(|diagnostic| &diagnostic.provider != provider);
+ current_diagnostics.extend(diagnostics);
+ current_diagnostics
+ // Sort diagnostics first by severity and then by line numbers.
+ }
+ Entry::Vacant(v) => v.insert(diagnostics),
+ };
+
+ diagnostics.sort();
+
+ if let Some(doc) = doc {
+ let diagnostic_of_language_server_and_not_in_unchanged_sources =
+ |diagnostic: &crate::Diagnostic| {
+ &diagnostic.provider == provider
+ && diagnostic
+ .source
+ .as_ref()
+ .map_or(true, |source| !unchanged_diag_sources.contains(source))
+ };
+ let diagnostics = Self::doc_diagnostics_with_filter(
+ &self.language_servers,
+ &self.diagnostics,
+ doc,
+ diagnostic_of_language_server_and_not_in_unchanged_sources,
+ );
+ doc.replace_diagnostics(diagnostics, &unchanged_diag_sources, Some(provider));
+
+ let doc = doc.id();
+ helix_event::dispatch(DiagnosticsDidChange { editor: self, doc });
+ }
+ }
}
fn try_restore_indent(doc: &mut Document, view: &mut View) {