multi_buffer: Optimize a bunch of things (#48519)

Lukas Wirth and Cole Miller created

Release Notes:

- N/A *or* Added/Fixed/Improved ...

---------

Co-authored-by: Cole Miller <cole@zed.dev>

Change summary

crates/agent/src/edit_agent/streaming_fuzzy_matcher.rs |  16 
crates/buffer_diff/src/buffer_diff.rs                  |  69 ++--
crates/editor/src/display_map.rs                       |   9 
crates/editor/src/display_map/block_map.rs             |  68 +++-
crates/editor/src/split.rs                             |   1 
crates/language/src/buffer.rs                          |  66 ++--
crates/language/src/buffer/row_chunk.rs                |   2 
crates/language/src/syntax_map/syntax_map_tests.rs     |   2 
crates/multi_buffer/src/multi_buffer.rs                | 159 ++++++++---
crates/project/src/lsp_store/inlay_hint_cache.rs       |   2 
crates/sum_tree/src/tree_map.rs                        |   3 
crates/text/src/text.rs                                |  16 
12 files changed, 256 insertions(+), 157 deletions(-)

Detailed changes

crates/agent/src/edit_agent/streaming_fuzzy_matcher.rs 🔗

@@ -320,7 +320,7 @@ mod tests {
         );
         let snapshot = buffer.snapshot();
 
-        let mut finder = StreamingFuzzyMatcher::new(snapshot);
+        let mut finder = StreamingFuzzyMatcher::new(snapshot.clone());
         assert_eq!(push(&mut finder, ""), None);
         assert_eq!(finish(finder), None);
     }
@@ -334,7 +334,7 @@ mod tests {
         );
         let snapshot = buffer.snapshot();
 
-        let mut finder = StreamingFuzzyMatcher::new(snapshot);
+        let mut finder = StreamingFuzzyMatcher::new(snapshot.clone());
 
         // Push partial query
         assert_eq!(push(&mut finder, "This"), None);
@@ -366,7 +366,7 @@ mod tests {
         );
         let snapshot = buffer.snapshot();
 
-        let mut finder = StreamingFuzzyMatcher::new(snapshot);
+        let mut finder = StreamingFuzzyMatcher::new(snapshot.clone());
 
         // Push a fuzzy query that should match the first function
         assert_eq!(
@@ -392,7 +392,7 @@ mod tests {
         );
         let snapshot = buffer.snapshot();
 
-        let mut finder = StreamingFuzzyMatcher::new(snapshot);
+        let mut finder = StreamingFuzzyMatcher::new(snapshot.clone());
 
         // No match initially
         assert_eq!(push(&mut finder, "Lin"), None);
@@ -421,7 +421,7 @@ mod tests {
         );
         let snapshot = buffer.snapshot();
 
-        let mut finder = StreamingFuzzyMatcher::new(snapshot);
+        let mut finder = StreamingFuzzyMatcher::new(snapshot.clone());
 
         // Push text in small chunks across line boundaries
         assert_eq!(push(&mut finder, "jumps "), None); // No newline yet
@@ -459,7 +459,7 @@ mod tests {
         );
         let snapshot = buffer.snapshot();
 
-        let mut finder = StreamingFuzzyMatcher::new(snapshot);
+        let mut finder = StreamingFuzzyMatcher::new(snapshot.clone());
 
         assert_eq!(
             push(&mut finder, "impl Debug for User {\n"),
@@ -716,7 +716,7 @@ mod tests {
             "Expected to match `second_function` based on the line hint"
         );
 
-        let mut matcher = StreamingFuzzyMatcher::new(snapshot);
+        let mut matcher = StreamingFuzzyMatcher::new(snapshot.clone());
         matcher.push(query, None);
         matcher.finish();
         let best_match = matcher.select_best_match();
@@ -732,7 +732,7 @@ mod tests {
         let buffer = TextBuffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), text.clone());
         let snapshot = buffer.snapshot();
 
-        let mut matcher = StreamingFuzzyMatcher::new(snapshot);
+        let mut matcher = StreamingFuzzyMatcher::new(snapshot.clone());
 
         // Split query into random chunks
         let chunks = to_random_chunks(rng, query);

crates/buffer_diff/src/buffer_diff.rs 🔗

@@ -30,7 +30,7 @@ pub struct BufferDiff {
 #[derive(Clone)]
 pub struct BufferDiffSnapshot {
     inner: BufferDiffInner<language::BufferSnapshot>,
-    secondary_diff: Option<Box<BufferDiffSnapshot>>,
+    secondary_diff: Option<Arc<BufferDiffSnapshot>>,
 }
 
 impl std::fmt::Debug for BufferDiffSnapshot {
@@ -222,11 +222,11 @@ impl std::fmt::Debug for BufferDiffInner<language::BufferSnapshot> {
 impl BufferDiffSnapshot {
     #[cfg(test)]
     fn new_sync(
-        buffer: text::BufferSnapshot,
+        buffer: &text::BufferSnapshot,
         diff_base: String,
         cx: &mut gpui::TestAppContext,
     ) -> BufferDiffSnapshot {
-        let buffer_diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, &buffer, cx));
+        let buffer_diff = cx.new(|cx| BufferDiff::new_with_base_text(&diff_base, buffer, cx));
         buffer_diff.update(cx, |buffer_diff, cx| buffer_diff.snapshot(cx))
     }
 
@@ -950,10 +950,10 @@ fn build_diff_options(
 
 fn compute_hunks(
     diff_base: Option<(Arc<str>, Rope)>,
-    buffer: text::BufferSnapshot,
+    buffer: &text::BufferSnapshot,
     diff_options: Option<DiffOptions>,
 ) -> SumTree<InternalDiffHunk> {
-    let mut tree = SumTree::new(&buffer);
+    let mut tree = SumTree::new(buffer);
 
     if let Some((diff_base, diff_base_rope)) = diff_base {
         let buffer_text = buffer.as_rope().to_string();
@@ -980,7 +980,7 @@ fn compute_hunks(
                     base_word_diffs: Vec::default(),
                     buffer_word_diffs: Vec::default(),
                 },
-                &buffer,
+                buffer,
             );
             return tree;
         }
@@ -992,11 +992,11 @@ fn compute_hunks(
                     &patch,
                     hunk_index,
                     &diff_base_rope,
-                    &buffer,
+                    buffer,
                     &mut divergence,
                     diff_options.as_ref(),
                 );
-                tree.push(hunk, &buffer);
+                tree.push(hunk, buffer);
             }
         }
     } else {
@@ -1007,7 +1007,7 @@ fn compute_hunks(
                 base_word_diffs: Vec::default(),
                 buffer_word_diffs: Vec::default(),
             },
-            &buffer,
+            buffer,
         );
     }
 
@@ -1544,7 +1544,7 @@ impl BufferDiff {
                     base_text
                         .clone()
                         .map(|base_text| (base_text, base_text_rope.clone())),
-                    buffer.clone(),
+                    &buffer,
                     diff_options,
                 );
                 let base_text = base_text.unwrap_or_default();
@@ -1785,10 +1785,10 @@ impl BufferDiff {
                 base_text_exists: self.inner.base_text_exists,
                 buffer_snapshot: self.inner.buffer_snapshot.clone(),
             },
-            secondary_diff: self
-                .secondary_diff
-                .as_ref()
-                .map(|diff| Box::new(diff.read(cx).snapshot(cx))),
+            secondary_diff: self.secondary_diff.as_ref().map(|diff| {
+                debug_assert!(diff.read(cx).secondary_diff.is_none());
+                Arc::new(diff.read(cx).snapshot(cx))
+            }),
         }
     }
 
@@ -2018,7 +2018,7 @@ mod tests {
         .unindent();
 
         let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), buffer_text);
-        let mut diff = BufferDiffSnapshot::new_sync(buffer.clone(), diff_base.clone(), cx);
+        let mut diff = BufferDiffSnapshot::new_sync(&buffer, diff_base.clone(), cx);
         assert_hunks(
             diff.hunks_intersecting_range(
                 Anchor::min_max_range_for_buffer(buffer.remote_id()),
@@ -2030,7 +2030,7 @@ mod tests {
         );
 
         buffer.edit([(0..0, "point five\n")]);
-        diff = BufferDiffSnapshot::new_sync(buffer.clone(), diff_base.clone(), cx);
+        diff = BufferDiffSnapshot::new_sync(&buffer, diff_base.clone(), cx);
         assert_hunks(
             diff.hunks_intersecting_range(
                 Anchor::min_max_range_for_buffer(buffer.remote_id()),
@@ -2101,10 +2101,9 @@ mod tests {
         .unindent();
 
         let buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), buffer_text);
-        let unstaged_diff = BufferDiffSnapshot::new_sync(buffer.clone(), index_text, cx);
-        let mut uncommitted_diff =
-            BufferDiffSnapshot::new_sync(buffer.clone(), head_text.clone(), cx);
-        uncommitted_diff.secondary_diff = Some(Box::new(unstaged_diff));
+        let unstaged_diff = BufferDiffSnapshot::new_sync(&buffer, index_text, cx);
+        let mut uncommitted_diff = BufferDiffSnapshot::new_sync(&buffer, head_text.clone(), cx);
+        uncommitted_diff.secondary_diff = Some(Arc::new(unstaged_diff));
 
         let expected_hunks = vec![
             (2..3, "two\n", "TWO\n", DiffHunkStatus::modified_none()),
@@ -2631,7 +2630,7 @@ mod tests {
         let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), buffer_text_1);
 
         let empty_diff = cx.update(|cx| BufferDiff::new(&buffer, cx).snapshot(cx));
-        let diff_1 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);
+        let diff_1 = BufferDiffSnapshot::new_sync(&buffer, base_text.clone(), cx);
         let DiffChanged {
             changed_range,
             base_text_changed_range,
@@ -2666,7 +2665,7 @@ mod tests {
             "
             .unindent(),
         );
-        let diff_2 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);
+        let diff_2 = BufferDiffSnapshot::new_sync(&buffer, base_text.clone(), cx);
         let DiffChanged {
             changed_range,
             base_text_changed_range,
@@ -2704,7 +2703,7 @@ mod tests {
             "
             .unindent(),
         );
-        let diff_3 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);
+        let diff_3 = BufferDiffSnapshot::new_sync(&buffer, base_text.clone(), cx);
         let DiffChanged {
             changed_range,
             base_text_changed_range,
@@ -2738,7 +2737,7 @@ mod tests {
             "
             .unindent(),
         );
-        let diff_4 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);
+        let diff_4 = BufferDiffSnapshot::new_sync(&buffer, base_text.clone(), cx);
         let DiffChanged {
             changed_range,
             base_text_changed_range,
@@ -3172,11 +3171,11 @@ mod tests {
         .unindent();
 
         let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), buffer_text);
-        let old_buffer = buffer.snapshot();
-        let diff_a = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);
+        let old_buffer = buffer.snapshot().clone();
+        let diff_a = BufferDiffSnapshot::new_sync(&buffer, base_text.clone(), cx);
 
         buffer.edit([(Point::new(1, 3)..Point::new(1, 3), "\n")]);
-        let diff_b = BufferDiffSnapshot::new_sync(buffer.clone(), base_text, cx);
+        let diff_b = BufferDiffSnapshot::new_sync(&buffer, base_text, cx);
 
         let DiffChanged {
             changed_range,
@@ -3235,11 +3234,11 @@ mod tests {
         .unindent();
 
         let mut buffer_2 = Buffer::new(ReplicaId::LOCAL, BufferId::new(2).unwrap(), buffer_text_2);
-        let old_buffer_2 = buffer_2.snapshot();
-        let diff_2a = BufferDiffSnapshot::new_sync(buffer_2.clone(), base_text_2.clone(), cx);
+        let old_buffer_2 = buffer_2.snapshot().clone();
+        let diff_2a = BufferDiffSnapshot::new_sync(&buffer_2, base_text_2.clone(), cx);
 
         buffer_2.edit([(Point::new(4, 0)..Point::new(4, 4), "FIVE_CHANGED")]);
-        let diff_2b = BufferDiffSnapshot::new_sync(buffer_2.clone(), base_text_2, cx);
+        let diff_2b = BufferDiffSnapshot::new_sync(&buffer_2, base_text_2, cx);
 
         let DiffChanged {
             changed_range,
@@ -3293,7 +3292,7 @@ mod tests {
             buffer_text_1.to_string(),
         );
 
-        let old_base_snapshot_1 = base_text_buffer.snapshot();
+        let old_base_snapshot_1 = base_text_buffer.snapshot().clone();
         let old_hunks_1 = compute_hunks(
             Some((Arc::from(initial_base), Rope::from(initial_base))),
             buffer.snapshot(),
@@ -3352,7 +3351,7 @@ mod tests {
             buffer_text_2.to_string(),
         );
 
-        let old_base_snapshot_2 = base_buf_2.snapshot();
+        let old_base_snapshot_2 = base_buf_2.snapshot().clone();
         let old_hunks_2 = compute_hunks(
             Some((Arc::from(simple_base), Rope::from(simple_base))),
             buffer_2.snapshot(),
@@ -3419,7 +3418,7 @@ mod tests {
             buffer_text_3.to_string(),
         );
 
-        let old_base_snapshot_3 = base_buf_3.snapshot();
+        let old_base_snapshot_3 = base_buf_3.snapshot().clone();
         let old_hunks_3 = compute_hunks(
             Some((Arc::from(base_3), Rope::from(base_3))),
             buffer_3.snapshot(),
@@ -3485,8 +3484,8 @@ mod tests {
             buffer_text_4.to_string(),
         );
 
-        let old_base_snapshot_4 = base_buf_4.snapshot();
-        let old_buffer_snapshot_4 = buffer_4.snapshot();
+        let old_base_snapshot_4 = base_buf_4.snapshot().clone();
+        let old_buffer_snapshot_4 = buffer_4.snapshot().clone();
         let old_hunks_4 = compute_hunks(
             Some((Arc::from(base_4), Rope::from(base_4))),
             buffer_4.snapshot(),

crates/editor/src/display_map.rs 🔗

@@ -441,6 +441,15 @@ impl DisplayMap {
             return;
         };
 
+        // Second call to set_companion doesn't need to do anything
+        if companion_display_map
+            .update(cx, |companion_dm, _| companion_dm.companion.is_none())
+            .unwrap_or(true)
+        {
+            self.companion = Some((companion_display_map, companion));
+            return;
+        }
+
         let rhs_display_map_id = companion.read(cx).rhs_display_map_id;
         if self.entity_id != rhs_display_map_id {
             let buffer_mapping = companion

crates/editor/src/display_map/block_map.rs 🔗

@@ -1110,20 +1110,32 @@ impl BlockMap {
         let mut companion_tab_point_cursor = companion_snapshot.tab_point_cursor();
         let mut companion_wrap_point_cursor = companion_snapshot.wrap_point_cursor();
 
-        let mut determine_spacer = |our_point: Point, their_point: Point, delta: i32| {
-            let our_wrap = our_wrap_point_cursor
+        let mut our_wrapper = |our_point: Point| {
+            our_wrap_point_cursor
                 .map(our_tab_point_cursor.map(
                     our_fold_point_cursor.map(our_inlay_point_cursor.map(our_point), Bias::Left),
                 ))
-                .row();
-            let companion_wrap = companion_wrap_point_cursor
+                .row()
+        };
+        let mut companion_wrapper = |their_point: Point| {
+            companion_wrap_point_cursor
                 .map(
                     companion_tab_point_cursor.map(
                         companion_fold_point_cursor
                             .map(companion_inlay_point_cursor.map(their_point), Bias::Left),
                     ),
                 )
-                .row();
+                .row()
+        };
+        fn determine_spacer(
+            our_wrapper: &mut impl FnMut(Point) -> WrapRow,
+            companion_wrapper: &mut impl FnMut(Point) -> WrapRow,
+            our_point: Point,
+            their_point: Point,
+            delta: i32,
+        ) -> (i32, Option<(WrapRow, u32)>) {
+            let our_wrap = our_wrapper(our_point);
+            let companion_wrap = companion_wrapper(their_point);
             let new_delta = companion_wrap.0 as i32 - our_wrap.0 as i32;
 
             let spacer = if new_delta > delta {
@@ -1133,7 +1145,7 @@ impl BlockMap {
                 None
             };
             (new_delta, spacer)
-        };
+        }
 
         let mut result = Vec::new();
 
@@ -1179,12 +1191,8 @@ impl BlockMap {
                 // Case 3: We are at the start of the excerpt--no previous row to use as the baseline.
                 (first_point, edit_for_first_point.new.start)
             };
-            let our_baseline = wrap_snapshot
-                .make_wrap_point(our_baseline, Bias::Left)
-                .row();
-            let their_baseline = companion_snapshot
-                .make_wrap_point(their_baseline, Bias::Left)
-                .row();
+            let our_baseline = our_wrapper(our_baseline);
+            let their_baseline = companion_wrapper(their_baseline);
 
             let mut delta = their_baseline.0 as i32 - our_baseline.0 as i32;
 
@@ -1201,8 +1209,13 @@ impl BlockMap {
                     current_boundary = next_point;
                 }
 
-                let (new_delta, spacer) =
-                    determine_spacer(current_boundary, current_range.end, delta);
+                let (new_delta, spacer) = determine_spacer(
+                    &mut our_wrapper,
+                    &mut companion_wrapper,
+                    current_boundary,
+                    current_range.end,
+                    delta,
+                );
 
                 delta = new_delta;
                 if let Some((wrap_row, height)) = spacer {
@@ -1229,8 +1242,13 @@ impl BlockMap {
                 }
 
                 // Align the two sides at the start of this group.
-                let (delta_at_start, mut spacer_at_start) =
-                    determine_spacer(current_boundary, current_range.start, delta);
+                let (delta_at_start, mut spacer_at_start) = determine_spacer(
+                    &mut our_wrapper,
+                    &mut companion_wrapper,
+                    current_boundary,
+                    current_range.start,
+                    delta,
+                );
                 delta = delta_at_start;
 
                 while let Some(next_point) = source_points.peek().copied() {
@@ -1260,8 +1278,13 @@ impl BlockMap {
                     break;
                 }
 
-                let (delta_at_end, spacer_at_end) =
-                    determine_spacer(current_boundary, current_range.end, delta);
+                let (delta_at_end, spacer_at_end) = determine_spacer(
+                    &mut our_wrapper,
+                    &mut companion_wrapper,
+                    current_boundary,
+                    current_range.end,
+                    delta,
+                );
                 delta = delta_at_end;
 
                 if let Some((wrap_row, mut height)) = spacer_at_start {
@@ -1289,8 +1312,13 @@ impl BlockMap {
             }
 
             if last_source_point == excerpt.source_excerpt_range.end {
-                let (_new_delta, spacer) =
-                    determine_spacer(last_source_point, excerpt.target_excerpt_range.end, delta);
+                let (_new_delta, spacer) = determine_spacer(
+                    &mut our_wrapper,
+                    &mut companion_wrapper,
+                    last_source_point,
+                    excerpt.target_excerpt_range.end,
+                    delta,
+                );
                 if let Some((wrap_row, height)) = spacer {
                     result.push((
                         BlockPlacement::Below(wrap_row),

crates/editor/src/split.rs 🔗

@@ -523,6 +523,7 @@ impl SplittableEditor {
             convert_lhs_rows_to_rhs,
         );
 
+        // stream this
         for (path, diff) in path_diffs {
             for (lhs, rhs) in
                 lhs.update_path_excerpts_from_rhs(path, &self.rhs_multibuffer, diff.clone(), cx)

crates/language/src/buffer.rs 🔗

@@ -151,16 +151,16 @@ pub struct TreeSitterData {
 const MAX_ROWS_IN_A_CHUNK: u32 = 50;
 
 impl TreeSitterData {
-    fn clear(&mut self, snapshot: text::BufferSnapshot) {
-        self.chunks = RowChunks::new(snapshot, MAX_ROWS_IN_A_CHUNK);
+    fn clear(&mut self, snapshot: &text::BufferSnapshot) {
+        self.chunks = RowChunks::new(&snapshot, MAX_ROWS_IN_A_CHUNK);
         self.brackets_by_chunks.get_mut().clear();
         self.brackets_by_chunks
             .get_mut()
             .resize(self.chunks.len(), None);
     }
 
-    fn new(snapshot: text::BufferSnapshot) -> Self {
-        let chunks = RowChunks::new(snapshot, MAX_ROWS_IN_A_CHUNK);
+    fn new(snapshot: &text::BufferSnapshot) -> Self {
+        let chunks = RowChunks::new(&snapshot, MAX_ROWS_IN_A_CHUNK);
         Self {
             brackets_by_chunks: Mutex::new(vec![None; chunks.len()]),
             chunks,
@@ -188,12 +188,12 @@ struct BufferBranchState {
 pub struct BufferSnapshot {
     pub text: text::BufferSnapshot,
     pub syntax: SyntaxSnapshot,
-    file: Option<Arc<dyn File>>,
+    tree_sitter_data: Arc<TreeSitterData>,
     diagnostics: TreeMap<LanguageServerId, DiagnosticSet>,
     remote_selections: TreeMap<ReplicaId, SelectionSet>,
     language: Option<Arc<Language>>,
+    file: Option<Arc<dyn File>>,
     non_text_state_update_count: usize,
-    tree_sitter_data: Arc<TreeSitterData>,
     pub capability: Capability,
 }
 
@@ -1168,14 +1168,14 @@ impl Buffer {
         let buffer_id = entity_id.as_non_zero_u64().into();
         async move {
             let text =
-                TextBuffer::new_normalized(ReplicaId::LOCAL, buffer_id, Default::default(), text)
-                    .snapshot();
+                TextBuffer::new_normalized(ReplicaId::LOCAL, buffer_id, Default::default(), text);
+            let text = text.into_snapshot();
             let mut syntax = SyntaxMap::new(&text).snapshot();
             if let Some(language) = language.clone() {
                 let language_registry = language_registry.clone();
                 syntax.reparse(&text, language_registry, language);
             }
-            let tree_sitter_data = TreeSitterData::new(text.clone());
+            let tree_sitter_data = TreeSitterData::new(&text);
             BufferSnapshot {
                 text,
                 syntax,
@@ -1198,10 +1198,10 @@ impl Buffer {
             buffer_id,
             Default::default(),
             Rope::new(),
-        )
-        .snapshot();
+        );
+        let text = text.into_snapshot();
         let syntax = SyntaxMap::new(&text).snapshot();
-        let tree_sitter_data = TreeSitterData::new(text.clone());
+        let tree_sitter_data = TreeSitterData::new(&text);
         BufferSnapshot {
             text,
             syntax,
@@ -1226,12 +1226,12 @@ impl Buffer {
         let buffer_id = entity_id.as_non_zero_u64().into();
         let text =
             TextBuffer::new_normalized(ReplicaId::LOCAL, buffer_id, Default::default(), text)
-                .snapshot();
+                .into_snapshot();
         let mut syntax = SyntaxMap::new(&text).snapshot();
         if let Some(language) = language.clone() {
             syntax.reparse(&text, language_registry, language);
         }
-        let tree_sitter_data = TreeSitterData::new(text.clone());
+        let tree_sitter_data = TreeSitterData::new(&text);
         BufferSnapshot {
             text,
             syntax,
@@ -1249,18 +1249,21 @@ impl Buffer {
     /// cheap, and allows reading from the buffer on a background thread.
     pub fn snapshot(&self) -> BufferSnapshot {
         let text = self.text.snapshot();
-        let mut syntax_map = self.syntax_map.lock();
-        syntax_map.interpolate(&text);
-        let syntax = syntax_map.snapshot();
+
+        let syntax = {
+            let mut syntax_map = self.syntax_map.lock();
+            syntax_map.interpolate(text);
+            syntax_map.snapshot()
+        };
 
         let tree_sitter_data = if self.text.version() != *self.tree_sitter_data.version() {
-            Arc::new(TreeSitterData::new(text.clone()))
+            Arc::new(TreeSitterData::new(text))
         } else {
             self.tree_sitter_data.clone()
         };
 
         BufferSnapshot {
-            text,
+            text: text.clone(),
             syntax,
             tree_sitter_data,
             file: self.file.clone(),
@@ -1304,7 +1307,7 @@ impl Buffer {
     ) -> Task<EditPreview> {
         let registry = self.language_registry();
         let language = self.language().cloned();
-        let old_snapshot = self.text.snapshot();
+        let old_snapshot = self.text.snapshot().clone();
         let mut branch_buffer = self.text.branch();
         let mut syntax_snapshot = self.syntax_map.lock().snapshot();
         cx.background_spawn(async move {
@@ -1323,7 +1326,7 @@ impl Buffer {
             }
             EditPreview {
                 old_snapshot,
-                applied_edits_snapshot: branch_buffer.snapshot(),
+                applied_edits_snapshot: branch_buffer.into_snapshot(),
                 syntax_snapshot,
             }
         })
@@ -1416,8 +1419,7 @@ impl Buffer {
         }
     }
 
-    #[cfg(test)]
-    pub(crate) fn as_text_snapshot(&self) -> &text::BufferSnapshot {
+    pub fn as_text_snapshot(&self) -> &text::BufferSnapshot {
         &self.text
     }
 
@@ -1425,7 +1427,8 @@ impl Buffer {
     /// language-related state like the syntax tree or diagnostics.
     #[ztracing::instrument(skip_all)]
     pub fn text_snapshot(&self) -> text::BufferSnapshot {
-        self.text.snapshot()
+        // todo lw
+        self.text.snapshot().clone()
     }
 
     /// The file associated with the buffer, if any.
@@ -1781,12 +1784,15 @@ impl Buffer {
         self.sync_parse_timeout = timeout;
     }
 
-    fn invalidate_tree_sitter_data(&mut self, snapshot: text::BufferSnapshot) {
-        match Arc::get_mut(&mut self.tree_sitter_data) {
+    fn invalidate_tree_sitter_data(
+        tree_sitter_data: &mut Arc<TreeSitterData>,
+        snapshot: &text::BufferSnapshot,
+    ) {
+        match Arc::get_mut(tree_sitter_data) {
             Some(tree_sitter_data) => tree_sitter_data.clear(snapshot),
             None => {
-                let tree_sitter_data = TreeSitterData::new(snapshot);
-                self.tree_sitter_data = Arc::new(tree_sitter_data)
+                let new_tree_sitter_data = TreeSitterData::new(snapshot);
+                *tree_sitter_data = Arc::new(new_tree_sitter_data)
             }
         }
     }
@@ -1817,7 +1823,7 @@ impl Buffer {
     #[ztracing::instrument(skip_all)]
     pub fn reparse(&mut self, cx: &mut Context<Self>, may_block: bool) {
         if self.text.version() != *self.tree_sitter_data.version() {
-            self.invalidate_tree_sitter_data(self.text.snapshot());
+            Self::invalidate_tree_sitter_data(&mut self.tree_sitter_data, self.text.snapshot());
         }
         if self.reparse.is_some() {
             return;
@@ -1898,7 +1904,7 @@ impl Buffer {
         self.was_changed();
         self.request_autoindent(cx, block_budget);
         self.parse_status.0.send(ParseStatus::Idle).unwrap();
-        self.invalidate_tree_sitter_data(self.text.snapshot());
+        Self::invalidate_tree_sitter_data(&mut self.tree_sitter_data, &self.text.snapshot());
         cx.emit(BufferEvent::Reparsed);
         cx.notify();
     }

crates/language/src/buffer/row_chunk.rs 🔗

@@ -31,7 +31,7 @@ impl std::fmt::Debug for RowChunks {
 }
 
 impl RowChunks {
-    pub fn new(snapshot: text::BufferSnapshot, max_rows_per_chunk: u32) -> Self {
+    pub fn new(snapshot: &text::BufferSnapshot, max_rows_per_chunk: u32) -> Self {
         let buffer_point_range = (0..snapshot.len()).to_point(&snapshot);
         let last_row = buffer_point_range.end.row;
         let chunks = (buffer_point_range.start.row..=last_row)

crates/language/src/syntax_map/syntax_map_tests.rs 🔗

@@ -1047,7 +1047,7 @@ fn test_random_edits(
     log::info!("initial text:\n{}", buffer.text());
 
     for _ in 0..operations {
-        let prev_buffer = buffer.snapshot();
+        let prev_buffer = buffer.snapshot().clone();
         let prev_syntax_map = syntax_map.snapshot();
 
         buffer.randomly_edit(&mut rng, 3);

crates/multi_buffer/src/multi_buffer.rs 🔗

@@ -678,7 +678,7 @@ impl std::hash::Hash for DiffTransformHunkInfo {
 #[derive(Clone)]
 pub struct ExcerptInfo {
     pub id: ExcerptId,
-    pub buffer: BufferSnapshot,
+    pub buffer: Arc<BufferSnapshot>,
     pub buffer_id: BufferId,
     pub range: ExcerptRange<text::Anchor>,
     pub end_row: MultiBufferRow,
@@ -739,7 +739,7 @@ struct Excerpt {
     /// The buffer being excerpted
     buffer_id: BufferId,
     /// A snapshot of the buffer being excerpted
-    buffer: BufferSnapshot,
+    buffer: Arc<BufferSnapshot>,
     /// The range of the buffer to be shown in the excerpt
     range: ExcerptRange<text::Anchor>,
     /// The last row in the excerpted slice of the buffer
@@ -1849,6 +1849,7 @@ impl MultiBuffer {
         };
 
         let mut excerpts = Vec::new();
+        let buffer_snapshot = Arc::new(buffer_snapshot);
         while let Some((id, range)) = ranges.next() {
             let locator = Locator::between(&prev_locator, &next_locator);
             if let Err(ix) = buffer_state.excerpts.binary_search(&locator) {
@@ -3013,6 +3014,21 @@ impl MultiBuffer {
         *has_deleted_file = false;
         *has_conflict = false;
 
+        if !diffs.is_empty() {
+            let mut diffs_to_add = Vec::new();
+            for (id, diff) in diffs {
+                if diff.is_inverted || buffer_diff.get(id).is_none() {
+                    if diffs_to_add.capacity() == 0 {
+                        // we'd rather overallocate than reallocate as buffer diffs are quite big
+                        // meaning re-allocations will be fairly expensive
+                        diffs_to_add.reserve(diffs.len());
+                    }
+                    diffs_to_add.push((*id, diff.snapshot(cx)));
+                }
+            }
+            buffer_diff.extend(diffs_to_add);
+        }
+
         let mut excerpts_to_edit = Vec::new();
         let mut non_text_state_updated = false;
         let mut edited = false;
@@ -3052,19 +3068,7 @@ impl MultiBuffer {
             *non_text_state_update_count += 1;
         }
 
-        let diffs_to_add = diffs
-            .iter()
-            .filter_map(|(id, diff)| {
-                if diff.is_inverted || buffer_diff.get(id).is_none() {
-                    Some((*id, diff.snapshot(cx)))
-                } else {
-                    None
-                }
-            })
-            .collect::<Vec<_>>();
-        buffer_diff.extend(diffs_to_add);
-
-        excerpts_to_edit.sort_unstable_by_key(|(locator, _, _)| *locator);
+        excerpts_to_edit.sort_unstable_by_key(|&(locator, _, _)| locator);
 
         let mut edits = Vec::new();
         let mut new_excerpts = SumTree::default();
@@ -3075,7 +3079,6 @@ impl MultiBuffer {
             let old_excerpt = cursor.item().unwrap();
             let buffer = buffer.read(cx);
             let buffer_id = buffer.remote_id();
-
             let mut new_excerpt;
             if buffer_edited {
                 edits.extend(
@@ -3103,13 +3106,13 @@ impl MultiBuffer {
                     old_excerpt.id,
                     locator.clone(),
                     buffer_id,
-                    buffer.snapshot(),
+                    Arc::new(buffer.snapshot()),
                     old_excerpt.range.clone(),
                     old_excerpt.has_trailing_newline,
                 );
             } else {
                 new_excerpt = old_excerpt.clone();
-                new_excerpt.buffer = buffer.snapshot();
+                new_excerpt.buffer = Arc::new(buffer.snapshot());
             }
 
             new_excerpts.push(new_excerpt, ());
@@ -3981,7 +3984,7 @@ impl MultiBufferSnapshot {
                 return None;
             }
             let excerpt = region.excerpt;
-            cursor.next_excerpt();
+            cursor.next_excerpt_forwards();
             Some(excerpt)
         })
     }
@@ -4308,7 +4311,7 @@ impl MultiBufferSnapshot {
                     {
                         return None;
                     }
-                    cursor.next_excerpt();
+                    cursor.next_excerpt_forwards();
                 }
             }
         })
@@ -4477,7 +4480,7 @@ impl MultiBufferSnapshot {
             self.excerpts
                 .iter()
                 .next()
-                .map(|e| (&e.id, e.buffer_id, &e.buffer))
+                .map(|e| (&e.id, e.buffer_id, &*e.buffer))
         } else {
             None
         }
@@ -5699,7 +5702,7 @@ impl MultiBufferSnapshot {
     ) -> impl Iterator<Item = (ExcerptId, &BufferSnapshot, ExcerptRange<text::Anchor>)> {
         self.excerpts
             .iter()
-            .map(|excerpt| (excerpt.id, &excerpt.buffer, excerpt.range.clone()))
+            .map(|excerpt| (excerpt.id, &*excerpt.buffer, excerpt.range.clone()))
     }
 
     fn cursor<'a, MBD, BD>(&'a self) -> MultiBufferCursor<'a, MBD, BD>
@@ -5778,16 +5781,18 @@ impl MultiBufferSnapshot {
         });
 
         if cursor
-            .region()
-            .is_some_and(|region| bounds.contains(&region.range.start.key))
+            .fetch_excerpt_with_range()
+            .is_some_and(|(_, range)| bounds.contains(&range.start.key))
         {
             cursor.prev_excerpt();
         } else {
             cursor.seek_to_start_of_current_excerpt();
         }
-        let mut prev_region = cursor.region().cloned();
+        let mut prev_region = cursor
+            .fetch_excerpt_with_range()
+            .map(|(excerpt, _)| excerpt);
 
-        cursor.next_excerpt();
+        cursor.next_excerpt_forwards();
 
         iter::from_fn(move || {
             loop {
@@ -5795,34 +5800,34 @@ impl MultiBufferSnapshot {
                     return None;
                 }
 
-                let next_region = cursor.region()?.clone();
-                cursor.next_excerpt();
-                if !bounds.contains(&next_region.range.start.key) {
-                    prev_region = Some(next_region);
+                let (next_excerpt, next_range) = cursor.fetch_excerpt_with_range()?;
+                cursor.next_excerpt_forwards();
+                if !bounds.contains(&next_range.start.key) {
+                    prev_region = Some(next_excerpt);
                     continue;
                 }
 
-                let next_region_start = next_region.range.start.value.unwrap();
-                let next_region_end = if let Some(region) = cursor.region() {
-                    region.range.start.value.unwrap()
+                let next_region_start = next_range.start.value.unwrap();
+                let next_region_end = if let Some((_, range)) = cursor.fetch_excerpt_with_range() {
+                    range.start.value.unwrap()
                 } else {
                     self.max_point()
                 };
 
                 let prev = prev_region.as_ref().map(|region| ExcerptInfo {
-                    id: region.excerpt.id,
-                    buffer: region.excerpt.buffer.clone(),
-                    buffer_id: region.excerpt.buffer_id,
-                    range: region.excerpt.range.clone(),
+                    id: region.id,
+                    buffer: region.buffer.clone(),
+                    buffer_id: region.buffer_id,
+                    range: region.range.clone(),
                     end_row: MultiBufferRow(next_region_start.row),
                 });
 
                 let next = ExcerptInfo {
-                    id: next_region.excerpt.id,
-                    buffer: next_region.excerpt.buffer.clone(),
-                    buffer_id: next_region.excerpt.buffer_id,
-                    range: next_region.excerpt.range.clone(),
-                    end_row: if next_region.excerpt.has_trailing_newline {
+                    id: next_excerpt.id,
+                    buffer: next_excerpt.buffer.clone(),
+                    buffer_id: next_excerpt.buffer_id,
+                    range: next_excerpt.range.clone(),
+                    end_row: if next_excerpt.has_trailing_newline {
                         MultiBufferRow(next_region_end.row - 1)
                     } else {
                         MultiBufferRow(next_region_end.row)
@@ -5831,7 +5836,7 @@ impl MultiBufferSnapshot {
 
                 let row = MultiBufferRow(next_region_start.row);
 
-                prev_region = Some(next_region);
+                prev_region = Some(next_excerpt);
 
                 return Some(ExcerptBoundary { row, prev, next });
             }
@@ -6048,7 +6053,7 @@ impl MultiBufferSnapshot {
             cursor.next();
             Some(line_indents.map(move |(buffer_row, indent)| {
                 let row = region_row + (buffer_row - region_buffer_row);
-                (MultiBufferRow(row), indent, region_buffer)
+                (MultiBufferRow(row), indent, region_buffer.as_ref())
             }))
         })
         .flatten()
@@ -6092,7 +6097,7 @@ impl MultiBufferSnapshot {
             cursor.prev();
             Some(line_indents.map(move |(buffer_row, indent)| {
                 let row = region_row + (buffer_row - region_buffer_row);
-                (MultiBufferRow(row), indent, region_buffer)
+                (MultiBufferRow(row), indent, region_buffer.as_ref())
             }))
         })
         .flatten()
@@ -7029,8 +7034,26 @@ where
 
     fn seek_to_start_of_current_excerpt(&mut self) {
         self.cached_region.take();
-        self.diff_transforms.seek(self.excerpts.start(), Bias::Left);
-        if self.diff_transforms.end().excerpt_dimension == *self.excerpts.start()
+
+        if self.diff_transforms.seek(self.excerpts.start(), Bias::Left)
+            && self.diff_transforms.start().excerpt_dimension < *self.excerpts.start()
+            && self.diff_transforms.next_item().is_some()
+        {
+            self.diff_transforms.next();
+        }
+    }
+
+    fn next_excerpt_forwards(&mut self) {
+        self.excerpts.next();
+        self.seek_to_start_of_current_excerpt_forward();
+    }
+
+    fn seek_to_start_of_current_excerpt_forward(&mut self) {
+        self.cached_region.take();
+
+        if self
+            .diff_transforms
+            .seek_forward(self.excerpts.start(), Bias::Left)
             && self.diff_transforms.start().excerpt_dimension < *self.excerpts.start()
             && self.diff_transforms.next_item().is_some()
         {
@@ -7191,10 +7214,10 @@ where
                 let mut end;
                 let mut buffer_end;
                 let has_trailing_newline;
-                if self.diff_transforms.end().excerpt_dimension < self.excerpts.end() {
-                    let overshoot =
-                        self.diff_transforms.end().excerpt_dimension - *self.excerpts.start();
-                    end = self.diff_transforms.end().output_dimension.0;
+                let transform_end = self.diff_transforms.end();
+                if transform_end.excerpt_dimension < self.excerpts.end() {
+                    let overshoot = transform_end.excerpt_dimension - *self.excerpts.start();
+                    end = transform_end.output_dimension.0;
                     buffer_end = buffer_context_start;
                     buffer_end += overshoot;
                     has_trailing_newline = false;
@@ -7228,6 +7251,38 @@ where
         }
     }
 
+    fn fetch_excerpt_with_range(&self) -> Option<(&'a Excerpt, Range<MBD>)> {
+        let excerpt = self.excerpts.item()?;
+        match self.diff_transforms.item()? {
+            &DiffTransform::DeletedHunk { .. } => {
+                let start = self.diff_transforms.start().output_dimension.0;
+                let end = self.diff_transforms.end().output_dimension.0;
+                Some((excerpt, start..end))
+            }
+            DiffTransform::BufferContent { .. } => {
+                let mut start = self.diff_transforms.start().output_dimension.0;
+                if self.diff_transforms.start().excerpt_dimension < *self.excerpts.start() {
+                    let overshoot =
+                        *self.excerpts.start() - self.diff_transforms.start().excerpt_dimension;
+                    start += overshoot;
+                }
+
+                let mut end;
+                let transform_end = self.diff_transforms.end();
+                if transform_end.excerpt_dimension < self.excerpts.end() {
+                    end = transform_end.output_dimension.0;
+                } else {
+                    let overshoot =
+                        self.excerpts.end() - self.diff_transforms.start().excerpt_dimension;
+                    end = self.diff_transforms.start().output_dimension.0;
+                    end += overshoot;
+                };
+
+                Some((excerpt, start..end))
+            }
+        }
+    }
+
     fn excerpt(&self) -> Option<&'a Excerpt> {
         self.excerpts.item()
     }
@@ -7238,7 +7293,7 @@ impl Excerpt {
         id: ExcerptId,
         locator: Locator,
         buffer_id: BufferId,
-        buffer: BufferSnapshot,
+        buffer: Arc<BufferSnapshot>,
         range: ExcerptRange<text::Anchor>,
         has_trailing_newline: bool,
     ) -> Self {

crates/project/src/lsp_store/inlay_hint_cache.rs 🔗

@@ -78,7 +78,7 @@ const MAX_ROWS_IN_A_CHUNK: u32 = 50;
 
 impl BufferInlayHints {
     pub fn new(buffer: &Entity<Buffer>, cx: &mut App) -> Self {
-        let chunks = RowChunks::new(buffer.read(cx).text_snapshot(), MAX_ROWS_IN_A_CHUNK);
+        let chunks = RowChunks::new(buffer.read(cx).as_text_snapshot(), MAX_ROWS_IN_A_CHUNK);
 
         Self {
             hints_by_chunks: vec![None; chunks.len()],

crates/sum_tree/src/tree_map.rs 🔗

@@ -83,9 +83,6 @@ impl<K: Clone + Ord, V: Clone> TreeMap<K, V> {
             .into_iter()
             .map(|(key, value)| Edit::Insert(MapEntry { key, value }))
             .collect();
-        if edits.is_empty() {
-            return;
-        }
         self.0.edit(edits, ());
     }
 

crates/text/src/text.rs 🔗

@@ -104,16 +104,16 @@ impl From<BufferId> for u64 {
 
 #[derive(Clone)]
 pub struct BufferSnapshot {
-    replica_id: ReplicaId,
-    remote_id: BufferId,
     visible_text: Rope,
     deleted_text: Rope,
-    line_ending: LineEnding,
-    undo_map: UndoMap,
     fragments: SumTree<Fragment>,
     insertions: SumTree<InsertionFragment>,
     insertion_slices: TreeSet<InsertionSlice>,
+    undo_map: UndoMap,
     pub version: clock::Global,
+    remote_id: BufferId,
+    replica_id: ReplicaId,
+    line_ending: LineEnding,
 }
 
 #[derive(Clone, Debug)]
@@ -780,8 +780,12 @@ impl Buffer {
         self.version.clone()
     }
 
-    pub fn snapshot(&self) -> BufferSnapshot {
-        self.snapshot.clone()
+    pub fn snapshot(&self) -> &BufferSnapshot {
+        &self.snapshot
+    }
+
+    pub fn into_snapshot(self) -> BufferSnapshot {
+        self.snapshot
     }
 
     pub fn branch(&self) -> Self {