From e01567eddd70b0d1cac79f3b8de292eef2ceab6b Mon Sep 17 00:00:00 2001 From: Philipp Mildenberger Date: Mon, 20 Nov 2023 23:32:46 +0100 Subject: [PATCH] Initialize diagnostics when opening a document --- helix-term/src/application.rs | 109 ++++----------------------------- helix-term/src/ui/picker.rs | 5 +- helix-view/src/document.rs | 111 ++++++++++++++++++++++++++++++++-- helix-view/src/editor.rs | 7 +-- 4 files changed, 120 insertions(+), 112 deletions(-) diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index ed085749b6d3d..7da1e5b5e00fd 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -1,13 +1,8 @@ use arc_swap::{access::Map, ArcSwap}; use futures_util::Stream; -use helix_core::{ - diagnostic::{DiagnosticTag, NumberOrString}, - path::get_relative_path, - pos_at_coords, syntax, Selection, -}; +use helix_core::{path::get_relative_path, pos_at_coords, syntax, Selection}; use helix_lsp::{ lsp::{self, notification::Notification}, - util::lsp_pos_to_pos, LspProgressMap, }; use helix_view::{ @@ -743,99 +738,15 @@ impl Application { }); if let Some(doc) = doc { - let lang_conf = doc.language_config(); - let text = doc.text(); - let diagnostics = params .diagnostics .iter() .filter_map(|diagnostic| { - use helix_core::diagnostic::{Diagnostic, Range, Severity::*}; - use lsp::DiagnosticSeverity; - - // TODO: convert inside server - let start = if let Some(start) = lsp_pos_to_pos( - text, - diagnostic.range.start, + doc.lsp_diagnostic_to_diagnostic( + diagnostic, + server_id, 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.map(|severity| match severity { - DiagnosticSeverity::ERROR => Error, - DiagnosticSeverity::WARNING => Warning, - DiagnosticSeverity::INFORMATION => Info, - DiagnosticSeverity::HINT => Hint, - severity => unreachable!( - "unrecognized diagnostic severity: {:?}", - severity - ), - }); - - if let Some(lang_conf) = lang_conf { - if let Some(severity) = severity { - if severity < lang_conf.diagnostic_severity { - return None; - } - } - }; - - 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() - }; - - Some(Diagnostic { - range: Range { start, end }, - line: diagnostic.range.start.line as usize, - message: diagnostic.message.clone(), - severity, - code, - tags, - source: diagnostic.source.clone(), - data: diagnostic.data.clone(), - language_server_id: server_id, - }) + ) }) .collect(); @@ -859,12 +770,14 @@ impl Application { 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 - .sort_unstable_by_key(|(d, _)| (d.severity, d.range.start)); + current_diagnostics.sort_by_key(|(d, lsp_id)| { + (d.severity, d.range.start, *lsp_id) + }); } Entry::Vacant(v) => { - diagnostics - .sort_unstable_by_key(|(d, _)| (d.severity, d.range.start)); + diagnostics.sort_by_key(|(d, lsp_id)| { + (d.severity, d.range.start, *lsp_id) + }); v.insert(diagnostics); } }; diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index 9ba45335777fd..45b42a2bac958 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -427,7 +427,7 @@ impl Picker { (size, _) if size > MAX_FILE_SIZE_FOR_PREVIEW => { CachedPreview::LargeFile } - _ => Document::open(path, None, None, editor.config.clone()) + _ => Document::open(path, None, None, editor) .map(|doc| CachedPreview::Document(Box::new(doc))) .unwrap_or(CachedPreview::NotFound), }, @@ -480,8 +480,7 @@ impl Picker { .find::>>() .map(|overlay| &mut overlay.content.file_picker), }; - let Some(picker) = picker - else { + let Some(picker) = picker else { log::info!("picker closed before syntax highlighting finished"); return; }; diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index bb61eaa6aae68..dda89fa1ead5c 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -8,6 +8,7 @@ use helix_core::doc_formatter::TextFormat; use helix_core::encoding::Encoding; use helix_core::syntax::{Highlight, LanguageServerFeature}; use helix_core::text_annotations::{InlineAnnotation, TextAnnotations}; +use helix_lsp::util::lsp_pos_to_pos; use helix_vcs::{DiffHandle, DiffProviderRegistry}; use ::parking_lot::Mutex; @@ -692,7 +693,7 @@ impl Document { path: &Path, encoding: Option<&'static Encoding>, config_loader: Option>, - config: Arc>, + editor: &Editor, ) -> Result { // Open the file if it exists, otherwise assume it is a new file (and thus empty). let (rope, encoding, has_bom) = if path.exists() { @@ -700,12 +701,12 @@ impl Document { std::fs::File::open(path).context(format!("unable to open {:?}", path))?; from_reader(&mut file, encoding)? } else { - let line_ending: LineEnding = config.load().default_line_ending.into(); + let line_ending: LineEnding = editor.config.load().default_line_ending.into(); let encoding = encoding.unwrap_or(encoding::UTF_8); (Rope::from(line_ending.as_str()), encoding, false) }; - let mut doc = Self::from(rope, Some((encoding, has_bom)), config); + let mut doc = Self::from(rope, Some((encoding, has_bom)), editor.config.clone()); // set the path and try detecting the language doc.set_path(Some(path)); @@ -715,6 +716,24 @@ impl Document { doc.detect_indent_and_line_ending(); + let diagnostics = Url::from_file_path(path) // TODO log error? + .ok() + .and_then(|uri| editor.diagnostics.get(&uri)) + .map(|diags| { + diags + .iter() + .filter_map(|(diagnostic, lsp_id)| { + let offset_encoding = + editor.language_server_by_id(*lsp_id)?.offset_encoding(); + doc.lsp_diagnostic_to_diagnostic(diagnostic, *lsp_id, offset_encoding) + }) + .collect::>() + }); + + if let Some(diagnostics) = diagnostics { + doc.diagnostics = diagnostics; + } + Ok(doc) } @@ -1692,6 +1711,86 @@ impl Document { ) } + pub fn lsp_diagnostic_to_diagnostic( + &self, + diagnostic: &helix_lsp::lsp::Diagnostic, + language_server_id: usize, + offset_encoding: helix_lsp::OffsetEncoding, + ) -> Option { + use helix_core::diagnostic::{Range, Severity::*}; + + let lang_conf = self.language_config(); + let text = self.text(); + + // 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.map(|severity| match severity { + lsp::DiagnosticSeverity::ERROR => Error, + lsp::DiagnosticSeverity::WARNING => Warning, + lsp::DiagnosticSeverity::INFORMATION => Info, + lsp::DiagnosticSeverity::HINT => Hint, + severity => unreachable!("unrecognized diagnostic severity: {:?}", severity), + }); + + if let Some(lang_conf) = lang_conf { + 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() + }; + + Some(Diagnostic { + range: Range { start, end }, + line: diagnostic.range.start.line as usize, + message: diagnostic.message.clone(), + severity, + code, + tags, + source: diagnostic.source.clone(), + data: diagnostic.data.clone(), + language_server_id, + }) + } + #[inline] pub fn diagnostics(&self) -> &[Diagnostic] { &self.diagnostics @@ -1699,8 +1798,10 @@ impl Document { pub fn shown_diagnostics(&self) -> impl Iterator + DoubleEndedIterator { self.diagnostics.iter().filter(|d| { - self.language_servers_with_feature(LanguageServerFeature::Diagnostics) - .any(|ls| ls.id() == d.language_server_id) + self.language_servers.len() == 0 + || self + .language_servers_with_feature(LanguageServerFeature::Diagnostics) + .any(|ls| ls.id() == d.language_server_id) }) } diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index f2e853071f516..048b2bf11f994 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -1449,12 +1449,7 @@ impl Editor { let id = if let Some(id) = id { id } else { - let mut doc = Document::open( - &path, - None, - Some(self.syn_loader.clone()), - self.config.clone(), - )?; + let mut doc = Document::open(&path, None, Some(self.syn_loader.clone()), self)?; if let Some(diff_base) = self.diff_providers.get_diff_base(&path) { doc.set_diff_base(diff_base);