diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 0276268784..a2801a8502 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -637,7 +637,7 @@ mod tests { worktree.update(&mut cx, |worktree, cx| { worktree - .update_diagnostic_entries( + .update_point_utf16_diagnostics( "lsp".into(), Arc::from("/test/main.rs".as_ref()), None, @@ -764,7 +764,7 @@ mod tests { worktree.update(&mut cx, |worktree, cx| { worktree - .update_diagnostic_entries( + .update_point_utf16_diagnostics( "lsp".into(), Arc::from("/test/a.rs".as_ref()), None, diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index e752364835..6303cfd374 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -66,12 +66,12 @@ pub struct BracketPair { #[async_trait] pub trait DiagnosticSource: 'static + Send + Sync { - fn name(&self) -> &'static str; + fn name(&self) -> Arc; async fn diagnose( &self, path: Arc, - ) -> Result>)>>; + ) -> Result>)>>; } pub struct Language { diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index b1151b2d16..7d6a6d9d02 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -511,17 +511,22 @@ impl Project { let worktree_path = worktree.abs_path().clone(); let worktree_handle = worktree_handle.downgrade(); cx.spawn_weak(|_, mut cx| async move { - if let Some(diagnostics) = - diagnostic_source.diagnose(worktree_path).await.log_err() - { - if let Some(worktree_handle) = worktree_handle.upgrade(&cx) { - worktree_handle.update(&mut cx, |worktree, cx| { - for (path, diagnostics) in diagnostics { - todo!() - } - }) + let diagnostics = + diagnostic_source.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_offset_diagnostics( + diagnostic_source.name(), + path.into(), + diagnostics, + cx, + ) + .log_err()?; } - } + Some(()) + }) }) .detach(); } diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index aa7d76b6ab..1284300e96 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -743,7 +743,7 @@ impl Worktree { }) .collect::>(); - self.update_diagnostic_entries( + self.update_point_utf16_diagnostics( provider_name, worktree_path, params.version, @@ -752,87 +752,54 @@ impl Worktree { ) } - pub fn update_diagnostics( + pub fn update_offset_diagnostics( &mut self, provider_name: Arc, - mut params: lsp::PublishDiagnosticsParams, - disk_based_sources: &HashSet, + path: Arc, + diagnostics: Vec>, cx: &mut ModelContext, ) -> Result<()> { - let this = self.as_local_mut().ok_or_else(|| anyhow!("not local"))?; - let abs_path = params - .uri - .to_file_path() - .map_err(|_| anyhow!("URI is not a file"))?; - let worktree_path = Arc::from( - abs_path - .strip_prefix(&this.abs_path) - .context("path is not within worktree")?, - ); - - let mut group_ids_by_diagnostic_range = HashMap::default(); - let mut diagnostics_by_group_id = HashMap::default(); - let mut next_group_id = 0; - for diagnostic in &mut params.diagnostics { - let source = diagnostic.source.as_ref(); - let code = diagnostic.code.as_ref(); - let group_id = diagnostic_ranges(&diagnostic, &abs_path) - .find_map(|range| group_ids_by_diagnostic_range.get(&(source, code, range))) - .copied() - .unwrap_or_else(|| { - let group_id = post_inc(&mut next_group_id); - for range in diagnostic_ranges(&diagnostic, &abs_path) { - group_ids_by_diagnostic_range.insert((source, code, range), group_id); - } - group_id - }); - - diagnostics_by_group_id - .entry(group_id) - .or_insert(Vec::new()) - .push(DiagnosticEntry { - range: diagnostic.range.start.to_point_utf16() - ..diagnostic.range.end.to_point_utf16(), - diagnostic: Diagnostic { - code: diagnostic.code.clone().map(|code| match code { - lsp::NumberOrString::Number(code) => code.to_string(), - lsp::NumberOrString::String(code) => code, - }), - severity: diagnostic.severity.unwrap_or(DiagnosticSeverity::ERROR), - message: mem::take(&mut diagnostic.message), - group_id, - is_primary: false, - is_valid: true, - is_disk_based: diagnostic - .source - .as_ref() - .map_or(false, |source| disk_based_sources.contains(source)), - }, - }); + 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( + provider_name, + None, + diagnostics + .iter() + .map(|entry| DiagnosticEntry { + range: buffer.offset_to_point_utf16(entry.range.start) + ..buffer.offset_to_point_utf16(entry.range.end), + diagnostic: entry.diagnostic.clone(), + }) + .collect(), + cx, + ), + ) + }); + self.send_buffer_update(remote_id, operation?, cx); + break; + } + } } - let diagnostics = diagnostics_by_group_id - .into_values() - .flat_map(|mut diagnostics| { - let primary = diagnostics - .iter_mut() - .min_by_key(|entry| entry.diagnostic.severity) - .unwrap(); - primary.diagnostic.is_primary = true; - diagnostics - }) - .collect::>(); - - self.update_diagnostic_entries( - provider_name, - worktree_path, - params.version, - diagnostics, - cx, - ) + let this = self.as_local_mut().unwrap(); + this.diagnostic_summaries + .insert(path.clone(), DiagnosticSummary::new(&diagnostics)); + this.offset_diagnostics.insert(path.clone(), diagnostics); + cx.emit(Event::DiagnosticsUpdated(path.clone())); + Ok(()) } - pub fn update_diagnostic_entries( + pub fn update_point_utf16_diagnostics( &mut self, provider_name: Arc, path: Arc, @@ -868,11 +835,26 @@ impl Worktree { let this = self.as_local_mut().unwrap(); this.diagnostic_summaries .insert(path.clone(), DiagnosticSummary::new(&diagnostics)); - this.diagnostics.insert(path.clone(), diagnostics); + this.point_utf16_diagnostics + .insert(path.clone(), diagnostics); cx.emit(Event::DiagnosticsUpdated(path.clone())); Ok(()) } + fn convert_diagnostics( + diagnostics: &[DiagnosticEntry], + buffer: &Buffer, + ) -> Vec> { + diagnostics + .iter() + .map(|entry| DiagnosticEntry { + range: buffer.offset_to_point_utf16(entry.range.start) + ..buffer.offset_to_point_utf16(entry.range.end), + diagnostic: entry.diagnostic.clone(), + }) + .collect() + } + fn send_buffer_update( &mut self, buffer_id: u64, @@ -943,7 +925,8 @@ pub struct LocalWorktree { loading_buffers: LoadingBuffers, open_buffers: HashMap>, shared_buffers: HashMap>>, - diagnostics: HashMap, Vec>>, + point_utf16_diagnostics: HashMap, Vec>>, + offset_diagnostics: HashMap, Vec>>, diagnostic_summaries: BTreeMap, DiagnosticSummary>, queued_operations: Vec<(u64, Operation)>, language_registry: Arc, @@ -1051,7 +1034,8 @@ impl LocalWorktree { loading_buffers: Default::default(), open_buffers: Default::default(), shared_buffers: Default::default(), - diagnostics: Default::default(), + point_utf16_diagnostics: Default::default(), + offset_diagnostics: Default::default(), diagnostic_summaries: Default::default(), queued_operations: Default::default(), language_registry: languages, @@ -1200,27 +1184,39 @@ impl LocalWorktree { .update(&mut cx, |t, cx| t.as_local().unwrap().load(&path, cx)) .await?; - 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 (point_utf16_diagnostics, offset_diagnostics, language, language_server) = this + .update(&mut cx, |this, cx| { + let this = this.as_local_mut().unwrap(); + let point_utf16_diagnostics = this.point_utf16_diagnostics.remove(&path); + let offset_diagnostics = this.offset_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)); + ( + point_utf16_diagnostics, + offset_diagnostics, + language, + server, + ) + }); 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) = diagnostics { + if let Some(diagnostics) = point_utf16_diagnostics { buffer .update_diagnostics(todo!(), None, diagnostics, cx) .unwrap(); } + if let Some(diagnostics) = offset_diagnostics { + buffer + .update_offset_diagnostics(todo!(), None, diagnostics, cx) + .unwrap(); + } buffer });