Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-view/src/document.rs')
-rw-r--r--helix-view/src/document.rs242
1 files changed, 99 insertions, 143 deletions
diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs
index e7d83ab0..aaf9aab5 100644
--- a/helix-view/src/document.rs
+++ b/helix-view/src/document.rs
@@ -4,16 +4,15 @@ use arc_swap::ArcSwap;
use futures_util::future::BoxFuture;
use futures_util::FutureExt;
use helix_core::auto_pairs::AutoPairs;
-use helix_core::chars::char_is_word;
use helix_core::command_line::Token;
-use helix_core::diagnostic::DiagnosticProvider;
+use helix_core::diagnostic::Severity;
use helix_core::doc_formatter::TextFormat;
use helix_core::encoding::Encoding;
use helix_core::snippets::{ActiveSnippet, SnippetRenderCtx};
use helix_core::syntax::config::LanguageServerFeature;
use helix_core::text_annotations::{InlineAnnotation, Overlay};
+use helix_core::RopeSlice;
use helix_event::TaskController;
-use helix_lsp::util::lsp_pos_to_pos;
use helix_stdx::faccess::{copy_metadata, readonly};
use helix_vcs::{DiffHandle, DiffProviderRegistry};
use once_cell::sync::OnceCell;
@@ -40,10 +39,11 @@ use helix_core::{
indent::{auto_detect_indent_style, IndentStyle},
line_ending::auto_detect_line_ending,
syntax::{self, config::LanguageConfiguration},
- ChangeSet, Diagnostic, LineEnding, Range, Rope, RopeBuilder, Selection, Syntax, Transaction,
+ ChangeSet, LineEnding, Range, Rope, RopeBuilder, Selection, Syntax, Transaction,
};
use crate::{
+ diagnostic::DiagnosticProvider,
editor::Config,
events::{DocumentDidChange, SelectionDidChange},
expansion,
@@ -1452,45 +1452,7 @@ impl Document {
diff_handle.update_document(self.text.clone(), false);
}
- // map diagnostics over changes too
- changes.update_positions(self.diagnostics.iter_mut().map(|diagnostic| {
- let assoc = if diagnostic.starts_at_word {
- Assoc::BeforeWord
- } else {
- Assoc::After
- };
- (&mut diagnostic.range.start, assoc)
- }));
- changes.update_positions(self.diagnostics.iter_mut().filter_map(|diagnostic| {
- if diagnostic.zero_width {
- // for zero width diagnostics treat the diagnostic as a point
- // rather than a range
- return None;
- }
- let assoc = if diagnostic.ends_at_word {
- Assoc::AfterWord
- } else {
- Assoc::Before
- };
- Some((&mut diagnostic.range.end, assoc))
- }));
- self.diagnostics.retain_mut(|diagnostic| {
- if diagnostic.zero_width {
- diagnostic.range.end = diagnostic.range.start
- } else if diagnostic.range.start >= diagnostic.range.end {
- return false;
- }
- diagnostic.line = self.text.char_to_line(diagnostic.range.start);
- true
- });
-
- self.diagnostics.sort_by_key(|diagnostic| {
- (
- diagnostic.range,
- diagnostic.severity,
- diagnostic.provider.clone(),
- )
- });
+ Diagnostic::apply_changes(&mut self.diagnostics, changes, self.text.slice(..));
// Update the inlay hint annotations' positions, helping ensure they are displayed in the proper place
let apply_inlay_hint_changes = |annotations: &mut Vec<InlineAnnotation>| {
@@ -2022,94 +1984,6 @@ impl Document {
)
}
- pub fn lsp_diagnostic_to_diagnostic(
- text: &Rope,
- language_config: Option<&LanguageConfiguration>,
- diagnostic: &helix_lsp::lsp::Diagnostic,
- provider: DiagnosticProvider,
- offset_encoding: helix_lsp::OffsetEncoding,
- ) -> Option<Diagnostic> {
- use helix_core::diagnostic::{Range, Severity::*};
-
- // TODO: convert inside server
- let start =
- if let Some(start) = lsp_pos_to_pos(text, diagnostic.range.start, offset_encoding) {
- start
- } else {
- log::warn!("lsp position out of bounds - {:?}", diagnostic);
- return None;
- };
-
- let end = if let Some(end) = lsp_pos_to_pos(text, diagnostic.range.end, offset_encoding) {
- end
- } else {
- log::warn!("lsp position out of bounds - {:?}", diagnostic);
- return None;
- };
-
- let severity = diagnostic.severity.and_then(|severity| match severity {
- lsp::DiagnosticSeverity::ERROR => Some(Error),
- lsp::DiagnosticSeverity::WARNING => Some(Warning),
- lsp::DiagnosticSeverity::INFORMATION => Some(Info),
- lsp::DiagnosticSeverity::HINT => Some(Hint),
- severity => {
- log::error!("unrecognized diagnostic severity: {:?}", severity);
- None
- }
- });
-
- if let Some(lang_conf) = language_config {
- if let Some(severity) = severity {
- if severity < lang_conf.diagnostic_severity {
- return None;
- }
- }
- };
- use helix_core::diagnostic::{DiagnosticTag, NumberOrString};
-
- let code = match diagnostic.code.clone() {
- Some(x) => match x {
- lsp::NumberOrString::Number(x) => Some(NumberOrString::Number(x)),
- lsp::NumberOrString::String(x) => Some(NumberOrString::String(x)),
- },
- None => None,
- };
-
- let tags = if let Some(tags) = &diagnostic.tags {
- let new_tags = tags
- .iter()
- .filter_map(|tag| match *tag {
- lsp::DiagnosticTag::DEPRECATED => Some(DiagnosticTag::Deprecated),
- lsp::DiagnosticTag::UNNECESSARY => Some(DiagnosticTag::Unnecessary),
- _ => None,
- })
- .collect();
-
- new_tags
- } else {
- Vec::new()
- };
-
- let ends_at_word =
- start != end && end != 0 && text.get_char(end - 1).is_some_and(char_is_word);
- let starts_at_word = start != end && text.get_char(start).is_some_and(char_is_word);
-
- Some(Diagnostic {
- range: Range { start, end },
- ends_at_word,
- starts_at_word,
- zero_width: start == end,
- line: diagnostic.range.start.line as usize,
- message: diagnostic.message.clone(),
- severity,
- code,
- tags,
- source: diagnostic.source.clone(),
- data: diagnostic.data.clone(),
- provider,
- })
- }
-
#[inline]
pub fn diagnostics(&self) -> &[Diagnostic] {
&self.diagnostics
@@ -2124,17 +1998,17 @@ impl Document {
if unchanged_sources.is_empty() {
if let Some(provider) = provider {
self.diagnostics
- .retain(|diagnostic| &diagnostic.provider != provider);
+ .retain(|diagnostic| &diagnostic.inner.provider != provider);
} else {
self.diagnostics.clear();
}
} else {
self.diagnostics.retain(|d| {
- if provider.is_some_and(|provider| provider != &d.provider) {
+ if provider.is_some_and(|provider| provider != &d.inner.provider) {
return true;
}
- if let Some(source) = &d.source {
+ if let Some(source) = &d.inner.source {
unchanged_sources.contains(source)
} else {
false
@@ -2142,19 +2016,13 @@ impl Document {
});
}
self.diagnostics.extend(diagnostics);
- self.diagnostics.sort_by_key(|diagnostic| {
- (
- diagnostic.range,
- diagnostic.severity,
- diagnostic.provider.clone(),
- )
- });
+ self.diagnostics.sort();
}
- /// clears diagnostics for a given language server id if set, otherwise all diagnostics are cleared
+ /// clears diagnostics for a given language server id
pub fn clear_diagnostics_for_language_server(&mut self, id: LanguageServerId) {
self.diagnostics
- .retain(|d| d.provider.language_server_id() != Some(id));
+ .retain(|d| d.inner.provider.language_server_id() != Some(id));
}
/// Get the document's auto pairs. If the document has a recognized
@@ -2318,6 +2186,94 @@ impl Display for FormatterError {
}
}
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Diagnostic {
+ pub inner: crate::Diagnostic,
+ pub range: helix_stdx::Range,
+ pub line: usize,
+ ends_at_word: bool,
+ starts_at_word: bool,
+ zero_width: bool,
+}
+
+impl Diagnostic {
+ #[inline]
+ pub fn severity(&self) -> Severity {
+ self.inner.severity.unwrap_or(Severity::Warning)
+ }
+
+ fn apply_changes(diagnostics: &mut Vec<Self>, changes: &ChangeSet, text: RopeSlice) {
+ use helix_core::Assoc;
+
+ changes.update_positions(diagnostics.iter_mut().map(|diagnostic| {
+ let assoc = if diagnostic.starts_at_word {
+ Assoc::BeforeWord
+ } else {
+ Assoc::After
+ };
+ (&mut diagnostic.range.start, assoc)
+ }));
+ changes.update_positions(diagnostics.iter_mut().filter_map(|diagnostic| {
+ if diagnostic.zero_width {
+ // for zero width diagnostics treat the diagnostic as a point
+ // rather than a range
+ return None;
+ }
+ let assoc = if diagnostic.ends_at_word {
+ Assoc::AfterWord
+ } else {
+ Assoc::Before
+ };
+ Some((&mut diagnostic.range.end, assoc))
+ }));
+ diagnostics.retain_mut(|diagnostic| {
+ if diagnostic.zero_width {
+ diagnostic.range.end = diagnostic.range.start;
+ } else if diagnostic.range.start >= diagnostic.range.end {
+ return false;
+ }
+ diagnostic.line = text.char_to_line(diagnostic.range.start);
+ true
+ });
+
+ diagnostics.sort();
+ }
+}
+
+impl crate::Diagnostic {
+ pub(crate) fn to_document_diagnostic(&self, text: &Rope) -> Option<Diagnostic> {
+ use helix_core::chars::char_is_word;
+ use helix_lsp::util;
+
+ let (start, end, line) = match self.range {
+ crate::Range::Lsp {
+ range,
+ offset_encoding,
+ } => {
+ let start = util::lsp_pos_to_pos(text, range.start, offset_encoding)?;
+ let end = util::lsp_pos_to_pos(text, range.end, offset_encoding)?;
+ (start, end, range.start.line as usize)
+ }
+ crate::Range::Document(range) => {
+ (range.start, range.end, text.char_to_line(range.start))
+ }
+ };
+
+ let ends_at_word =
+ start != end && end != 0 && text.get_char(end - 1).is_some_and(char_is_word);
+ let starts_at_word = start != end && text.get_char(start).is_some_and(char_is_word);
+
+ Some(Diagnostic {
+ inner: self.clone(),
+ range: helix_stdx::Range { start, end },
+ line,
+ starts_at_word,
+ ends_at_word,
+ zero_width: start == end,
+ })
+ }
+}
+
#[cfg(test)]
mod test {
use arc_swap::ArcSwap;