Cargo.lock 🔗
@@ -5702,6 +5702,7 @@ dependencies = [
"chat_panel",
"client",
"clock",
+ "collections",
"contacts_panel",
"crossbeam-channel",
"ctor",
Antonio Scandurra and Nathan Sobo created
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
Cargo.lock | 1
crates/language/src/buffer.rs | 7 +
crates/language/src/language.rs | 10 --
crates/workspace/src/workspace.rs | 14 +++
crates/zed/Cargo.toml | 1
crates/zed/src/language.rs | 115 +++++++++++++++++++++++++++++++++
6 files changed, 138 insertions(+), 10 deletions(-)
@@ -5702,6 +5702,7 @@ dependencies = [
"chat_panel",
"client",
"clock",
+ "collections",
"contacts_panel",
"crossbeam-channel",
"ctor",
@@ -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);
@@ -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<Path>,
- ) -> Result<Vec<(PathBuf, Vec<DiagnosticEntry<usize>>)>>;
+ ) -> Result<HashMap<Arc<Path>, Vec<DiagnosticEntry<usize>>>>;
}
pub struct Language {
@@ -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()
}
@@ -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" }
@@ -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<HashSet<Arc<Path>>>,
+ }
+
+ #[derive(Debug, Deserialize)]
+ struct Check {
+ message: CompilerMessage,
+ }
+
+ #[derive(Debug, Deserialize)]
+ struct CompilerMessage {
+ code: ErrorCode,
+ spans: Vec<Span>,
+ 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<Path>,
+ ) -> Result<HashMap<Arc<Path>, Vec<DiagnosticEntry<usize>>>> {
+ 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::<Check>(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<Path> = 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 {