diff --git a/Cargo.lock b/Cargo.lock index 41dcf21797..2407ec6140 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5702,6 +5702,7 @@ dependencies = [ "chat_panel", "client", "clock", + "collections", "contacts_panel", "crossbeam-channel", "ctor", diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 29724db677..64be4a9c04 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -805,10 +805,13 @@ impl Buffer { } } + let start_overshoot = start - last_edit_old_end; start = last_edit_new_end; - start.add_assign(&(start - last_edit_old_end)); + start.add_assign(&start_overshoot); + + let end_overshoot = end - last_edit_old_end; end = last_edit_new_end; - end.add_assign(&(end - last_edit_old_end)); + end.add_assign(&end_overshoot); } let range = start.clip(Bias::Left, content)..end.clip(Bias::Right, content); diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 26c0e84261..d6c13a7fd4 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -9,18 +9,14 @@ use anyhow::{anyhow, Result}; use async_trait::async_trait; pub use buffer::Operation; pub use buffer::*; -use collections::HashSet; +use collections::{HashMap, HashSet}; pub use diagnostic_set::DiagnosticEntry; use gpui::AppContext; use highlight_map::HighlightMap; use lazy_static::lazy_static; use parking_lot::Mutex; use serde::Deserialize; -use std::{ - path::{Path, PathBuf}, - str, - sync::Arc, -}; +use std::{path::Path, str, sync::Arc}; use theme::SyntaxTheme; use tree_sitter::{self, Query}; pub use tree_sitter::{Parser, Tree}; @@ -69,7 +65,7 @@ pub trait DiagnosticProvider: 'static + Send + Sync { async fn diagnose( &self, path: Arc, - ) -> Result>)>>; + ) -> Result, Vec>>>; } pub struct Language { diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index fdce6f6ff2..311b68e096 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -791,16 +791,24 @@ 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(|_, mut cx| async move { + cx.spawn(|this, 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(); } @@ -832,6 +840,10 @@ 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/Cargo.toml b/crates/zed/Cargo.toml index 452122d55a..2b9d7150f2 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -29,6 +29,7 @@ test-support = [ [dependencies] chat_panel = { path = "../chat_panel" } +collections = { path = "../collections" } client = { path = "../client" } clock = { path = "../clock" } contacts_panel = { path = "../contacts_panel" } diff --git a/crates/zed/src/language.rs b/crates/zed/src/language.rs index a84d2cbd40..653c66392c 100644 --- a/crates/zed/src/language.rs +++ b/crates/zed/src/language.rs @@ -7,6 +7,120 @@ 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: ErrorCode, + spans: Vec, + message: String, + level: ErrorLevel, + } + + #[derive(Debug, Deserialize)] + enum ErrorLevel { + #[serde(rename = "warning")] + Warning, + #[serde(rename = "error")] + Error, + #[serde(rename = "note")] + Note, + } + + #[derive(Debug, Deserialize)] + struct ErrorCode { + code: String, + } + + #[derive(Debug, Deserialize)] + struct Span { + is_primary: bool, + file_name: PathBuf, + byte_start: usize, + byte_end: usize, + } + + #[async_trait] + impl language::DiagnosticProvider for DiagnosticProvider { + async fn diagnose( + &self, + path: Arc, + ) -> Result, Vec>>> { + let output = Command::new("cargo") + .arg("check") + .args(["--message-format", "json"]) + .current_dir(&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 severity = match check.message.level { + ErrorLevel::Warning => DiagnosticSeverity::WARNING, + ErrorLevel::Error => DiagnosticSeverity::ERROR, + ErrorLevel::Note => DiagnosticSeverity::INFORMATION, + }; + for span in check.message.spans { + let span_path: Arc = span.file_name.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: Some(check.message.code.code.clone()), + severity, + message: check.message.message.clone(), + group_id, + is_valid: true, + is_primary: span.is_primary, + 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())); @@ -24,6 +138,7 @@ fn rust() -> Language { .unwrap() .with_indents_query(load_query("rust/indents.scm").as_ref()) .unwrap() + .with_diagnostic_provider(rust::DiagnosticProvider::default()) } fn markdown() -> Language {