diff --git a/Cargo.lock b/Cargo.lock index 1aabe1333d..503333e6a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2705,8 +2705,7 @@ dependencies = [ [[package]] name = "tree-sitter" version = "0.19.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad726ec26496bf4c083fff0f43d4eb3a2ad1bba305323af5ff91383c0b6ecac0" +source = "git+https://github.com/tree-sitter/tree-sitter?rev=8c3d1466ecae2a22a9625d1456ffaae84b13fd3e#8c3d1466ecae2a22a9625d1456ffaae84b13fd3e" dependencies = [ "cc", "regex", diff --git a/Cargo.toml b/Cargo.toml index 8109db121b..28ab1b44a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = ["zed", "gpui", "gpui_macros", "fsevent", "scoped_pool"] [patch.crates-io] async-task = {git = "https://github.com/zed-industries/async-task", rev = "341b57d6de98cdfd7b418567b8de2022ca993a6e"} +tree-sitter = {git = "https://github.com/tree-sitter/tree-sitter", rev = "8c3d1466ecae2a22a9625d1456ffaae84b13fd3e"} # TODO - Remove when a version is released with this PR: https://github.com/servo/core-foundation-rs/pull/457 cocoa = {git = "https://github.com/servo/core-foundation-rs", rev = "025dcb3c0d1ef01530f57ef65f3b1deb948f5737"} diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index 6c0086a03a..194f0a4d6f 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -4,13 +4,12 @@ pub mod rope; mod selection; pub use anchor::*; -use parking_lot::Mutex; pub use point::*; pub use rope::{ChunksIter, Rope, TextSummary}; use seahash::SeaHasher; pub use selection::*; use similar::{ChangeTag, TextDiff}; -use tree_sitter::{InputEdit, Parser}; +use tree_sitter::{InputEdit, Parser, QueryCursor}; use crate::{ editor::Bias, @@ -77,9 +76,10 @@ pub struct Buffer { history: History, file: Option, language: Option>, - tree: Mutex>, + tree: Option<(Tree, time::Global)>, is_parsing: bool, selections: HashMap>, + cursor: QueryCursor, pub selections_last_update: SelectionsVersion, deferred_ops: OperationQueue, deferred_replicas: HashSet, @@ -487,8 +487,9 @@ impl Buffer { undo_map: Default::default(), history, file, - tree: Mutex::new(None), + tree: None, is_parsing: false, + cursor: QueryCursor::new(), language, saved_mtime, selections: HashMap::default(), @@ -549,8 +550,8 @@ impl Buffer { ctx.emit(Event::Saved); } - pub fn syntax_tree(&self) -> Option { - if let Some((tree, tree_version)) = self.tree.lock().as_mut() { + pub fn syntax_tree(&mut self) -> Option { + if let Some((mut tree, tree_version)) = self.tree.take() { let mut delta = 0_isize; for Edit { old_range, @@ -572,8 +573,9 @@ impl Buffer { }); delta += new_bytes as isize - old_bytes as isize; } - *tree_version = self.version(); - Some(tree.clone()) + let result = tree.clone(); + self.tree = Some((tree, self.version())); + Some(result) } else { None } @@ -581,7 +583,6 @@ impl Buffer { fn should_reparse(&self) -> bool { self.tree - .lock() .as_ref() .map_or(true, |(_, tree_version)| *tree_version != self.version) } @@ -613,7 +614,7 @@ impl Buffer { .await; handle.update(&mut ctx, |this, ctx| { - *this.tree.lock() = Some((new_tree, new_version)); + this.tree = Some((new_tree, new_version)); ctx.emit(Event::Reparsed); }); } @@ -750,6 +751,36 @@ impl Buffer { self.visible_text.chunks_in_range(start..end) } + pub fn highlighted_text_for_range<'a, T: ToOffset>( + &'a mut self, + range: Range, + ) -> impl Iterator { + if let (Some(language), Some((tree, _))) = (&self.language, self.tree.as_ref()) { + let visible_text = &self.visible_text; + let start = range.start.to_offset(self); + let end = range.end.to_offset(self); + self.cursor.set_byte_range(start, end); + let chunks = self.visible_text.chunks_in_range(start..end); + let captures = self.cursor.captures( + &language.highlight_query, + tree.root_node(), + move |node: tree_sitter::Node| { + visible_text + .chunks_in_range(node.byte_range()) + .map(str::as_bytes) + }, + ); + + HighlightedChunksIter { + captures, + chunks, + stack: Default::default(), + } + } else { + todo!() + } + } + pub fn chars(&self) -> impl Iterator + '_ { self.chars_at(0) } @@ -2003,8 +2034,9 @@ impl Clone for Buffer { selections_last_update: self.selections_last_update.clone(), deferred_ops: self.deferred_ops.clone(), file: self.file.clone(), + cursor: tree_sitter::QueryCursor::new(), language: self.language.clone(), - tree: Mutex::new(self.tree.lock().clone()), + tree: self.tree.clone(), is_parsing: false, deferred_replicas: self.deferred_replicas.clone(), replica_id: self.replica_id, @@ -2135,6 +2167,25 @@ impl<'a, F: Fn(&FragmentSummary) -> bool> Iterator for Edits<'a, F> { } } +pub struct HighlightedChunksIter<'a, T: tree_sitter::TextProvider<'a>> { + chunks: ChunksIter<'a>, + captures: tree_sitter::QueryCaptures<'a, 'a, T>, + stack: Vec<(usize, usize)>, +} + +impl<'a, T: tree_sitter::TextProvider<'a>> Iterator for HighlightedChunksIter<'a, T> { + type Item = (&'a str, usize); + + fn next(&mut self) -> Option { + if let Some((mat, capture_ix)) = self.captures.next() { + let capture = mat.captures[capture_ix as usize]; + let range = capture.node.range(); + } + + todo!() + } +} + #[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Debug)] struct FragmentId(Arc<[u16]>); @@ -3276,7 +3327,7 @@ mod tests { let buffer = Buffer::from_history(0, History::new(text.into()), None, rust_lang, ctx); assert!(buffer.is_parsing); - assert!(buffer.syntax_tree().is_none()); + assert!(buffer.tree.is_none()); buffer }); @@ -3390,7 +3441,7 @@ mod tests { fn get_tree_sexp(buffer: &ModelHandle, ctx: &gpui::TestAppContext) -> String { buffer.read_with(ctx, |buffer, _| { - buffer.syntax_tree().unwrap().root_node().to_sexp() + buffer.tree.as_ref().unwrap().0.root_node().to_sexp() }) } }