WIP

Max Brunsfeld created

Change summary

Cargo.lock                   |  3 
Cargo.toml                   |  1 
zed/src/editor/buffer/mod.rs | 77 +++++++++++++++++++++++++++++++------
3 files changed, 66 insertions(+), 15 deletions(-)

Detailed changes

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",

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"}

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<FileHandle>,
     language: Option<Arc<Language>>,
-    tree: Mutex<Option<(Tree, time::Global)>>,
+    tree: Option<(Tree, time::Global)>,
     is_parsing: bool,
     selections: HashMap<SelectionSetId, Arc<[Selection]>>,
+    cursor: QueryCursor,
     pub selections_last_update: SelectionsVersion,
     deferred_ops: OperationQueue<Operation>,
     deferred_replicas: HashSet<ReplicaId>,
@@ -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<Tree> {
-        if let Some((tree, tree_version)) = self.tree.lock().as_mut() {
+    pub fn syntax_tree(&mut self) -> Option<Tree> {
+        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<T>,
+    ) -> impl Iterator<Item = (&'a str, usize)> {
+        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<Item = char> + '_ {
         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<Self::Item> {
+        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<Buffer>, 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()
             })
         }
     }