Move `highlighted_text_in_range` from `Buffer` to `buffer::Snapshot`

Antonio Scandurra created

Change summary

zed/src/editor/buffer/mod.rs           | 199 +++++++++++++++------------
zed/src/editor/buffer_view.rs          |   4 
zed/src/editor/display_map/fold_map.rs |  67 +++-----
zed/src/editor/display_map/mod.rs      |  91 ++++--------
zed/src/editor/movement.rs             |  16 +-
zed/src/worktree.rs                    |   5 
6 files changed, 182 insertions(+), 200 deletions(-)

Detailed changes

zed/src/editor/buffer/mod.rs šŸ”—

@@ -62,7 +62,11 @@ type HashMap<K, V> = std::collections::HashMap<K, V>;
 type HashSet<T> = std::collections::HashSet<T>;
 
 thread_local! {
-    pub static PARSER: RefCell<Parser> = RefCell::new(Parser::new());
+    static PARSER: RefCell<Parser> = RefCell::new(Parser::new());
+}
+
+lazy_static! {
+    static ref QUERY_CURSORS: Mutex<Vec<QueryCursor>> = Default::default();
 }
 
 pub struct Buffer {
@@ -78,8 +82,7 @@ pub struct Buffer {
     history: History,
     file: Option<FileHandle>,
     language: Option<Arc<Language>>,
-    tree: Option<(Tree, time::Global)>,
-    query_cursor: Mutex<Option<tree_sitter::QueryCursor>>,
+    tree: Mutex<Option<(Tree, time::Global)>>,
     is_parsing: bool,
     selections: HashMap<SelectionSetId, Arc<[Selection]>>,
     pub selections_last_update: SelectionsVersion,
@@ -489,10 +492,9 @@ impl Buffer {
             undo_map: Default::default(),
             history,
             file,
-            tree: None,
+            tree: Mutex::new(None),
             is_parsing: false,
             language,
-            query_cursor: Mutex::new(Some(QueryCursor::new())),
             saved_mtime,
             selections: HashMap::default(),
             selections_last_update: 0,
@@ -506,8 +508,14 @@ impl Buffer {
         result
     }
 
-    pub fn snapshot(&self) -> Rope {
-        self.visible_text.clone()
+    pub fn snapshot(&self) -> Snapshot {
+        let mut cursors = QUERY_CURSORS.lock();
+        Snapshot {
+            text: self.visible_text.clone(),
+            tree: self.syntax_tree(),
+            language: self.language.clone(),
+            query_cursor: Some(cursors.pop().unwrap_or_else(|| QueryCursor::new())),
+        }
     }
 
     pub fn file(&self) -> Option<&FileHandle> {
@@ -519,13 +527,13 @@ impl Buffer {
         new_file: Option<FileHandle>,
         ctx: &mut ModelContext<Self>,
     ) -> Task<Result<()>> {
-        let snapshot = self.snapshot();
+        let text = self.visible_text.clone();
         let version = self.version.clone();
         let file = self.file.clone();
 
         ctx.spawn(|handle, mut ctx| async move {
             if let Some(file) = new_file.as_ref().or(file.as_ref()) {
-                let result = ctx.read(|ctx| file.save(snapshot, ctx.as_ref())).await;
+                let result = ctx.read(|ctx| file.save(text, ctx.as_ref())).await;
                 if result.is_ok() {
                     handle.update(&mut ctx, |me, ctx| me.did_save(version, new_file, ctx));
                 }
@@ -552,8 +560,8 @@ impl Buffer {
         ctx.emit(Event::Saved);
     }
 
-    pub fn syntax_tree(&mut self) -> Option<Tree> {
-        if let Some((mut tree, tree_version)) = self.tree.take() {
+    pub fn syntax_tree(&self) -> Option<Tree> {
+        if let Some((tree, tree_version)) = self.tree.lock().as_mut() {
             let mut delta = 0_isize;
             for Edit {
                 old_range,
@@ -575,9 +583,8 @@ impl Buffer {
                 });
                 delta += new_bytes as isize - old_bytes as isize;
             }
-            let result = tree.clone();
-            self.tree = Some((tree, self.version()));
-            Some(result)
+            *tree_version = self.version();
+            Some(tree.clone())
         } else {
             None
         }
@@ -585,6 +592,7 @@ impl Buffer {
 
     fn should_reparse(&self) -> bool {
         self.tree
+            .lock()
             .as_ref()
             .map_or(true, |(_, tree_version)| *tree_version != self.version)
     }
@@ -616,7 +624,7 @@ impl Buffer {
                         .await;
 
                     handle.update(&mut ctx, |this, ctx| {
-                        this.tree = Some((new_tree, new_version));
+                        *this.tree.lock() = Some((new_tree, new_version));
                         ctx.emit(Event::Reparsed);
                         ctx.notify();
                     });
@@ -754,47 +762,6 @@ impl Buffer {
         self.visible_text.chunks_in_range(start..end)
     }
 
-    pub fn highlighted_text_for_range<T: ToOffset>(&self, range: Range<T>) -> HighlightedChunks {
-        let start = range.start.to_offset(self);
-        let end = range.end.to_offset(self);
-        let chunks = self.visible_text.chunks_in_range(start..end);
-
-        if let (Some(language), Some((tree, _))) = (&self.language, self.tree.as_ref()) {
-            let mut cursor = self
-                .query_cursor
-                .lock()
-                .take()
-                .unwrap_or_else(|| QueryCursor::new());
-
-            cursor.set_byte_range(start, end);
-            let cursor_ref = unsafe { mem::transmute::<_, &'static mut QueryCursor>(&mut cursor) };
-            let captures = cursor_ref.captures(
-                &language.highlight_query,
-                tree.root_node(),
-                TextProvider(&self.visible_text),
-            );
-
-            HighlightedChunks {
-                range: start..end,
-                chunks,
-                highlights: Some(Highlights {
-                    captures: captures.peekable(),
-                    stack: Default::default(),
-                    theme_mapping: language.theme_mapping(),
-                    cursor,
-                }),
-                buffer: self,
-            }
-        } else {
-            HighlightedChunks {
-                range: start..end,
-                chunks,
-                highlights: None,
-                buffer: self,
-            }
-        }
-    }
-
     pub fn chars(&self) -> impl Iterator<Item = char> + '_ {
         self.chars_at(0)
     }
@@ -2049,9 +2016,8 @@ impl Clone for Buffer {
             deferred_ops: self.deferred_ops.clone(),
             file: self.file.clone(),
             language: self.language.clone(),
-            tree: self.tree.clone(),
+            tree: Mutex::new(self.tree.lock().clone()),
             is_parsing: false,
-            query_cursor: Mutex::new(Some(QueryCursor::new())),
             deferred_replicas: self.deferred_replicas.clone(),
             replica_id: self.replica_id,
             local_clock: self.local_clock.clone(),
@@ -2060,6 +2026,79 @@ impl Clone for Buffer {
     }
 }
 
+pub struct Snapshot {
+    text: Rope,
+    tree: Option<Tree>,
+    language: Option<Arc<Language>>,
+    query_cursor: Option<QueryCursor>,
+}
+
+impl Snapshot {
+    pub fn len(&self) -> usize {
+        self.text.len()
+    }
+
+    pub fn text(&self) -> Rope {
+        self.text.clone()
+    }
+
+    pub fn text_for_range(&self, range: Range<usize>) -> Chunks {
+        self.text.chunks_in_range(range)
+    }
+
+    pub fn highlighted_text_for_range(&mut self, range: Range<usize>) -> HighlightedChunks {
+        let chunks = self.text.chunks_in_range(range.clone());
+        if let Some((language, tree)) = self.language.as_ref().zip(self.tree.as_ref()) {
+            let query_cursor = self.query_cursor.as_mut().unwrap();
+            query_cursor.set_byte_range(range.start, range.end);
+            let captures = query_cursor.captures(
+                &language.highlight_query,
+                tree.root_node(),
+                TextProvider(&self.text),
+            );
+
+            HighlightedChunks {
+                range,
+                chunks,
+                highlights: Some(Highlights {
+                    captures,
+                    next_capture: None,
+                    stack: Default::default(),
+                    theme_mapping: language.theme_mapping(),
+                }),
+            }
+        } else {
+            HighlightedChunks {
+                range,
+                chunks,
+                highlights: None,
+            }
+        }
+    }
+
+    pub fn clip_offset(&self, offset: usize, bias: Bias) -> usize {
+        self.text.clip_offset(offset, bias)
+    }
+
+    pub fn clip_point(&self, point: Point, bias: Bias) -> Point {
+        self.text.clip_point(point, bias)
+    }
+
+    pub fn to_offset(&self, point: Point) -> usize {
+        self.text.to_offset(point)
+    }
+
+    pub fn to_point(&self, offset: usize) -> Point {
+        self.text.to_point(offset)
+    }
+}
+
+impl Drop for Snapshot {
+    fn drop(&mut self) {
+        QUERY_CURSORS.lock().push(self.query_cursor.take().unwrap());
+    }
+}
+
 struct RopeBuilder<'a> {
     old_visible_cursor: rope::Cursor<'a>,
     old_deleted_cursor: rope::Cursor<'a>,
@@ -2202,9 +2241,9 @@ impl<'a> tree_sitter::TextProvider<'a> for TextProvider<'a> {
 }
 
 struct Highlights<'a> {
-    captures: iter::Peekable<tree_sitter::QueryCaptures<'a, 'a, TextProvider<'a>>>,
+    captures: tree_sitter::QueryCaptures<'a, 'a, TextProvider<'a>>,
+    next_capture: Option<(tree_sitter::QueryMatch<'a, 'a>, usize)>,
     stack: Vec<(usize, StyleId)>,
-    cursor: QueryCursor,
     theme_mapping: ThemeMap,
 }
 
@@ -2212,7 +2251,6 @@ pub struct HighlightedChunks<'a> {
     range: Range<usize>,
     chunks: Chunks<'a>,
     highlights: Option<Highlights<'a>>,
-    buffer: &'a Buffer,
 }
 
 impl<'a> HighlightedChunks<'a> {
@@ -2220,21 +2258,9 @@ impl<'a> HighlightedChunks<'a> {
         self.range.start = offset;
         self.chunks.seek(self.range.start);
         if let Some(highlights) = self.highlights.as_mut() {
-            let language = self.buffer.language.as_ref().unwrap();
-            let tree = &self.buffer.tree.as_ref().unwrap().0;
-            let cursor_ref =
-                unsafe { mem::transmute::<_, &'static mut QueryCursor>(&mut highlights.cursor) };
-            highlights
-                .cursor
-                .set_byte_range(self.range.start, self.range.end);
             highlights.stack.clear();
-            highlights.captures = cursor_ref
-                .captures(
-                    &language.highlight_query,
-                    tree.root_node(),
-                    TextProvider(&self.buffer.visible_text),
-                )
-                .peekable();
+            highlights.next_capture.take();
+            highlights.captures.advance_to_byte(self.range.start);
         }
     }
 
@@ -2258,7 +2284,11 @@ impl<'a> Iterator for HighlightedChunks<'a> {
                 }
             }
 
-            while let Some((mat, capture_ix)) = highlights.captures.peek() {
+            if highlights.next_capture.is_none() {
+                highlights.next_capture = highlights.captures.next();
+            }
+
+            while let Some((mat, capture_ix)) = highlights.next_capture.as_ref() {
                 let capture = mat.captures[*capture_ix as usize];
                 if self.range.start < capture.node.start_byte() {
                     next_capture_start = capture.node.start_byte();
@@ -2266,7 +2296,7 @@ impl<'a> Iterator for HighlightedChunks<'a> {
                 } else {
                     let style_id = highlights.theme_mapping.get(capture.index);
                     highlights.stack.push((capture.node.end_byte(), style_id));
-                    highlights.captures.next().unwrap();
+                    highlights.next_capture = highlights.captures.next();
                 }
             }
         }
@@ -2296,17 +2326,6 @@ impl<'a> Iterator for HighlightedChunks<'a> {
     }
 }
 
-impl<'a> Drop for HighlightedChunks<'a> {
-    fn drop(&mut self) {
-        if let Some(highlights) = self.highlights.take() {
-            let mut buffer_cursor = self.buffer.query_cursor.lock();
-            if buffer_cursor.is_none() {
-                *buffer_cursor = Some(highlights.cursor);
-            }
-        }
-    }
-}
-
 #[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Debug)]
 struct FragmentId(Arc<[u16]>);
 
@@ -3448,7 +3467,7 @@ mod tests {
 
             let buffer = Buffer::from_history(0, History::new(text.into()), None, rust_lang, ctx);
             assert!(buffer.is_parsing);
-            assert!(buffer.tree.is_none());
+            assert!(buffer.syntax_tree().is_none());
             buffer
         });
 
@@ -3562,7 +3581,7 @@ mod tests {
 
         fn get_tree_sexp(buffer: &ModelHandle<Buffer>, ctx: &gpui::TestAppContext) -> String {
             buffer.read_with(ctx, |buffer, _| {
-                buffer.tree.as_ref().unwrap().0.root_node().to_sexp()
+                buffer.syntax_tree().unwrap().root_node().to_sexp()
             })
         }
     }

zed/src/editor/buffer_view.rs šŸ”—

@@ -2148,8 +2148,8 @@ impl BufferView {
         let mut line = String::new();
         let mut styles = Vec::new();
         let mut row = rows.start;
-        let snapshot = self.display_map.snapshot(ctx);
-        let chunks = snapshot.highlighted_chunks_at(rows.start, ctx);
+        let mut snapshot = self.display_map.snapshot(ctx);
+        let chunks = snapshot.highlighted_chunks_at(rows.start);
         let theme = settings.theme.clone();
 
         'outer: for (chunk, style_ix) in chunks.chain(Some(("\n", StyleId::default()))) {

zed/src/editor/display_map/fold_map.rs šŸ”—

@@ -46,7 +46,7 @@ impl FoldMap {
     pub fn snapshot(&self, ctx: &AppContext) -> FoldMapSnapshot {
         FoldMapSnapshot {
             transforms: self.sync(ctx).clone(),
-            buffer: self.buffer.clone(),
+            buffer: self.buffer.read(ctx).snapshot(),
         }
     }
 
@@ -210,11 +210,11 @@ impl FoldMap {
     }
 
     pub fn to_buffer_offset(&self, point: DisplayPoint, ctx: &AppContext) -> usize {
-        self.snapshot(ctx).to_buffer_offset(point, ctx)
+        self.snapshot(ctx).to_buffer_offset(point)
     }
 
     pub fn to_display_offset(&self, point: DisplayPoint, ctx: &AppContext) -> DisplayOffset {
-        self.snapshot(ctx).to_display_offset(point, ctx)
+        self.snapshot(ctx).to_display_offset(point)
     }
 
     pub fn to_buffer_point(&self, display_point: DisplayPoint, ctx: &AppContext) -> Point {
@@ -394,7 +394,7 @@ impl FoldMap {
 
 pub struct FoldMapSnapshot {
     transforms: SumTree<Transform>,
-    buffer: ModelHandle<Buffer>,
+    buffer: buffer::Snapshot,
 }
 
 impl FoldMapSnapshot {
@@ -413,13 +413,12 @@ impl FoldMapSnapshot {
         }
     }
 
-    pub fn chunks_at<'a>(&'a self, offset: DisplayOffset, ctx: &'a AppContext) -> Chunks<'a> {
+    pub fn chunks_at(&self, offset: DisplayOffset) -> Chunks {
         let mut transform_cursor = self.transforms.cursor::<DisplayOffset, TransformSummary>();
         transform_cursor.seek(&offset, SeekBias::Right, &());
         let overshoot = offset.0 - transform_cursor.start().display.bytes;
         let buffer_offset = transform_cursor.start().buffer.bytes + overshoot;
-        let buffer = self.buffer.read(ctx);
-        let rope_cursor = buffer.text_for_range(buffer_offset..buffer.len());
+        let rope_cursor = self.buffer.text_for_range(buffer_offset..self.buffer.len());
         Chunks {
             transform_cursor,
             buffer_offset,
@@ -427,34 +426,27 @@ impl FoldMapSnapshot {
         }
     }
 
-    pub fn highlighted_chunks_at<'a>(
-        &'a self,
-        offset: DisplayOffset,
-        ctx: &'a AppContext,
-    ) -> HighlightedChunks<'a> {
+    pub fn highlighted_chunks_at(&mut self, offset: DisplayOffset) -> HighlightedChunks {
         let mut transform_cursor = self.transforms.cursor::<DisplayOffset, TransformSummary>();
         transform_cursor.seek(&offset, SeekBias::Right, &());
         let overshoot = offset.0 - transform_cursor.start().display.bytes;
         let buffer_offset = transform_cursor.start().buffer.bytes + overshoot;
-        let buffer = self.buffer.read(ctx);
         HighlightedChunks {
             transform_cursor,
             buffer_offset,
-            buffer_chunks: buffer.highlighted_text_for_range(buffer_offset..buffer.len()),
+            buffer_chunks: self
+                .buffer
+                .highlighted_text_for_range(buffer_offset..self.buffer.len()),
             buffer_chunk: None,
         }
     }
 
-    pub fn chars_at<'a>(
-        &'a self,
-        point: DisplayPoint,
-        ctx: &'a AppContext,
-    ) -> impl Iterator<Item = char> + 'a {
-        let offset = self.to_display_offset(point, ctx);
-        self.chunks_at(offset, ctx).flat_map(str::chars)
+    pub fn chars_at<'a>(&'a self, point: DisplayPoint) -> impl Iterator<Item = char> + 'a {
+        let offset = self.to_display_offset(point);
+        self.chunks_at(offset).flat_map(str::chars)
     }
 
-    pub fn to_display_offset(&self, point: DisplayPoint, ctx: &AppContext) -> DisplayOffset {
+    pub fn to_display_offset(&self, point: DisplayPoint) -> DisplayOffset {
         let mut cursor = self.transforms.cursor::<DisplayPoint, TransformSummary>();
         cursor.seek(&point, SeekBias::Right, &());
         let overshoot = point.0 - cursor.start().display.lines;
@@ -462,27 +454,24 @@ impl FoldMapSnapshot {
         if !overshoot.is_zero() {
             let transform = cursor.item().expect("display point out of range");
             assert!(transform.display_text.is_none());
-            let end_buffer_offset =
-                (cursor.start().buffer.lines + overshoot).to_offset(self.buffer.read(ctx));
+            let end_buffer_offset = self
+                .buffer
+                .to_offset(cursor.start().buffer.lines + overshoot);
             offset += end_buffer_offset - cursor.start().buffer.bytes;
         }
         DisplayOffset(offset)
     }
 
-    pub fn to_buffer_offset(&self, point: DisplayPoint, ctx: &AppContext) -> usize {
+    pub fn to_buffer_offset(&self, point: DisplayPoint) -> usize {
         let mut cursor = self.transforms.cursor::<DisplayPoint, TransformSummary>();
         cursor.seek(&point, SeekBias::Right, &());
         let overshoot = point.0 - cursor.start().display.lines;
-        (cursor.start().buffer.lines + overshoot).to_offset(self.buffer.read(ctx))
+        self.buffer
+            .to_offset(cursor.start().buffer.lines + overshoot)
     }
 
     #[cfg(test)]
-    pub fn clip_offset(
-        &self,
-        offset: DisplayOffset,
-        bias: Bias,
-        ctx: &AppContext,
-    ) -> DisplayOffset {
+    pub fn clip_offset(&self, offset: DisplayOffset, bias: Bias) -> DisplayOffset {
         let mut cursor = self.transforms.cursor::<DisplayOffset, TransformSummary>();
         cursor.seek(&offset, SeekBias::Right, &());
         if let Some(transform) = cursor.item() {
@@ -496,7 +485,7 @@ impl FoldMapSnapshot {
             } else {
                 let overshoot = offset.0 - transform_start;
                 let buffer_offset = cursor.start().buffer.bytes + overshoot;
-                let clipped_buffer_offset = self.buffer.read(ctx).clip_offset(buffer_offset, bias);
+                let clipped_buffer_offset = self.buffer.clip_offset(buffer_offset, bias);
                 DisplayOffset(
                     (offset.0 as isize + (clipped_buffer_offset as isize - buffer_offset as isize))
                         as usize,
@@ -507,7 +496,7 @@ impl FoldMapSnapshot {
         }
     }
 
-    pub fn clip_point(&self, point: DisplayPoint, bias: Bias, ctx: &AppContext) -> DisplayPoint {
+    pub fn clip_point(&self, point: DisplayPoint, bias: Bias) -> DisplayPoint {
         let mut cursor = self.transforms.cursor::<DisplayPoint, TransformSummary>();
         cursor.seek(&point, SeekBias::Right, &());
         if let Some(transform) = cursor.item() {
@@ -521,8 +510,7 @@ impl FoldMapSnapshot {
             } else {
                 let overshoot = point.0 - transform_start;
                 let buffer_position = cursor.start().buffer.lines + overshoot;
-                let clipped_buffer_position =
-                    self.buffer.read(ctx).clip_point(buffer_position, bias);
+                let clipped_buffer_position = self.buffer.clip_point(buffer_position, bias);
                 DisplayPoint::new(
                     point.row(),
                     ((point.column() as i32) + clipped_buffer_position.column as i32
@@ -1131,11 +1119,10 @@ mod tests {
                     let offset = map.snapshot(app.as_ref()).clip_offset(
                         DisplayOffset(rng.gen_range(0..=map.len(app.as_ref()))),
                         Bias::Right,
-                        app.as_ref(),
                     );
                     assert_eq!(
                         map.snapshot(app.as_ref())
-                            .chunks_at(offset, app.as_ref())
+                            .chunks_at(offset)
                             .collect::<String>(),
                         &expected_text[offset.0..],
                     );
@@ -1218,9 +1205,7 @@ mod tests {
 
     impl FoldMap {
         fn text(&self, app: &AppContext) -> String {
-            self.snapshot(app)
-                .chunks_at(DisplayOffset(0), app)
-                .collect()
+            self.snapshot(app).chunks_at(DisplayOffset(0)).collect()
         }
 
         fn merged_fold_ranges(&self, app: &AppContext) -> Vec<Range<usize>> {

zed/src/editor/display_map/mod.rs šŸ”—

@@ -62,16 +62,14 @@ impl DisplayMap {
     }
 
     pub fn text(&self, ctx: &AppContext) -> String {
-        self.snapshot(ctx)
-            .chunks_at(DisplayPoint::zero(), ctx)
-            .collect()
+        self.snapshot(ctx).chunks_at(DisplayPoint::zero()).collect()
     }
 
     pub fn line(&self, display_row: u32, ctx: &AppContext) -> String {
         let mut result = String::new();
         for chunk in self
             .snapshot(ctx)
-            .chunks_at(DisplayPoint::new(display_row, 0), ctx)
+            .chunks_at(DisplayPoint::new(display_row, 0))
         {
             if let Some(ix) = chunk.find('\n') {
                 result.push_str(&chunk[0..ix]);
@@ -88,7 +86,7 @@ impl DisplayMap {
         let mut is_blank = true;
         for c in self
             .snapshot(ctx)
-            .chars_at(DisplayPoint::new(display_row, 0), ctx)
+            .chars_at(DisplayPoint::new(display_row, 0))
         {
             if c == ' ' {
                 indent += 1;
@@ -138,12 +136,11 @@ impl DisplayMapSnapshot {
         self.folds_snapshot.buffer_rows(start_row)
     }
 
-    pub fn chunks_at<'a>(&'a self, point: DisplayPoint, app: &'a AppContext) -> Chunks<'a> {
-        let (point, expanded_char_column, to_next_stop) =
-            self.collapse_tabs(point, Bias::Left, app);
+    pub fn chunks_at(&self, point: DisplayPoint) -> Chunks {
+        let (point, expanded_char_column, to_next_stop) = self.collapse_tabs(point, Bias::Left);
         let fold_chunks = self
             .folds_snapshot
-            .chunks_at(self.folds_snapshot.to_display_offset(point, app), app);
+            .chunks_at(self.folds_snapshot.to_display_offset(point));
         Chunks {
             fold_chunks,
             column: expanded_char_column,
@@ -153,15 +150,11 @@ impl DisplayMapSnapshot {
         }
     }
 
-    pub fn highlighted_chunks_at<'a>(
-        &'a self,
-        row: u32,
-        app: &'a AppContext,
-    ) -> HighlightedChunks<'a> {
+    pub fn highlighted_chunks_at(&mut self, row: u32) -> HighlightedChunks {
         let point = DisplayPoint::new(row, 0);
-        let offset = self.folds_snapshot.to_display_offset(point, app);
+        let offset = self.folds_snapshot.to_display_offset(point);
         HighlightedChunks {
-            fold_chunks: self.folds_snapshot.highlighted_chunks_at(offset, app),
+            fold_chunks: self.folds_snapshot.highlighted_chunks_at(offset),
             column: 0,
             tab_size: self.tab_size,
             chunk: "",
@@ -169,18 +162,14 @@ impl DisplayMapSnapshot {
         }
     }
 
-    pub fn chars_at<'a>(
-        &'a self,
-        point: DisplayPoint,
-        app: &'a AppContext,
-    ) -> impl Iterator<Item = char> + 'a {
-        self.chunks_at(point, app).flat_map(str::chars)
+    pub fn chars_at<'a>(&'a self, point: DisplayPoint) -> impl Iterator<Item = char> + 'a {
+        self.chunks_at(point).flat_map(str::chars)
     }
 
-    pub fn column_to_chars(&self, display_row: u32, target: u32, ctx: &AppContext) -> u32 {
+    pub fn column_to_chars(&self, display_row: u32, target: u32) -> u32 {
         let mut count = 0;
         let mut column = 0;
-        for c in self.chars_at(DisplayPoint::new(display_row, 0), ctx) {
+        for c in self.chars_at(DisplayPoint::new(display_row, 0)) {
             if column >= target {
                 break;
             }
@@ -190,10 +179,10 @@ impl DisplayMapSnapshot {
         count
     }
 
-    pub fn column_from_chars(&self, display_row: u32, char_count: u32, ctx: &AppContext) -> u32 {
+    pub fn column_from_chars(&self, display_row: u32, char_count: u32) -> u32 {
         let mut count = 0;
         let mut column = 0;
-        for c in self.chars_at(DisplayPoint::new(display_row, 0), ctx) {
+        for c in self.chars_at(DisplayPoint::new(display_row, 0)) {
             if c == '\n' || count >= char_count {
                 break;
             }
@@ -203,32 +192,26 @@ impl DisplayMapSnapshot {
         column
     }
 
-    pub fn clip_point(&self, point: DisplayPoint, bias: Bias, ctx: &AppContext) -> DisplayPoint {
+    pub fn clip_point(&self, point: DisplayPoint, bias: Bias) -> DisplayPoint {
         self.expand_tabs(
             self.folds_snapshot
-                .clip_point(self.collapse_tabs(point, bias, ctx).0, bias, ctx),
-            ctx,
+                .clip_point(self.collapse_tabs(point, bias).0, bias),
         )
     }
 
-    fn expand_tabs(&self, mut point: DisplayPoint, ctx: &AppContext) -> DisplayPoint {
+    fn expand_tabs(&self, mut point: DisplayPoint) -> DisplayPoint {
         let chars = self
             .folds_snapshot
-            .chars_at(DisplayPoint(Point::new(point.row(), 0)), ctx);
+            .chars_at(DisplayPoint(Point::new(point.row(), 0)));
         let expanded = expand_tabs(chars, point.column() as usize, self.tab_size);
         *point.column_mut() = expanded as u32;
         point
     }
 
-    fn collapse_tabs(
-        &self,
-        mut point: DisplayPoint,
-        bias: Bias,
-        ctx: &AppContext,
-    ) -> (DisplayPoint, usize, usize) {
+    fn collapse_tabs(&self, mut point: DisplayPoint, bias: Bias) -> (DisplayPoint, usize, usize) {
         let chars = self
             .folds_snapshot
-            .chars_at(DisplayPoint(Point::new(point.row(), 0)), ctx);
+            .chars_at(DisplayPoint(Point::new(point.row(), 0)));
         let expanded = point.column() as usize;
         let (collapsed, expanded_char_column, to_next_stop) =
             collapse_tabs(chars, expanded, bias, self.tab_size);
@@ -276,11 +259,11 @@ impl DisplayPoint {
     }
 
     fn expand_tabs(self, map: &DisplayMap, ctx: &AppContext) -> Self {
-        map.snapshot(ctx).expand_tabs(self, ctx)
+        map.snapshot(ctx).expand_tabs(self)
     }
 
     fn collapse_tabs(self, map: &DisplayMap, bias: Bias, ctx: &AppContext) -> Self {
-        map.snapshot(ctx).collapse_tabs(self, bias, ctx).0
+        map.snapshot(ctx).collapse_tabs(self, bias).0
     }
 }
 
@@ -288,7 +271,7 @@ impl Point {
     pub fn to_display_point(self, map: &DisplayMap, ctx: &AppContext) -> DisplayPoint {
         let mut display_point = map.fold_map.to_display_point(self, ctx);
         let snapshot = map.fold_map.snapshot(ctx);
-        let chars = snapshot.chars_at(DisplayPoint::new(display_point.row(), 0), ctx);
+        let chars = snapshot.chars_at(DisplayPoint::new(display_point.row(), 0));
         *display_point.column_mut() =
             expand_tabs(chars, display_point.column() as usize, map.tab_size) as u32;
         display_point
@@ -487,19 +470,19 @@ mod tests {
 
         assert_eq!(
             &map.snapshot(app.as_ref())
-                .chunks_at(DisplayPoint::new(1, 0), app.as_ref())
+                .chunks_at(DisplayPoint::new(1, 0))
                 .collect::<String>()[0..10],
             "    b   bb"
         );
         assert_eq!(
             &map.snapshot(app.as_ref())
-                .chunks_at(DisplayPoint::new(1, 2), app.as_ref())
+                .chunks_at(DisplayPoint::new(1, 2))
                 .collect::<String>()[0..10],
             "  b   bbbb"
         );
         assert_eq!(
             &map.snapshot(app.as_ref())
-                .chunks_at(DisplayPoint::new(1, 6), app.as_ref())
+                .chunks_at(DisplayPoint::new(1, 6))
                 .collect::<String>()[0..13],
             "  bbbbb\nc   c"
         );
@@ -534,7 +517,7 @@ mod tests {
             ),
         ] {
             assert_eq!(
-                map.clip_point(DisplayPoint::new(1, input_column as u32), bias, ctx),
+                map.clip_point(DisplayPoint::new(1, input_column as u32), bias),
                 DisplayPoint::new(1, output_column as u32),
                 "clip_point(({}, {}))",
                 1,
@@ -584,7 +567,7 @@ mod tests {
         );
         assert_eq!(
             map.snapshot(ctx)
-                .chunks_at(DisplayPoint::new(0, "āœ…      ".len() as u32), ctx)
+                .chunks_at(DisplayPoint::new(0, "āœ…      ".len() as u32))
                 .collect::<String>(),
             " α\nβ   \nšŸ€Ī²      γ"
         );
@@ -598,26 +581,20 @@ mod tests {
         );
         assert_eq!(
             map.snapshot(ctx)
-                .chunks_at(DisplayPoint::new(0, "āœ… ".len() as u32), ctx)
+                .chunks_at(DisplayPoint::new(0, "āœ… ".len() as u32))
                 .collect::<String>(),
             "      α\nβ   \nšŸ€Ī²      γ"
         );
 
         // Clipping display points inside of multi-byte characters
         assert_eq!(
-            map.snapshot(ctx).clip_point(
-                DisplayPoint::new(0, "āœ…".len() as u32 - 1),
-                Bias::Left,
-                ctx
-            ),
+            map.snapshot(ctx)
+                .clip_point(DisplayPoint::new(0, "āœ…".len() as u32 - 1), Bias::Left),
             DisplayPoint::new(0, 0)
         );
         assert_eq!(
-            map.snapshot(ctx).clip_point(
-                DisplayPoint::new(0, "āœ…".len() as u32 - 1),
-                Bias::Right,
-                ctx
-            ),
+            map.snapshot(ctx)
+                .clip_point(DisplayPoint::new(0, "āœ…".len() as u32 - 1), Bias::Right),
             DisplayPoint::new(0, "āœ…".len() as u32)
         );
     }

zed/src/editor/movement.rs šŸ”—

@@ -9,7 +9,7 @@ pub fn left(map: &DisplayMap, mut point: DisplayPoint, app: &AppContext) -> Resu
         *point.row_mut() -= 1;
         *point.column_mut() = map.line_len(point.row(), app);
     }
-    Ok(map.snapshot(app).clip_point(point, Bias::Left, app))
+    Ok(map.snapshot(app).clip_point(point, Bias::Left))
 }
 
 pub fn right(map: &DisplayMap, mut point: DisplayPoint, app: &AppContext) -> Result<DisplayPoint> {
@@ -20,7 +20,7 @@ pub fn right(map: &DisplayMap, mut point: DisplayPoint, app: &AppContext) -> Res
         *point.row_mut() += 1;
         *point.column_mut() = 0;
     }
-    Ok(map.snapshot(app).clip_point(point, Bias::Right, app))
+    Ok(map.snapshot(app).clip_point(point, Bias::Right))
 }
 
 pub fn up(
@@ -33,12 +33,12 @@ pub fn up(
     let goal_column = if let SelectionGoal::Column(column) = goal {
         column
     } else {
-        map.column_to_chars(point.row(), point.column(), app)
+        map.column_to_chars(point.row(), point.column())
     };
 
     if point.row() > 0 {
         *point.row_mut() -= 1;
-        *point.column_mut() = map.column_from_chars(point.row(), goal_column, app);
+        *point.column_mut() = map.column_from_chars(point.row(), goal_column);
     } else {
         point = DisplayPoint::new(0, 0);
     }
@@ -57,12 +57,12 @@ pub fn down(
     let goal_column = if let SelectionGoal::Column(column) = goal {
         column
     } else {
-        map.column_to_chars(point.row(), point.column(), app)
+        map.column_to_chars(point.row(), point.column())
     };
 
     if point.row() < max_point.row() {
         *point.row_mut() += 1;
-        *point.column_mut() = map.column_from_chars(point.row(), goal_column, app);
+        *point.column_mut() = map.column_from_chars(point.row(), goal_column);
     } else {
         point = max_point;
     }
@@ -107,7 +107,7 @@ pub fn prev_word_boundary(
         let mut boundary = DisplayPoint::new(point.row(), 0);
         let mut column = 0;
         let mut prev_c = None;
-        for c in map.snapshot(app).chars_at(boundary, app) {
+        for c in map.snapshot(app).chars_at(boundary) {
             if column >= point.column() {
                 break;
             }
@@ -129,7 +129,7 @@ pub fn next_word_boundary(
     app: &AppContext,
 ) -> Result<DisplayPoint> {
     let mut prev_c = None;
-    for c in map.snapshot(app).chars_at(point, app) {
+    for c in map.snapshot(app).chars_at(point) {
         if prev_c.is_some() && (c == '\n' || char_kind(prev_c.unwrap()) != char_kind(c)) {
             break;
         }

zed/src/worktree.rs šŸ”—

@@ -1483,7 +1483,8 @@ mod tests {
         let path = tree.update(&mut app, |tree, ctx| {
             let path = tree.files(0).next().unwrap().path().clone();
             assert_eq!(path.file_name().unwrap(), "file1");
-            smol::block_on(tree.save(&path, buffer.read(ctx).snapshot(), ctx.as_ref())).unwrap();
+            smol::block_on(tree.save(&path, buffer.read(ctx).snapshot().text(), ctx.as_ref()))
+                .unwrap();
             path
         });
 
@@ -1512,7 +1513,7 @@ mod tests {
         let file = app.update(|ctx| tree.file("", ctx)).await;
         app.update(|ctx| {
             assert_eq!(file.path().file_name(), None);
-            smol::block_on(file.save(buffer.read(ctx).snapshot(), ctx.as_ref())).unwrap();
+            smol::block_on(file.save(buffer.read(ctx).snapshot().text(), ctx.as_ref())).unwrap();
         });
 
         let history = app.read(|ctx| file.load_history(ctx)).await.unwrap();