From 508b9dc0241bec2ba2c3b29869675757d9b7b198 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 4 Jan 2022 16:11:29 +0100 Subject: [PATCH] Rip out "diagnostic providers" --- crates/diagnostics/src/diagnostics.rs | 28 +-- crates/editor/src/editor.rs | 19 +- crates/editor/src/items.rs | 6 +- crates/editor/src/multi_buffer.rs | 5 +- crates/language/src/buffer.rs | 77 +++----- crates/language/src/diagnostic_set.rs | 17 +- crates/language/src/language.rs | 22 +-- crates/language/src/proto.rs | 134 ++++++-------- crates/language/src/tests.rs | 146 +++++++-------- crates/project/src/project.rs | 37 +--- crates/project/src/worktree.rs | 248 +++++++++----------------- crates/rpc/proto/zed.proto | 13 +- crates/rpc/src/peer.rs | 8 +- crates/server/src/rpc.rs | 2 +- crates/workspace/src/workspace.rs | 14 +- crates/zed/src/language.rs | 179 ------------------- 16 files changed, 267 insertions(+), 688 deletions(-) diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 87d9c521ab..f523b4ca7f 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -571,7 +571,7 @@ mod tests { use super::*; use client::{http::ServerResponse, test::FakeHttpClient, Client, UserStore}; use gpui::TestAppContext; - use language::{Diagnostic, DiagnosticEntry, DiagnosticSeverity, LanguageRegistry}; + use language::{Diagnostic, DiagnosticEntry, DiagnosticSeverity, LanguageRegistry, PointUtf16}; use project::FakeFs; use serde_json::json; use std::sync::Arc; @@ -629,11 +629,12 @@ mod tests { worktree.update(&mut cx, |worktree, cx| { worktree - .update_diagnostics_from_provider( + .update_diagnostic_entries( Arc::from("/test/main.rs".as_ref()), + None, vec![ DiagnosticEntry { - range: 20..21, + range: PointUtf16::new(1, 8)..PointUtf16::new(1, 9), diagnostic: Diagnostic { message: "move occurs because `x` has type `Vec`, which does not implement the `Copy` trait" @@ -646,7 +647,7 @@ mod tests { }, }, DiagnosticEntry { - range: 40..41, + range: PointUtf16::new(2, 8)..PointUtf16::new(2, 9), diagnostic: Diagnostic { message: "move occurs because `y` has type `Vec`, which does not implement the `Copy` trait" @@ -659,7 +660,7 @@ mod tests { }, }, DiagnosticEntry { - range: 58..59, + range: PointUtf16::new(3, 6)..PointUtf16::new(3, 7), diagnostic: Diagnostic { message: "value moved here".to_string(), severity: DiagnosticSeverity::INFORMATION, @@ -670,7 +671,7 @@ mod tests { }, }, DiagnosticEntry { - range: 68..69, + range: PointUtf16::new(4, 6)..PointUtf16::new(4, 7), diagnostic: Diagnostic { message: "value moved here".to_string(), severity: DiagnosticSeverity::INFORMATION, @@ -681,7 +682,7 @@ mod tests { }, }, DiagnosticEntry { - range: 112..113, + range: PointUtf16::new(7, 6)..PointUtf16::new(7, 7), diagnostic: Diagnostic { message: "use of moved value".to_string(), severity: DiagnosticSeverity::ERROR, @@ -692,7 +693,7 @@ mod tests { }, }, DiagnosticEntry { - range: 112..113, + range: PointUtf16::new(7, 6)..PointUtf16::new(7, 7), diagnostic: Diagnostic { message: "value used here after move".to_string(), severity: DiagnosticSeverity::INFORMATION, @@ -703,7 +704,7 @@ mod tests { }, }, DiagnosticEntry { - range: 122..123, + range: PointUtf16::new(8, 6)..PointUtf16::new(8, 7), diagnostic: Diagnostic { message: "use of moved value".to_string(), severity: DiagnosticSeverity::ERROR, @@ -714,7 +715,7 @@ mod tests { }, }, DiagnosticEntry { - range: 122..123, + range: PointUtf16::new(8, 6)..PointUtf16::new(8, 7), diagnostic: Diagnostic { message: "value used here after move".to_string(), severity: DiagnosticSeverity::INFORMATION, @@ -782,11 +783,12 @@ mod tests { worktree.update(&mut cx, |worktree, cx| { worktree - .update_diagnostics_from_provider( + .update_diagnostic_entries( Arc::from("/test/a.rs".as_ref()), + None, vec![ DiagnosticEntry { - range: 15..15, + range: PointUtf16::new(0, 15)..PointUtf16::new(0, 15), diagnostic: Diagnostic { message: "mismatched types".to_string(), severity: DiagnosticSeverity::ERROR, @@ -797,7 +799,7 @@ mod tests { }, }, DiagnosticEntry { - range: 15..15, + range: PointUtf16::new(0, 15)..PointUtf16::new(0, 15), diagnostic: Diagnostic { message: "expected `usize`, found `char`".to_string(), severity: DiagnosticSeverity::INFORMATION, diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index d7e3a14500..f418efd8d0 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -2857,19 +2857,19 @@ impl Editor { loop { let next_group = buffer .diagnostics_in_range::<_, usize>(search_start..buffer.len()) - .find_map(|(provider_name, entry)| { + .find_map(|entry| { if entry.diagnostic.is_primary && !entry.range.is_empty() && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end()) { - Some((provider_name, entry.range, entry.diagnostic.group_id)) + Some((entry.range, entry.diagnostic.group_id)) } else { None } }); - if let Some((provider_name, primary_range, group_id)) = next_group { - self.activate_diagnostics(provider_name, group_id, cx); + if let Some((primary_range, group_id)) = next_group { + self.activate_diagnostics(group_id, cx); self.update_selections( vec![Selection { id: selection.id, @@ -2897,7 +2897,7 @@ impl Editor { let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer); let is_valid = buffer .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone()) - .any(|(_, entry)| { + .any(|entry| { entry.diagnostic.is_primary && !entry.range.is_empty() && entry.range.start == primary_range_start @@ -2923,12 +2923,7 @@ impl Editor { } } - fn activate_diagnostics( - &mut self, - provider_name: &str, - group_id: usize, - cx: &mut ViewContext, - ) { + fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext) { self.dismiss_diagnostics(cx); self.active_diagnostics = self.display_map.update(cx, |display_map, cx| { let buffer = self.buffer.read(cx).snapshot(cx); @@ -2937,7 +2932,7 @@ impl Editor { let mut primary_message = None; let mut group_end = Point::zero(); let diagnostic_group = buffer - .diagnostic_group::(provider_name, group_id) + .diagnostic_group::(group_id) .map(|entry| { if entry.range.end > group_end { group_end = entry.range.end; diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index df819ec520..d88315fff7 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -298,9 +298,9 @@ impl DiagnosticMessage { let new_diagnostic = buffer .read(cx) .diagnostics_in_range::<_, usize>(cursor_position..cursor_position) - .filter(|(_, entry)| !entry.range.is_empty()) - .min_by_key(|(_, entry)| (entry.diagnostic.severity, entry.range.len())) - .map(|(_, entry)| entry.diagnostic); + .filter(|entry| !entry.range.is_empty()) + .min_by_key(|entry| (entry.diagnostic.severity, entry.range.len())) + .map(|entry| entry.diagnostic); if new_diagnostic != self.diagnostic { self.diagnostic = new_diagnostic; cx.notify(); diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index c875ce29a6..e96b8200f7 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -1487,7 +1487,6 @@ impl MultiBufferSnapshot { pub fn diagnostic_group<'a, O>( &'a self, - provider_name: &'a str, group_id: usize, ) -> impl Iterator> + 'a where @@ -1495,13 +1494,13 @@ impl MultiBufferSnapshot { { self.as_singleton() .into_iter() - .flat_map(move |buffer| buffer.diagnostic_group(provider_name, group_id)) + .flat_map(move |buffer| buffer.diagnostic_group(group_id)) } pub fn diagnostics_in_range<'a, T, O>( &'a self, range: Range, - ) -> impl Iterator)> + 'a + ) -> impl Iterator> + 'a where T: 'a + ToOffset, O: 'a + text::FromAnchor, diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 0c8d7ae574..a23631a577 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -66,7 +66,7 @@ pub struct Buffer { parsing_in_background: bool, parse_count: usize, remote_selections: TreeMap]>>, - diagnostic_sets: Vec, + diagnostics: DiagnosticSet, diagnostics_update_count: usize, language_server: Option, deferred_ops: OperationQueue, @@ -77,7 +77,7 @@ pub struct Buffer { pub struct BufferSnapshot { text: text::BufferSnapshot, tree: Option, - diagnostic_sets: Vec, + diagnostics: DiagnosticSet, remote_selections: TreeMap]>>, diagnostics_update_count: usize, is_parsing: bool, @@ -121,7 +121,6 @@ struct LanguageServerSnapshot { pub enum Operation { Buffer(text::Operation), UpdateDiagnostics { - provider_name: String, diagnostics: Arc<[DiagnosticEntry]>, lamport_timestamp: clock::Lamport, }, @@ -306,17 +305,11 @@ impl Buffer { ); } let snapshot = this.snapshot(); - for diagnostic_set in message.diagnostic_sets { - let (provider_name, entries) = proto::deserialize_diagnostic_set(diagnostic_set); - this.apply_diagnostic_update( - DiagnosticSet::from_sorted_entries( - provider_name, - entries.into_iter().cloned(), - &snapshot, - ), - cx, - ); - } + let entries = proto::deserialize_diagnostics(message.diagnostics); + this.apply_diagnostic_update( + DiagnosticSet::from_sorted_entries(entries.into_iter().cloned(), &snapshot), + cx, + ); Ok(this) } @@ -338,13 +331,7 @@ impl Buffer { selections: proto::serialize_selections(selections), }) .collect(), - diagnostic_sets: self - .diagnostic_sets - .iter() - .map(|set| { - proto::serialize_diagnostic_set(set.provider_name().to_string(), set.iter()) - }) - .collect(), + diagnostics: proto::serialize_diagnostics(self.diagnostics.iter()), } } @@ -379,7 +366,7 @@ impl Buffer { pending_autoindent: Default::default(), language: None, remote_selections: Default::default(), - diagnostic_sets: Default::default(), + diagnostics: Default::default(), diagnostics_update_count: 0, language_server: None, deferred_ops: OperationQueue::new(), @@ -393,7 +380,7 @@ impl Buffer { text: self.text.snapshot(), tree: self.syntax_tree(), remote_selections: self.remote_selections.clone(), - diagnostic_sets: self.diagnostic_sets.clone(), + diagnostics: self.diagnostics.clone(), diagnostics_update_count: self.diagnostics_update_count, is_parsing: self.parsing_in_background, language: self.language.clone(), @@ -743,7 +730,6 @@ impl Buffer { pub fn update_diagnostics( &mut self, - provider_name: Arc, version: Option, mut diagnostics: Vec>, cx: &mut ModelContext, @@ -833,10 +819,9 @@ impl Buffer { } drop(edits_since_save); - let set = DiagnosticSet::new(provider_name, sanitized_diagnostics, content); + let set = DiagnosticSet::new(sanitized_diagnostics, content); self.apply_diagnostic_update(set.clone(), cx); Ok(Operation::UpdateDiagnostics { - provider_name: set.provider_name().to_string(), diagnostics: set.iter().cloned().collect(), lamport_timestamp: self.text.lamport_clock.tick(), }) @@ -1347,17 +1332,12 @@ impl Buffer { unreachable!("buffer operations should never be applied at this layer") } Operation::UpdateDiagnostics { - provider_name, diagnostics: diagnostic_set, .. } => { let snapshot = self.snapshot(); self.apply_diagnostic_update( - DiagnosticSet::from_sorted_entries( - provider_name, - diagnostic_set.iter().cloned(), - &snapshot, - ), + DiagnosticSet::from_sorted_entries(diagnostic_set.iter().cloned(), &snapshot), cx, ); } @@ -1379,15 +1359,8 @@ impl Buffer { } } - fn apply_diagnostic_update(&mut self, set: DiagnosticSet, cx: &mut ModelContext) { - match self - .diagnostic_sets - .binary_search_by_key(&set.provider_name(), |set| set.provider_name()) - { - Ok(ix) => self.diagnostic_sets[ix] = set.clone(), - Err(ix) => self.diagnostic_sets.insert(ix, set.clone()), - } - + fn apply_diagnostic_update(&mut self, diagnostics: DiagnosticSet, cx: &mut ModelContext) { + self.diagnostics = diagnostics; self.diagnostics_update_count += 1; cx.notify(); cx.emit(Event::DiagnosticsUpdated); @@ -1625,7 +1598,7 @@ impl BufferSnapshot { let mut highlights = None; let mut diagnostic_endpoints = Vec::::new(); if let Some(theme) = theme { - for (_, entry) in self.diagnostics_in_range::<_, usize>(range.clone()) { + for entry in self.diagnostics_in_range::<_, usize>(range.clone()) { diagnostic_endpoints.push(DiagnosticEndpoint { offset: entry.range.start, is_start: true, @@ -1756,38 +1729,28 @@ impl BufferSnapshot { pub fn diagnostics_in_range<'a, T, O>( &'a self, search_range: Range, - ) -> impl 'a + Iterator)> + ) -> impl 'a + Iterator> where T: 'a + Clone + ToOffset, O: 'a + FromAnchor, { - self.diagnostic_sets.iter().flat_map(move |set| { - set.range(search_range.clone(), self, true) - .map(|e| (set.provider_name(), e)) - }) + self.diagnostics.range(search_range.clone(), self, true) } pub fn diagnostic_groups(&self) -> Vec> { let mut groups = Vec::new(); - for set in &self.diagnostic_sets { - set.groups(&mut groups, self); - } + self.diagnostics.groups(&mut groups, self); groups } pub fn diagnostic_group<'a, O>( &'a self, - provider_name: &str, group_id: usize, ) -> impl 'a + Iterator> where O: 'a + FromAnchor, { - self.diagnostic_sets - .iter() - .find(|s| s.provider_name() == provider_name) - .into_iter() - .flat_map(move |s| s.group(group_id, self)) + self.diagnostics.group(group_id, self) } pub fn diagnostics_update_count(&self) -> usize { @@ -1805,7 +1768,7 @@ impl Clone for BufferSnapshot { text: self.text.clone(), tree: self.tree.clone(), remote_selections: self.remote_selections.clone(), - diagnostic_sets: self.diagnostic_sets.clone(), + diagnostics: self.diagnostics.clone(), diagnostics_update_count: self.diagnostics_update_count, is_parsing: self.is_parsing, language: self.language.clone(), diff --git a/crates/language/src/diagnostic_set.rs b/crates/language/src/diagnostic_set.rs index 05e19e635a..047513fce7 100644 --- a/crates/language/src/diagnostic_set.rs +++ b/crates/language/src/diagnostic_set.rs @@ -4,14 +4,12 @@ use std::{ cmp::{Ordering, Reverse}, iter, ops::Range, - sync::Arc, }; use sum_tree::{self, Bias, SumTree}; use text::{Anchor, FromAnchor, Point, ToOffset}; #[derive(Clone, Debug)] pub struct DiagnosticSet { - provider_name: Arc, diagnostics: SumTree>, } @@ -36,32 +34,22 @@ pub struct Summary { } impl DiagnosticSet { - pub fn provider_name(&self) -> &str { - &self.provider_name - } - - pub fn from_sorted_entries( - provider_name: impl Into>, - iter: I, - buffer: &text::BufferSnapshot, - ) -> Self + pub fn from_sorted_entries(iter: I, buffer: &text::BufferSnapshot) -> Self where I: IntoIterator>, { Self { - provider_name: provider_name.into(), diagnostics: SumTree::from_iter(iter, buffer), } } - pub fn new(provider_name: Arc, iter: I, buffer: &text::BufferSnapshot) -> Self + pub fn new(iter: I, buffer: &text::BufferSnapshot) -> Self where I: IntoIterator>, { let mut entries = iter.into_iter().collect::>(); entries.sort_unstable_by_key(|entry| (entry.range.start, Reverse(entry.range.end))); Self { - provider_name, diagnostics: SumTree::from_iter( entries.into_iter().map(|entry| DiagnosticEntry { range: buffer.anchor_before(entry.range.start) @@ -159,7 +147,6 @@ impl DiagnosticSet { impl Default for DiagnosticSet { fn default() -> Self { Self { - provider_name: "".into(), diagnostics: Default::default(), } } diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index d6c13a7fd4..fe832929a9 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -6,10 +6,9 @@ pub mod proto; mod tests; use anyhow::{anyhow, Result}; -use async_trait::async_trait; pub use buffer::Operation; pub use buffer::*; -use collections::{HashMap, HashSet}; +use collections::HashSet; pub use diagnostic_set::DiagnosticEntry; use gpui::AppContext; use highlight_map::HighlightMap; @@ -60,18 +59,9 @@ pub struct BracketPair { pub newline: bool, } -#[async_trait] -pub trait DiagnosticProvider: 'static + Send + Sync { - async fn diagnose( - &self, - path: Arc, - ) -> Result, Vec>>>; -} - pub struct Language { pub(crate) config: LanguageConfig, pub(crate) grammar: Option>, - pub(crate) diagnostic_provider: Option>, } pub struct Grammar { @@ -136,7 +126,6 @@ impl Language { highlight_map: Default::default(), }) }), - diagnostic_provider: None, } } @@ -170,11 +159,6 @@ impl Language { Ok(self) } - pub fn with_diagnostic_provider(mut self, source: impl DiagnosticProvider) -> Self { - self.diagnostic_provider = Some(Arc::new(source)); - self - } - pub fn name(&self) -> &str { self.config.name.as_str() } @@ -208,10 +192,6 @@ impl Language { } } - pub fn diagnostic_provider(&self) -> Option<&Arc> { - self.diagnostic_provider.as_ref() - } - pub fn disk_based_diagnostic_sources(&self) -> Option<&HashSet> { self.config .language_server diff --git a/crates/language/src/proto.rs b/crates/language/src/proto.rs index 727b6a4d79..200a687052 100644 --- a/crates/language/src/proto.rs +++ b/crates/language/src/proto.rs @@ -57,16 +57,12 @@ pub fn serialize_operation(operation: &Operation) -> proto::Operation { lamport_timestamp: lamport_timestamp.value, }), Operation::UpdateDiagnostics { - provider_name, diagnostics, lamport_timestamp, - } => proto::operation::Variant::UpdateDiagnosticSet(proto::UpdateDiagnosticSet { + } => proto::operation::Variant::UpdateDiagnostics(proto::UpdateDiagnostics { replica_id: lamport_timestamp.replica_id as u32, lamport_timestamp: lamport_timestamp.value, - diagnostic_set: Some(serialize_diagnostic_set( - provider_name.clone(), - diagnostics.iter(), - )), + diagnostics: serialize_diagnostics(diagnostics.iter()), }), }), } @@ -103,33 +99,29 @@ pub fn serialize_selections(selections: &Arc<[Selection]>) -> Vec( - provider_name: String, +pub fn serialize_diagnostics<'a>( diagnostics: impl IntoIterator>, -) -> proto::DiagnosticSet { - proto::DiagnosticSet { - provider_name, - diagnostics: diagnostics - .into_iter() - .map(|entry| proto::Diagnostic { - start: Some(serialize_anchor(&entry.range.start)), - end: Some(serialize_anchor(&entry.range.end)), - message: entry.diagnostic.message.clone(), - severity: match entry.diagnostic.severity { - DiagnosticSeverity::ERROR => proto::diagnostic::Severity::Error, - DiagnosticSeverity::WARNING => proto::diagnostic::Severity::Warning, - DiagnosticSeverity::INFORMATION => proto::diagnostic::Severity::Information, - DiagnosticSeverity::HINT => proto::diagnostic::Severity::Hint, - _ => proto::diagnostic::Severity::None, - } as i32, - group_id: entry.diagnostic.group_id as u64, - is_primary: entry.diagnostic.is_primary, - is_valid: entry.diagnostic.is_valid, - code: entry.diagnostic.code.clone(), - is_disk_based: entry.diagnostic.is_disk_based, - }) - .collect(), - } +) -> Vec { + diagnostics + .into_iter() + .map(|entry| proto::Diagnostic { + start: Some(serialize_anchor(&entry.range.start)), + end: Some(serialize_anchor(&entry.range.end)), + message: entry.diagnostic.message.clone(), + severity: match entry.diagnostic.severity { + DiagnosticSeverity::ERROR => proto::diagnostic::Severity::Error, + DiagnosticSeverity::WARNING => proto::diagnostic::Severity::Warning, + DiagnosticSeverity::INFORMATION => proto::diagnostic::Severity::Information, + DiagnosticSeverity::HINT => proto::diagnostic::Severity::Hint, + _ => proto::diagnostic::Severity::None, + } as i32, + group_id: entry.diagnostic.group_id as u64, + is_primary: entry.diagnostic.is_primary, + is_valid: entry.diagnostic.is_valid, + code: entry.diagnostic.code.clone(), + is_disk_based: entry.diagnostic.is_disk_based, + }) + .collect() } fn serialize_anchor(anchor: &Anchor) -> proto::Anchor { @@ -215,21 +207,13 @@ pub fn deserialize_operation(message: proto::Operation) -> Result { value: message.lamport_timestamp, }, }, - proto::operation::Variant::UpdateDiagnosticSet(message) => { - let (provider_name, diagnostics) = deserialize_diagnostic_set( - message - .diagnostic_set - .ok_or_else(|| anyhow!("missing diagnostic set"))?, - ); - Operation::UpdateDiagnostics { - provider_name, - diagnostics, - lamport_timestamp: clock::Lamport { - replica_id: message.replica_id as ReplicaId, - value: message.lamport_timestamp, - }, - } - } + proto::operation::Variant::UpdateDiagnostics(message) => Operation::UpdateDiagnostics { + diagnostics: deserialize_diagnostics(message.diagnostics), + lamport_timestamp: clock::Lamport { + replica_id: message.replica_id as ReplicaId, + value: message.lamport_timestamp, + }, + }, }, ) } @@ -269,40 +253,32 @@ pub fn deserialize_selections(selections: Vec) -> Arc<[Selecti ) } -pub fn deserialize_diagnostic_set( - message: proto::DiagnosticSet, -) -> (String, Arc<[DiagnosticEntry]>) { - ( - message.provider_name, - message - .diagnostics - .into_iter() - .filter_map(|diagnostic| { - Some(DiagnosticEntry { - range: deserialize_anchor(diagnostic.start?)? - ..deserialize_anchor(diagnostic.end?)?, - diagnostic: Diagnostic { - severity: match proto::diagnostic::Severity::from_i32(diagnostic.severity)? - { - proto::diagnostic::Severity::Error => DiagnosticSeverity::ERROR, - proto::diagnostic::Severity::Warning => DiagnosticSeverity::WARNING, - proto::diagnostic::Severity::Information => { - DiagnosticSeverity::INFORMATION - } - proto::diagnostic::Severity::Hint => DiagnosticSeverity::HINT, - proto::diagnostic::Severity::None => return None, - }, - message: diagnostic.message, - group_id: diagnostic.group_id as usize, - code: diagnostic.code, - is_valid: diagnostic.is_valid, - is_primary: diagnostic.is_primary, - is_disk_based: diagnostic.is_disk_based, +pub fn deserialize_diagnostics( + diagnostics: Vec, +) -> Arc<[DiagnosticEntry]> { + diagnostics + .into_iter() + .filter_map(|diagnostic| { + Some(DiagnosticEntry { + range: deserialize_anchor(diagnostic.start?)?..deserialize_anchor(diagnostic.end?)?, + diagnostic: Diagnostic { + severity: match proto::diagnostic::Severity::from_i32(diagnostic.severity)? { + proto::diagnostic::Severity::Error => DiagnosticSeverity::ERROR, + proto::diagnostic::Severity::Warning => DiagnosticSeverity::WARNING, + proto::diagnostic::Severity::Information => DiagnosticSeverity::INFORMATION, + proto::diagnostic::Severity::Hint => DiagnosticSeverity::HINT, + proto::diagnostic::Severity::None => return None, }, - }) + message: diagnostic.message, + group_id: diagnostic.group_id as usize, + code: diagnostic.code, + is_valid: diagnostic.is_valid, + is_primary: diagnostic.is_primary, + is_disk_based: diagnostic.is_disk_based, + }, }) - .collect(), - ) + }) + .collect() } fn deserialize_anchor(anchor: proto::Anchor) -> Option { diff --git a/crates/language/src/tests.rs b/crates/language/src/tests.rs index 4db5e788f8..e94ff781f3 100644 --- a/crates/language/src/tests.rs +++ b/crates/language/src/tests.rs @@ -455,7 +455,6 @@ async fn test_diagnostics(mut cx: gpui::TestAppContext) { // Receive diagnostics for an earlier version of the buffer. buffer .update_diagnostics( - "lsp".into(), Some(open_notification.text_document.version), vec![ DiagnosticEntry { @@ -503,34 +502,28 @@ async fn test_diagnostics(mut cx: gpui::TestAppContext) { .diagnostics_in_range::<_, Point>(Point::new(3, 0)..Point::new(5, 0)) .collect::>(), &[ - ( - "lsp", - DiagnosticEntry { - range: Point::new(3, 9)..Point::new(3, 11), - diagnostic: Diagnostic { - severity: DiagnosticSeverity::ERROR, - message: "undefined variable 'BB'".to_string(), - is_disk_based: true, - group_id: 1, - is_primary: true, - ..Default::default() - }, + DiagnosticEntry { + range: Point::new(3, 9)..Point::new(3, 11), + diagnostic: Diagnostic { + severity: DiagnosticSeverity::ERROR, + message: "undefined variable 'BB'".to_string(), + is_disk_based: true, + group_id: 1, + is_primary: true, + ..Default::default() + }, + }, + DiagnosticEntry { + range: Point::new(4, 9)..Point::new(4, 12), + diagnostic: Diagnostic { + severity: DiagnosticSeverity::ERROR, + message: "undefined variable 'CCC'".to_string(), + is_disk_based: true, + group_id: 2, + is_primary: true, + ..Default::default() } - ), - ( - "lsp", - DiagnosticEntry { - range: Point::new(4, 9)..Point::new(4, 12), - diagnostic: Diagnostic { - severity: DiagnosticSeverity::ERROR, - message: "undefined variable 'CCC'".to_string(), - is_disk_based: true, - group_id: 2, - is_primary: true, - ..Default::default() - } - } - ) + } ] ); assert_eq!( @@ -557,7 +550,6 @@ async fn test_diagnostics(mut cx: gpui::TestAppContext) { // Ensure overlapping diagnostics are highlighted correctly. buffer .update_diagnostics( - "lsp".into(), Some(open_notification.text_document.version), vec![ DiagnosticEntry { @@ -591,33 +583,27 @@ async fn test_diagnostics(mut cx: gpui::TestAppContext) { .diagnostics_in_range::<_, Point>(Point::new(2, 0)..Point::new(3, 0)) .collect::>(), &[ - ( - "lsp", - DiagnosticEntry { - range: Point::new(2, 9)..Point::new(2, 12), - diagnostic: Diagnostic { - severity: DiagnosticSeverity::WARNING, - message: "unreachable statement".to_string(), - group_id: 1, - is_primary: true, - ..Default::default() - } + DiagnosticEntry { + range: Point::new(2, 9)..Point::new(2, 12), + diagnostic: Diagnostic { + severity: DiagnosticSeverity::WARNING, + message: "unreachable statement".to_string(), + group_id: 1, + is_primary: true, + ..Default::default() } - ), - ( - "lsp", - DiagnosticEntry { - range: Point::new(2, 9)..Point::new(2, 10), - diagnostic: Diagnostic { - severity: DiagnosticSeverity::ERROR, - message: "undefined variable 'A'".to_string(), - is_disk_based: true, - group_id: 0, - is_primary: true, - ..Default::default() - }, - } - ) + }, + DiagnosticEntry { + range: Point::new(2, 9)..Point::new(2, 10), + diagnostic: Diagnostic { + severity: DiagnosticSeverity::ERROR, + message: "undefined variable 'A'".to_string(), + is_disk_based: true, + group_id: 0, + is_primary: true, + ..Default::default() + }, + } ] ); assert_eq!( @@ -654,7 +640,6 @@ async fn test_diagnostics(mut cx: gpui::TestAppContext) { buffer.update(&mut cx, |buffer, cx| { buffer .update_diagnostics( - "lsp".into(), Some(change_notification_2.text_document.version), vec![ DiagnosticEntry { @@ -689,34 +674,28 @@ async fn test_diagnostics(mut cx: gpui::TestAppContext) { .diagnostics_in_range::<_, Point>(0..buffer.len()) .collect::>(), &[ - ( - "lsp", - DiagnosticEntry { - range: Point::new(2, 21)..Point::new(2, 22), - diagnostic: Diagnostic { - severity: DiagnosticSeverity::ERROR, - message: "undefined variable 'A'".to_string(), - is_disk_based: true, - group_id: 0, - is_primary: true, - ..Default::default() - } + DiagnosticEntry { + range: Point::new(2, 21)..Point::new(2, 22), + diagnostic: Diagnostic { + severity: DiagnosticSeverity::ERROR, + message: "undefined variable 'A'".to_string(), + is_disk_based: true, + group_id: 0, + is_primary: true, + ..Default::default() } - ), - ( - "lsp", - DiagnosticEntry { - range: Point::new(3, 9)..Point::new(3, 11), - diagnostic: Diagnostic { - severity: DiagnosticSeverity::ERROR, - message: "undefined variable 'BB'".to_string(), - is_disk_based: true, - group_id: 1, - is_primary: true, - ..Default::default() - }, - } - ) + }, + DiagnosticEntry { + range: Point::new(3, 9)..Point::new(3, 11), + diagnostic: Diagnostic { + severity: DiagnosticSeverity::ERROR, + message: "undefined variable 'BB'".to_string(), + is_disk_based: true, + group_id: 1, + is_primary: true, + ..Default::default() + }, + } ] ); }); @@ -735,7 +714,6 @@ async fn test_empty_diagnostic_ranges(mut cx: gpui::TestAppContext) { buffer.set_language(Some(Arc::new(rust_lang())), None, cx); buffer .update_diagnostics( - "lsp".into(), None, vec![ DiagnosticEntry { diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index f23df06eb7..499b3d4a52 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -18,7 +18,7 @@ use std::{ path::Path, sync::{atomic::AtomicBool, Arc}, }; -use util::{ResultExt, TryFutureExt as _}; +use util::TryFutureExt as _; pub use fs::*; pub use worktree::*; @@ -475,10 +475,7 @@ impl Project { fn add_worktree(&mut self, worktree: ModelHandle, cx: &mut ModelContext) { cx.observe(&worktree, |_, _, cx| cx.notify()).detach(); - cx.subscribe(&worktree, |this, worktree, event, cx| match event { - worktree::Event::LanguageRegistered => { - this.diagnose(cx); - } + cx.subscribe(&worktree, |_, worktree, event, cx| match event { worktree::Event::DiagnosticsUpdated(path) => { cx.emit(Event::DiagnosticsUpdated(ProjectPath { worktree_id: worktree.id(), @@ -506,36 +503,6 @@ impl Project { } } - pub fn diagnose(&self, cx: &mut ModelContext) { - for worktree_handle in &self.worktrees { - if let Some(worktree) = worktree_handle.read(cx).as_local() { - for language in worktree.languages() { - if let Some(provider) = language.diagnostic_provider().cloned() { - let worktree_path = worktree.abs_path().clone(); - let worktree_handle = worktree_handle.downgrade(); - cx.spawn_weak(|_, mut cx| async move { - let diagnostics = provider.diagnose(worktree_path).await.log_err()?; - let worktree_handle = worktree_handle.upgrade(&cx)?; - worktree_handle.update(&mut cx, |worktree, cx| { - for (path, diagnostics) in diagnostics { - worktree - .update_diagnostics_from_provider( - path.into(), - diagnostics, - cx, - ) - .log_err()?; - } - Some(()) - }) - }) - .detach(); - } - } - } - } - } - pub fn diagnostic_summaries<'a>( &'a self, cx: &'a AppContext, diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 82a0755223..76f0e2b270 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -50,8 +50,6 @@ use util::{post_inc, ResultExt, TryFutureExt}; lazy_static! { static ref GITIGNORE: &'static OsStr = OsStr::new(".gitignore"); - static ref DIAGNOSTIC_PROVIDER_NAME: Arc = Arc::from("diagnostic_source"); - static ref LSP_PROVIDER_NAME: Arc = Arc::from("lsp"); } #[derive(Clone, Debug)] @@ -68,7 +66,6 @@ pub enum Worktree { #[derive(Debug)] pub enum Event { - LanguageRegistered, DiagnosticsUpdated(Arc), } @@ -675,7 +672,7 @@ impl Worktree { } } - pub fn update_diagnostics_from_lsp( + pub fn update_diagnostics( &mut self, mut params: lsp::PublishDiagnosticsParams, disk_based_sources: &HashSet, @@ -745,6 +742,17 @@ impl Worktree { }) .collect::>(); + self.update_diagnostic_entries(worktree_path, params.version, diagnostics, cx)?; + Ok(()) + } + + pub fn update_diagnostic_entries( + &mut self, + worktree_path: Arc, + version: Option, + diagnostics: Vec>, + cx: &mut ModelContext, + ) -> Result<()> { let this = self.as_local_mut().unwrap(); for buffer in this.open_buffers.values() { if let Some(buffer) = buffer.upgrade(cx) { @@ -756,12 +764,7 @@ impl Worktree { let (remote_id, operation) = buffer.update(cx, |buffer, cx| { ( buffer.remote_id(), - buffer.update_diagnostics( - LSP_PROVIDER_NAME.clone(), - params.version, - diagnostics.clone(), - cx, - ), + buffer.update_diagnostics(version, diagnostics.clone(), cx), ) }); self.send_buffer_update(remote_id, operation?, cx); @@ -773,51 +776,11 @@ impl Worktree { let this = self.as_local_mut().unwrap(); this.diagnostic_summaries .insert(worktree_path.clone(), DiagnosticSummary::new(&diagnostics)); - this.lsp_diagnostics - .insert(worktree_path.clone(), diagnostics); + this.diagnostics.insert(worktree_path.clone(), diagnostics); cx.emit(Event::DiagnosticsUpdated(worktree_path.clone())); Ok(()) } - pub fn update_diagnostics_from_provider( - &mut self, - path: Arc, - diagnostics: Vec>, - cx: &mut ModelContext, - ) -> Result<()> { - let this = self.as_local_mut().unwrap(); - for buffer in this.open_buffers.values() { - if let Some(buffer) = buffer.upgrade(cx) { - if buffer - .read(cx) - .file() - .map_or(false, |file| *file.path() == path) - { - let (remote_id, operation) = buffer.update(cx, |buffer, cx| { - ( - buffer.remote_id(), - buffer.update_diagnostics( - DIAGNOSTIC_PROVIDER_NAME.clone(), - None, - diagnostics.clone(), - cx, - ), - ) - }); - self.send_buffer_update(remote_id, operation?, cx); - break; - } - } - } - - let this = self.as_local_mut().unwrap(); - this.diagnostic_summaries - .insert(path.clone(), DiagnosticSummary::new(&diagnostics)); - this.provider_diagnostics.insert(path.clone(), diagnostics); - cx.emit(Event::DiagnosticsUpdated(path.clone())); - Ok(()) - } - fn send_buffer_update( &mut self, buffer_id: u64, @@ -888,8 +851,7 @@ pub struct LocalWorktree { loading_buffers: LoadingBuffers, open_buffers: HashMap>, shared_buffers: HashMap>>, - lsp_diagnostics: HashMap, Vec>>, - provider_diagnostics: HashMap, Vec>>, + diagnostics: HashMap, Vec>>, diagnostic_summaries: BTreeMap, DiagnosticSummary>, queued_operations: Vec<(u64, Operation)>, language_registry: Arc, @@ -997,8 +959,7 @@ impl LocalWorktree { loading_buffers: Default::default(), open_buffers: Default::default(), shared_buffers: Default::default(), - lsp_diagnostics: Default::default(), - provider_diagnostics: Default::default(), + diagnostics: Default::default(), diagnostic_summaries: Default::default(), queued_operations: Default::default(), language_registry: languages, @@ -1061,7 +1022,6 @@ impl LocalWorktree { ) -> Option> { if !self.languages.iter().any(|l| Arc::ptr_eq(l, language)) { self.languages.push(language.clone()); - cx.emit(Event::LanguageRegistered); } if let Some(server) = self.language_servers.get(language.name()) { @@ -1087,7 +1047,7 @@ impl LocalWorktree { while let Ok(diagnostics) = diagnostics_rx.recv().await { if let Some(handle) = cx.read(|cx| this.upgrade(cx)) { handle.update(&mut cx, |this, cx| { - this.update_diagnostics_from_lsp(diagnostics, &disk_based_sources, cx) + this.update_diagnostics(diagnostics, &disk_based_sources, cx) .log_err(); }); } else { @@ -1138,35 +1098,25 @@ impl LocalWorktree { .update(&mut cx, |t, cx| t.as_local().unwrap().load(&path, cx)) .await?; - let (lsp_diagnostics, provider_diagnostics, language, language_server) = - this.update(&mut cx, |this, cx| { - let this = this.as_local_mut().unwrap(); - let lsp_diagnostics = this.lsp_diagnostics.remove(&path); - let provider_diagnostics = this.provider_diagnostics.remove(&path); - let language = this - .language_registry - .select_language(file.full_path()) - .cloned(); - let server = language - .as_ref() - .and_then(|language| this.register_language(language, cx)); - (lsp_diagnostics, provider_diagnostics, language, server) - }); + let (diagnostics, language, language_server) = this.update(&mut cx, |this, cx| { + let this = this.as_local_mut().unwrap(); + let diagnostics = this.diagnostics.remove(&path); + let language = this + .language_registry + .select_language(file.full_path()) + .cloned(); + let server = language + .as_ref() + .and_then(|language| this.register_language(language, cx)); + (diagnostics, language, server) + }); let mut buffer_operations = Vec::new(); let buffer = cx.add_model(|cx| { let mut buffer = Buffer::from_file(0, contents, Box::new(file), cx); buffer.set_language(language, language_server, cx); - if let Some(diagnostics) = lsp_diagnostics { - let op = buffer - .update_diagnostics(LSP_PROVIDER_NAME.clone(), None, diagnostics, cx) - .unwrap(); - buffer_operations.push(op); - } - if let Some(diagnostics) = provider_diagnostics { - let op = buffer - .update_diagnostics(DIAGNOSTIC_PROVIDER_NAME.clone(), None, diagnostics, cx) - .unwrap(); + if let Some(diagnostics) = diagnostics { + let op = buffer.update_diagnostics(None, diagnostics, cx).unwrap(); buffer_operations.push(op); } buffer @@ -3739,19 +3689,16 @@ mod tests { .collect::>(); assert_eq!( diagnostics, - &[( - LSP_PROVIDER_NAME.as_ref(), - DiagnosticEntry { - range: Point::new(0, 9)..Point::new(0, 10), - diagnostic: Diagnostic { - severity: lsp::DiagnosticSeverity::ERROR, - message: "undefined variable 'A'".to_string(), - group_id: 0, - is_primary: true, - ..Default::default() - } + &[DiagnosticEntry { + range: Point::new(0, 9)..Point::new(0, 10), + diagnostic: Diagnostic { + severity: lsp::DiagnosticSeverity::ERROR, + message: "undefined variable 'A'".to_string(), + group_id: 0, + is_primary: true, + ..Default::default() } - )] + }] ) }); } @@ -3896,7 +3843,7 @@ mod tests { worktree .update(&mut cx, |tree, cx| { - tree.update_diagnostics_from_lsp(message, &Default::default(), cx) + tree.update_diagnostics(message, &Default::default(), cx) }) .unwrap(); let buffer = buffer.read_with(&cx, |buffer, _| buffer.snapshot()); @@ -3906,78 +3853,61 @@ mod tests { .diagnostics_in_range::<_, Point>(0..buffer.len()) .collect::>(), &[ - ( - LSP_PROVIDER_NAME.as_ref(), - DiagnosticEntry { - range: Point::new(1, 8)..Point::new(1, 9), - diagnostic: Diagnostic { - severity: DiagnosticSeverity::WARNING, - message: "error 1".to_string(), - group_id: 0, - is_primary: true, - ..Default::default() - } + DiagnosticEntry { + range: Point::new(1, 8)..Point::new(1, 9), + diagnostic: Diagnostic { + severity: DiagnosticSeverity::WARNING, + message: "error 1".to_string(), + group_id: 0, + is_primary: true, + ..Default::default() } - ), - ( - LSP_PROVIDER_NAME.as_ref(), - DiagnosticEntry { - range: Point::new(1, 8)..Point::new(1, 9), - diagnostic: Diagnostic { - severity: DiagnosticSeverity::HINT, - message: "error 1 hint 1".to_string(), - group_id: 0, - is_primary: false, - ..Default::default() - } + }, + DiagnosticEntry { + range: Point::new(1, 8)..Point::new(1, 9), + diagnostic: Diagnostic { + severity: DiagnosticSeverity::HINT, + message: "error 1 hint 1".to_string(), + group_id: 0, + is_primary: false, + ..Default::default() } - ), - ( - LSP_PROVIDER_NAME.as_ref(), - DiagnosticEntry { - range: Point::new(1, 13)..Point::new(1, 15), - diagnostic: Diagnostic { - severity: DiagnosticSeverity::HINT, - message: "error 2 hint 1".to_string(), - group_id: 1, - is_primary: false, - ..Default::default() - } + }, + DiagnosticEntry { + range: Point::new(1, 13)..Point::new(1, 15), + diagnostic: Diagnostic { + severity: DiagnosticSeverity::HINT, + message: "error 2 hint 1".to_string(), + group_id: 1, + is_primary: false, + ..Default::default() } - ), - ( - LSP_PROVIDER_NAME.as_ref(), - DiagnosticEntry { - range: Point::new(1, 13)..Point::new(1, 15), - diagnostic: Diagnostic { - severity: DiagnosticSeverity::HINT, - message: "error 2 hint 2".to_string(), - group_id: 1, - is_primary: false, - ..Default::default() - } + }, + DiagnosticEntry { + range: Point::new(1, 13)..Point::new(1, 15), + diagnostic: Diagnostic { + severity: DiagnosticSeverity::HINT, + message: "error 2 hint 2".to_string(), + group_id: 1, + is_primary: false, + ..Default::default() } - ), - ( - LSP_PROVIDER_NAME.as_ref(), - DiagnosticEntry { - range: Point::new(2, 8)..Point::new(2, 17), - diagnostic: Diagnostic { - severity: DiagnosticSeverity::ERROR, - message: "error 2".to_string(), - group_id: 1, - is_primary: true, - ..Default::default() - } + }, + DiagnosticEntry { + range: Point::new(2, 8)..Point::new(2, 17), + diagnostic: Diagnostic { + severity: DiagnosticSeverity::ERROR, + message: "error 2".to_string(), + group_id: 1, + is_primary: true, + ..Default::default() } - ) + } ] ); assert_eq!( - buffer - .diagnostic_group::(&LSP_PROVIDER_NAME, 0) - .collect::>(), + buffer.diagnostic_group::(0).collect::>(), &[ DiagnosticEntry { range: Point::new(1, 8)..Point::new(1, 9), @@ -4002,9 +3932,7 @@ mod tests { ] ); assert_eq!( - buffer - .diagnostic_group::(&LSP_PROVIDER_NAME, 1) - .collect::>(), + buffer.diagnostic_group::(1).collect::>(), &[ DiagnosticEntry { range: Point::new(1, 13)..Point::new(1, 15), diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 0bcd992788..5ef34960e7 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -265,7 +265,7 @@ message Buffer { string content = 2; repeated Operation.Edit history = 3; repeated SelectionSet selections = 4; - repeated DiagnosticSet diagnostic_sets = 5; + repeated Diagnostic diagnostics = 5; } message SelectionSet { @@ -292,15 +292,10 @@ enum Bias { Right = 1; } -message UpdateDiagnosticSet { +message UpdateDiagnostics { uint32 replica_id = 1; uint32 lamport_timestamp = 2; - DiagnosticSet diagnostic_set = 3; -} - -message DiagnosticSet { - string provider_name = 1; - repeated Diagnostic diagnostics = 2; + repeated Diagnostic diagnostics = 3; } message Diagnostic { @@ -329,7 +324,7 @@ message Operation { Undo undo = 2; UpdateSelections update_selections = 3; RemoveSelections remove_selections = 4; - UpdateDiagnosticSet update_diagnostic_set = 5; + UpdateDiagnostics update_diagnostics = 5; } message Edit { diff --git a/crates/rpc/src/peer.rs b/crates/rpc/src/peer.rs index 7d4adededd..bd5d1c384f 100644 --- a/crates/rpc/src/peer.rs +++ b/crates/rpc/src/peer.rs @@ -401,7 +401,7 @@ mod tests { content: "path/one content".to_string(), history: vec![], selections: vec![], - diagnostic_sets: vec![], + diagnostics: vec![], }), } ); @@ -424,7 +424,7 @@ mod tests { content: "path/two content".to_string(), history: vec![], selections: vec![], - diagnostic_sets: vec![], + diagnostics: vec![], }), } ); @@ -455,7 +455,7 @@ mod tests { content: "path/one content".to_string(), history: vec![], selections: vec![], - diagnostic_sets: vec![], + diagnostics: vec![], }), } } @@ -467,7 +467,7 @@ mod tests { content: "path/two content".to_string(), history: vec![], selections: vec![], - diagnostic_sets: vec![], + diagnostics: vec![], }), } } diff --git a/crates/server/src/rpc.rs b/crates/server/src/rpc.rs index e507d3d7b8..0b1b7e7aab 100644 --- a/crates/server/src/rpc.rs +++ b/crates/server/src/rpc.rs @@ -1831,7 +1831,7 @@ mod tests { buffer .snapshot() .diagnostics_in_range::<_, Point>(0..buffer.len()) - .map(|(_, entry)| entry) + .map(|entry| entry) .collect::>(), &[ DiagnosticEntry { diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index ffbfee7d6d..b45d292cf2 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -791,24 +791,16 @@ impl Workspace { { error!("failed to save item: {:?}, ", error); } - - handle.update(&mut cx, |this, cx| { - this.project.update(cx, |project, cx| project.diagnose(cx)) - }); }) .detach(); } }, ); } else { - cx.spawn(|this, mut cx| async move { + cx.spawn(|_, mut cx| async move { if let Err(error) = cx.update(|cx| item.save(cx)).unwrap().await { error!("failed to save item: {:?}, ", error); } - - this.update(&mut cx, |this, cx| { - this.project.update(cx, |project, cx| project.diagnose(cx)) - }); }) .detach(); } @@ -840,10 +832,6 @@ impl Workspace { if let Err(error) = result { error!("failed to save item: {:?}, ", error); } - - handle.update(&mut cx, |this, cx| { - this.project.update(cx, |project, cx| project.diagnose(cx)) - }); }) .detach() } diff --git a/crates/zed/src/language.rs b/crates/zed/src/language.rs index 293deada40..a84d2cbd40 100644 --- a/crates/zed/src/language.rs +++ b/crates/zed/src/language.rs @@ -7,184 +7,6 @@ use std::{str, sync::Arc}; #[folder = "languages"] struct LanguageDir; -mod rust { - use anyhow::Result; - use async_trait::async_trait; - use collections::{HashMap, HashSet}; - use language::{Diagnostic, DiagnosticEntry, DiagnosticSeverity}; - use parking_lot::Mutex; - use serde::Deserialize; - use serde_json::Deserializer; - use smol::process::Command; - use std::path::{Path, PathBuf}; - use std::sync::Arc; - - #[derive(Default)] - pub struct DiagnosticProvider { - reported_paths: Mutex>>, - } - - #[derive(Debug, Deserialize)] - struct Check { - message: CompilerMessage, - } - - #[derive(Debug, Deserialize)] - struct CompilerMessage { - code: Option, - spans: Vec, - message: String, - level: ErrorLevel, - children: Vec, - } - - #[derive(Debug, Deserialize)] - enum ErrorLevel { - #[serde(rename = "warning")] - Warning, - #[serde(rename = "error")] - Error, - #[serde(rename = "help")] - Help, - #[serde(rename = "note")] - Note, - } - - #[derive(Debug, Deserialize)] - struct ErrorCode { - code: String, - } - - #[derive(Clone, Debug, Deserialize)] - struct Span { - is_primary: bool, - file_name: PathBuf, - byte_start: usize, - byte_end: usize, - expansion: Option>, - } - - #[derive(Clone, Debug, Deserialize)] - struct Expansion { - span: Span, - } - - #[async_trait] - impl language::DiagnosticProvider for DiagnosticProvider { - async fn diagnose( - &self, - root_path: Arc, - ) -> Result, Vec>>> { - let output = Command::new("cargo") - .arg("check") - .args(["--message-format", "json"]) - .current_dir(&root_path) - .output() - .await?; - - let mut group_id = 0; - let mut diagnostics_by_path = HashMap::default(); - let mut new_reported_paths = HashSet::default(); - for value in - Deserializer::from_slice(&output.stdout).into_iter::<&serde_json::value::RawValue>() - { - if let Ok(check) = serde_json::from_str::(value?.get()) { - let check_severity = match check.message.level { - ErrorLevel::Warning => DiagnosticSeverity::WARNING, - ErrorLevel::Error => DiagnosticSeverity::ERROR, - ErrorLevel::Help => DiagnosticSeverity::HINT, - ErrorLevel::Note => DiagnosticSeverity::INFORMATION, - }; - - let mut primary_span = None; - for mut span in check.message.spans { - if let Some(mut expansion) = span.expansion { - expansion.span.is_primary = span.is_primary; - span = expansion.span; - } - - let span_path: Arc = span.file_name.as_path().into(); - new_reported_paths.insert(span_path.clone()); - diagnostics_by_path - .entry(span_path) - .or_insert(Vec::new()) - .push(DiagnosticEntry { - range: span.byte_start..span.byte_end, - diagnostic: Diagnostic { - code: check.message.code.as_ref().map(|c| c.code.clone()), - severity: check_severity, - message: check.message.message.clone(), - group_id, - is_valid: true, - is_primary: span.is_primary, - is_disk_based: true, - }, - }); - - if span.is_primary { - primary_span = Some(span); - } - } - - for mut child in check.message.children { - if child.spans.is_empty() { - if let Some(primary_span) = primary_span.clone() { - child.spans.push(primary_span); - } - } else { - // TODO - continue; - } - - let child_severity = match child.level { - ErrorLevel::Warning => DiagnosticSeverity::WARNING, - ErrorLevel::Error => DiagnosticSeverity::ERROR, - ErrorLevel::Help => DiagnosticSeverity::HINT, - ErrorLevel::Note => DiagnosticSeverity::INFORMATION, - }; - - for mut span in child.spans { - if let Some(expansion) = span.expansion { - span = expansion.span; - } - - let span_path: Arc = span.file_name.as_path().into(); - new_reported_paths.insert(span_path.clone()); - diagnostics_by_path - .entry(span_path) - .or_insert(Vec::new()) - .push(DiagnosticEntry { - range: span.byte_start..span.byte_end, - diagnostic: Diagnostic { - code: child.code.as_ref().map(|c| c.code.clone()), - severity: child_severity, - message: child.message.clone(), - group_id, - is_valid: true, - is_primary: false, - is_disk_based: true, - }, - }); - } - } - - group_id += 1; - } - } - - let reported_paths = &mut *self.reported_paths.lock(); - for old_reported_path in reported_paths.iter() { - if !diagnostics_by_path.contains_key(old_reported_path) { - diagnostics_by_path.insert(old_reported_path.clone(), Default::default()); - } - } - *reported_paths = new_reported_paths; - - Ok(diagnostics_by_path) - } - } -} - pub fn build_language_registry() -> LanguageRegistry { let mut languages = LanguageRegistry::default(); languages.add(Arc::new(rust())); @@ -202,7 +24,6 @@ fn rust() -> Language { .unwrap() .with_indents_query(load_query("rust/indents.scm").as_ref()) .unwrap() - .with_diagnostic_provider(rust::DiagnosticProvider::default()) } fn markdown() -> Language {