push bufferdiff refactor through most of the workspace

Cole Miller and cameron created

Co-authored-by: cameron <cameron.studdstreet@gmail.com>

Change summary

crates/acp_thread/src/diff.rs                    | 109 ++++++++---------
crates/action_log/src/action_log.rs              |  53 ++++----
crates/agent_ui/src/agent_diff.rs                |   6 
crates/buffer_diff/src/buffer_diff.rs            |  64 +++++-----
crates/editor/src/editor.rs                      |   2 
crates/editor/src/editor_tests.rs                |  16 +
crates/editor/src/split.rs                       |   4 
crates/eval/src/example.rs                       |   7 
crates/git_ui/src/commit_view.rs                 |  39 ++---
crates/git_ui/src/file_diff_view.rs              |  55 +++++---
crates/git_ui/src/project_diff.rs                |   4 
crates/git_ui/src/text_diff_view.rs              |  16 +-
crates/language/src/buffer.rs                    |   2 
crates/multi_buffer/src/multi_buffer.rs          |  25 +--
crates/project/src/git_store.rs                  |   5 
crates/project/src/project_tests.rs              |  86 +++++++------
crates/remote_server/src/remote_editing_tests.rs |  35 +++--
crates/rope/src/chunk.rs                         |   1 
crates/zeta/src/rate_prediction_modal.rs         |  15 +-
19 files changed, 277 insertions(+), 267 deletions(-)

Detailed changes

crates/acp_thread/src/diff.rs 🔗

@@ -1,10 +1,10 @@
 use anyhow::Result;
-use buffer_diff::{BufferDiff, BufferDiffSnapshot};
+use buffer_diff::BufferDiff;
 use editor::{MultiBuffer, PathKey, multibuffer_context_lines};
 use gpui::{App, AppContext, AsyncApp, Context, Entity, Subscription, Task};
 use itertools::Itertools;
 use language::{
-    Anchor, Buffer, Capability, LanguageRegistry, OffsetRangeExt as _, Point, Rope, TextBuffer,
+    Anchor, Buffer, Capability, LanguageRegistry, OffsetRangeExt as _, Point, TextBuffer,
 };
 use std::{cmp::Reverse, ops::Range, path::Path, sync::Arc};
 use util::ResultExt;
@@ -49,15 +49,15 @@ impl Diff {
                     .update(cx, |multibuffer, cx| {
                         let hunk_ranges = {
                             let buffer = buffer.read(cx);
-                            let diff = diff.read(cx);
-                            diff.hunks_intersecting_range(
-                                Anchor::min_for_buffer(buffer.remote_id())
-                                    ..Anchor::max_for_buffer(buffer.remote_id()),
-                                buffer,
-                                cx,
-                            )
-                            .map(|diff_hunk| diff_hunk.buffer_range.to_point(buffer))
-                            .collect::<Vec<_>>()
+                            diff.read(cx)
+                                .snapshot(cx)
+                                .hunks_intersecting_range(
+                                    Anchor::min_for_buffer(buffer.remote_id())
+                                        ..Anchor::max_for_buffer(buffer.remote_id()),
+                                    buffer,
+                                )
+                                .map(|diff_hunk| diff_hunk.buffer_range.to_point(buffer))
+                                .collect::<Vec<_>>()
                         };
 
                         multibuffer.set_excerpts_for_path(
@@ -168,7 +168,7 @@ impl Diff {
                 new_buffer,
                 ..
             }) => {
-                base_text.as_str() != old_text
+                base_text.as_ref() != old_text
                     || !new_buffer.read(cx).as_rope().chunks().equals_str(new_text)
             }
             Diff::Finalized(FinalizedDiff {
@@ -176,7 +176,7 @@ impl Diff {
                 new_buffer,
                 ..
             }) => {
-                base_text.as_str() != old_text
+                base_text.as_ref() != old_text
                     || !new_buffer.read(cx).as_rope().chunks().equals_str(new_text)
             }
         }
@@ -200,21 +200,22 @@ impl PendingDiff {
         let base_text = self.base_text.clone();
         self.update_diff = cx.spawn(async move |diff, cx| {
             let text_snapshot = buffer.read_with(cx, |buffer, _| buffer.text_snapshot())?;
-            let diff_snapshot = BufferDiff::update_diff(
-                buffer_diff.clone(),
-                text_snapshot.clone(),
-                Some(base_text),
-                false,
-                false,
-                None,
-                None,
-                cx,
-            )
-            .await?;
+            let language = buffer.read_with(cx, |buffer, _| buffer.language().cloned())?;
+            let update = buffer_diff
+                .update(cx, |diff, cx| {
+                    diff.update_diff(
+                        text_snapshot.clone(),
+                        Some(base_text.clone()),
+                        false,
+                        language,
+                        cx,
+                    )
+                })?
+                .await;
             buffer_diff.update(cx, |diff, cx| {
-                diff.set_snapshot(diff_snapshot.clone(), &text_snapshot, cx);
+                diff.set_snapshot(update.clone(), &text_snapshot, false, cx);
                 diff.secondary_diff().unwrap().update(cx, |diff, cx| {
-                    diff.set_snapshot(diff_snapshot.clone(), &text_snapshot, cx);
+                    diff.set_snapshot(update, &text_snapshot, false, cx);
                 });
             })?;
             diff.update(cx, |diff, cx| {
@@ -311,13 +312,14 @@ impl PendingDiff {
 
     fn excerpt_ranges(&self, cx: &App) -> Vec<Range<Point>> {
         let buffer = self.new_buffer.read(cx);
-        let diff = self.diff.read(cx);
-        let mut ranges = diff
+        let mut ranges = self
+            .diff
+            .read(cx)
+            .snapshot(cx)
             .hunks_intersecting_range(
                 Anchor::min_for_buffer(buffer.remote_id())
                     ..Anchor::max_for_buffer(buffer.remote_id()),
                 buffer,
-                cx,
             )
             .map(|diff_hunk| diff_hunk.buffer_range.to_point(buffer))
             .collect::<Vec<_>>();
@@ -349,60 +351,47 @@ impl PendingDiff {
 
 pub struct FinalizedDiff {
     path: String,
-    base_text: Arc<String>,
+    base_text: Arc<str>,
     new_buffer: Entity<Buffer>,
     multibuffer: Entity<MultiBuffer>,
     _update_diff: Task<Result<()>>,
 }
 
 async fn build_buffer_diff(
-    old_text: Arc<String>,
+    old_text: Arc<str>,
     buffer: &Entity<Buffer>,
     language_registry: Option<Arc<LanguageRegistry>>,
     cx: &mut AsyncApp,
 ) -> Result<Entity<BufferDiff>> {
+    let language = cx.update(|cx| buffer.read(cx).language().cloned())?;
     let buffer = cx.update(|cx| buffer.read(cx).snapshot())?;
 
-    let old_text_rope = cx
-        .background_spawn({
-            let old_text = old_text.clone();
-            async move { Rope::from(old_text.as_str()) }
-        })
-        .await;
-    let base_buffer = cx
-        .update(|cx| {
-            Buffer::build_snapshot(
-                old_text_rope,
-                buffer.language().cloned(),
-                language_registry,
-                cx,
-            )
-        })?
-        .await;
+    let secondary_diff = cx.new(|cx| BufferDiff::new(&buffer, cx))?;
 
-    let diff_snapshot = cx
-        .update(|cx| {
-            BufferDiffSnapshot::new_with_base_buffer(
+    let update = secondary_diff
+        .update(cx, |secondary_diff, cx| {
+            secondary_diff.update_diff(
                 buffer.text.clone(),
                 Some(old_text),
-                base_buffer,
+                true,
+                language.clone(),
                 cx,
             )
         })?
         .await;
 
-    let secondary_diff = cx.new(|cx| {
-        let mut diff = BufferDiff::new(&buffer, cx);
-        diff.set_snapshot(diff_snapshot.clone(), &buffer, cx);
-        diff
+    secondary_diff.update(cx, |secondary_diff, cx| {
+        secondary_diff.language_changed(language.clone(), language_registry.clone(), cx);
+        secondary_diff.set_snapshot(update.clone(), &buffer, true, cx);
     })?;
 
-    cx.new(|cx| {
-        let mut diff = BufferDiff::new(&buffer.text, cx);
-        diff.set_snapshot(diff_snapshot, &buffer, cx);
+    let diff = cx.new(|cx| BufferDiff::new(&buffer, cx))?;
+    diff.update(cx, |diff, cx| {
+        diff.language_changed(language, language_registry, cx);
+        diff.set_snapshot(update.clone(), &buffer, true, cx);
         diff.set_secondary_diff(secondary_diff);
-        diff
-    })
+    })?;
+    Ok(diff)
 }
 
 #[cfg(test)]

crates/action_log/src/action_log.rs 🔗

@@ -262,7 +262,7 @@ impl ActionLog {
                         );
                     }
 
-                    (Arc::new(base_text.to_string()), base_text)
+                    (Arc::from(base_text.to_string().as_str()), base_text)
                 }
             });
 
@@ -302,7 +302,7 @@ impl ActionLog {
                     .context("buffer not tracked")?;
                 let old_unreviewed_edits = tracked_buffer.unreviewed_edits.clone();
                 let agent_diff_base = tracked_buffer.diff_base.clone();
-                let git_diff_base = git_diff.read(cx).base_text().as_rope().clone();
+                let git_diff_base = git_diff.read(cx).base_text(cx).as_rope().clone();
                 let buffer_text = tracked_buffer.snapshot.as_rope().clone();
                 anyhow::Ok(cx.background_spawn(async move {
                     let mut old_unreviewed_edits = old_unreviewed_edits.into_iter().peekable();
@@ -352,7 +352,7 @@ impl ActionLog {
                     }
 
                     (
-                        Arc::new(new_agent_diff_base.to_string()),
+                        Arc::from(new_agent_diff_base.to_string().as_str()),
                         new_agent_diff_base,
                     )
                 }))
@@ -374,11 +374,11 @@ impl ActionLog {
         this: &WeakEntity<ActionLog>,
         buffer: &Entity<Buffer>,
         buffer_snapshot: text::BufferSnapshot,
-        new_base_text: Arc<String>,
+        new_base_text: Arc<str>,
         new_diff_base: Rope,
         cx: &mut AsyncApp,
     ) -> Result<()> {
-        let (diff, language, language_registry) = this.read_with(cx, |this, cx| {
+        let (diff, language) = this.read_with(cx, |this, cx| {
             let tracked_buffer = this
                 .tracked_buffers
                 .get(buffer)
@@ -386,25 +386,28 @@ impl ActionLog {
             anyhow::Ok((
                 tracked_buffer.diff.clone(),
                 buffer.read(cx).language().cloned(),
-                buffer.read(cx).language_registry(),
             ))
         })??;
-        let diff_snapshot = BufferDiff::update_diff(
-            diff.clone(),
-            buffer_snapshot.clone(),
-            Some(new_base_text),
-            true,
-            false,
-            language,
-            language_registry,
-            cx,
-        )
-        .await;
+        let update = diff.update(cx, |diff, cx| {
+            diff.update_diff(
+                buffer_snapshot.clone(),
+                Some(new_base_text),
+                true,
+                language,
+                cx,
+            )
+        });
         let mut unreviewed_edits = Patch::default();
-        if let Ok(diff_snapshot) = diff_snapshot {
+        if let Ok(update) = update {
+            let update = update.await;
+
+            let diff_snapshot = diff.update(cx, |diff, cx| {
+                diff.set_snapshot(update.clone(), &buffer_snapshot, true, cx);
+                diff.snapshot(cx)
+            })?;
+
             unreviewed_edits = cx
                 .background_spawn({
-                    let diff_snapshot = diff_snapshot.clone();
                     let buffer_snapshot = buffer_snapshot.clone();
                     let new_diff_base = new_diff_base.clone();
                     async move {
@@ -431,10 +434,6 @@ impl ActionLog {
                     }
                 })
                 .await;
-
-            diff.update(cx, |diff, cx| {
-                diff.set_snapshot(diff_snapshot, &buffer_snapshot, cx);
-            })?;
         }
         this.update(cx, |this, cx| {
             let tracked_buffer = this
@@ -975,7 +974,8 @@ impl TrackedBuffer {
     fn has_edits(&self, cx: &App) -> bool {
         self.diff
             .read(cx)
-            .hunks(self.buffer.read(cx), cx)
+            .snapshot(cx)
+            .hunks(self.buffer.read(cx))
             .next()
             .is_some()
     }
@@ -2388,13 +2388,14 @@ mod tests {
                     (
                         buffer,
                         diff.read(cx)
-                            .hunks(&snapshot, cx)
+                            .snapshot(cx)
+                            .hunks(&snapshot)
                             .map(|hunk| HunkStatus {
                                 diff_status: hunk.status().kind,
                                 range: hunk.range,
                                 old_text: diff
                                     .read(cx)
-                                    .base_text()
+                                    .base_text(cx)
                                     .text_for_range(hunk.diff_base_byte_range)
                                     .collect(),
                             })

crates/agent_ui/src/agent_diff.rs 🔗

@@ -141,13 +141,13 @@ impl AgentDiffPane {
             paths_to_delete.remove(&path_key);
 
             let snapshot = buffer.read(cx).snapshot();
-            let diff = diff_handle.read(cx);
 
-            let diff_hunk_ranges = diff
+            let diff_hunk_ranges = diff_handle
+                .read(cx)
+                .snapshot(cx)
                 .hunks_intersecting_range(
                     language::Anchor::min_max_range_for_buffer(snapshot.remote_id()),
                     &snapshot,
-                    cx,
                 )
                 .map(|diff_hunk| diff_hunk.buffer_range.to_point(&snapshot))
                 .collect::<Vec<_>>();

crates/buffer_diff/src/buffer_diff.rs 🔗

@@ -1,14 +1,13 @@
 use futures::channel::oneshot;
 use git2::{DiffLineType as GitDiffLineType, DiffOptions as GitOptions, Patch as GitPatch};
-use gpui::{App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Task, TaskLabel};
+use gpui::{App, AppContext as _, Context, Entity, EventEmitter, Task, TaskLabel};
 use language::{
-    BufferRow, DiffOptions, File, Language, LanguageName, LanguageRegistry,
+    BufferRow, Capability, DiffOptions, File, Language, LanguageName, LanguageRegistry,
     language_settings::language_settings, word_diff_ranges,
 };
 use rope::Rope;
 use std::{
     cmp::Ordering,
-    future::Future,
     iter,
     ops::Range,
     sync::{Arc, LazyLock},
@@ -1051,10 +1050,16 @@ impl EventEmitter<BufferDiffEvent> for BufferDiff {}
 
 impl BufferDiff {
     pub fn new(buffer: &text::BufferSnapshot, cx: &mut App) -> Self {
+        let base_text = cx.new(|cx| {
+            let mut buffer = language::Buffer::local("", cx);
+            buffer.set_capability(Capability::ReadOnly, cx);
+            buffer
+        });
+
         BufferDiff {
             buffer_id: buffer.remote_id(),
             inner: BufferDiffInner {
-                base_text: cx.new(|cx| language::Buffer::local("", cx)),
+                base_text,
                 hunks: SumTree::new(buffer),
                 pending_hunks: SumTree::new(buffer),
                 base_text_exists: false,
@@ -1065,10 +1070,16 @@ impl BufferDiff {
 
     pub fn new_unchanged(buffer: &text::BufferSnapshot, cx: &mut Context<Self>) -> Self {
         let base_text = buffer.text();
+        let base_text = cx.new(|cx| {
+            let mut buffer = language::Buffer::local(base_text, cx);
+            buffer.set_capability(Capability::ReadOnly, cx);
+            buffer
+        });
+
         BufferDiff {
             buffer_id: buffer.remote_id(),
             inner: BufferDiffInner {
-                base_text: cx.new(|cx| language::Buffer::local(base_text, cx)),
+                base_text,
                 hunks: SumTree::new(buffer),
                 pending_hunks: SumTree::new(buffer),
                 base_text_exists: false,
@@ -1193,11 +1204,6 @@ impl BufferDiff {
                     diff_options,
                 );
                 let base_text = base_text.unwrap_or_default();
-                if cfg!(debug_assertions) {
-                    for hunk in hunks.iter() {
-                        base_text.get(hunk.diff_base_byte_range.clone()).unwrap();
-                    }
-                }
                 BufferDiffInner {
                     base_text,
                     hunks,
@@ -1207,7 +1213,18 @@ impl BufferDiff {
             })
     }
 
-    pub fn language_changed(&mut self, cx: &mut Context<Self>) {
+    pub fn language_changed(
+        &mut self,
+        language: Option<Arc<Language>>,
+        language_registry: Option<Arc<LanguageRegistry>>,
+        cx: &mut Context<Self>,
+    ) {
+        self.inner.base_text.update(cx, |base_text, cx| {
+            base_text.set_language(language, cx);
+            if let Some(language_registry) = language_registry {
+                base_text.set_language_registry(language_registry);
+            }
+        });
         cx.emit(BufferDiffEvent::LanguageChanged);
     }
 
@@ -1234,6 +1251,7 @@ impl BufferDiff {
     ) -> Option<Range<Anchor>> {
         log::debug!("set snapshot with secondary {secondary_diff_change:?}");
 
+        dbg!(base_text_changed);
         let old_snapshot = self.snapshot(cx);
         let state = &mut self.inner;
         let (mut changed_range, mut base_text_changed_range) =
@@ -1269,32 +1287,14 @@ impl BufferDiff {
 
         let state = &mut self.inner;
         state.base_text_exists = new_state.base_text_exists;
-        if cfg!(debug_assertions) {
-            for hunk in new_state.hunks.iter() {
-                new_state.base_text.get(hunk.diff_base_byte_range.clone());
-            }
-        }
         if base_text_changed {
             state.base_text.update(cx, |base_text, cx| {
-                base_text.set_text(dbg!(new_state.base_text.clone()), cx);
+                base_text.set_capability(Capability::ReadWrite, cx);
+                base_text.set_text(new_state.base_text.clone(), cx);
+                base_text.set_capability(Capability::ReadOnly, cx);
             })
         }
         state.hunks = new_state.hunks;
-        if cfg!(debug_assertions) {
-            pretty_assertions::assert_eq!(
-                state.base_text.read(cx).snapshot().text().as_str(),
-                new_state.base_text.as_ref()
-            );
-            for hunk in state.hunks.iter() {
-                state
-                    .base_text
-                    .read(cx)
-                    .snapshot()
-                    .text_summary_for_range::<text::TextSummary, _>(
-                        hunk.diff_base_byte_range.clone(),
-                    );
-            }
-        }
         if base_text_changed || clear_pending_hunks {
             if let Some((first, last)) = state.pending_hunks.first().zip(state.pending_hunks.last())
             {

crates/editor/src/editor.rs 🔗

@@ -11412,7 +11412,7 @@ impl Editor {
         let buffer = buffer.read(cx);
         let original_text = diff
             .read(cx)
-            .base_text()
+            .base_text(cx)
             .as_rope()
             .slice(hunk.diff_base_byte_range.start.0..hunk.diff_base_byte_range.end.0);
         let buffer_snapshot = buffer.snapshot();

crates/editor/src/editor_tests.rs 🔗

@@ -19556,7 +19556,9 @@ async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
             (buffer_2.clone(), base_text_2),
             (buffer_3.clone(), base_text_3),
         ] {
-            let diff = cx.new(|cx| BufferDiff::new_with_base_text(diff_base, &buffer, cx));
+            let diff = cx.new(|cx| {
+                BufferDiff::new_with_base_text(diff_base, &buffer.read(cx).text_snapshot(), cx)
+            });
             editor
                 .buffer
                 .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
@@ -20181,7 +20183,9 @@ async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
                 (buffer_2.clone(), file_2_old),
                 (buffer_3.clone(), file_3_old),
             ] {
-                let diff = cx.new(|cx| BufferDiff::new_with_base_text(diff_base, &buffer, cx));
+                let diff = cx.new(|cx| {
+                    BufferDiff::new_with_base_text(diff_base, &buffer.read(cx).text_snapshot(), cx)
+                });
                 editor
                     .buffer
                     .update(cx, |buffer, cx| buffer.add_diff(diff, cx));
@@ -20287,7 +20291,9 @@ async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) {
         cx.add_window(|window, cx| Editor::new(EditorMode::full(), multi_buffer, None, window, cx));
     editor
         .update(cx, |editor, _window, cx| {
-            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base, &buffer, cx));
+            let diff = cx.new(|cx| {
+                BufferDiff::new_with_base_text(base, &buffer.read(cx).text_snapshot(), cx)
+            });
             editor
                 .buffer
                 .update(cx, |buffer, cx| buffer.add_diff(diff, cx))
@@ -21631,7 +21637,9 @@ async fn test_indent_guide_with_expanded_diff_hunks(cx: &mut TestAppContext) {
 
         editor.buffer().update(cx, |multibuffer, cx| {
             let buffer = multibuffer.as_singleton().unwrap();
-            let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
+            let diff = cx.new(|cx| {
+                BufferDiff::new_with_base_text(base_text, &buffer.read(cx).text_snapshot(), cx)
+            });
 
             multibuffer.set_all_diff_hunks_expanded(cx);
             multibuffer.add_diff(diff, cx);

crates/editor/src/split.rs 🔗

@@ -440,7 +440,9 @@ mod tests {
             HELLO!
         "};
         let buffer = cx.new(|cx| Buffer::local(buffer_text, cx));
-        let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
+        let diff = cx.new(|cx| {
+            BufferDiff::new_with_base_text(base_text, &buffer.read(cx).text_snapshot(), cx)
+        });
         let project = Project::test(FakeFs::new(cx.executor()), [], cx).await;
         let (workspace, cx) =
             cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));

crates/eval/src/example.rs 🔗

@@ -366,11 +366,12 @@ impl ExampleContext {
                         let snapshot = buffer.read(cx).snapshot();
 
                         let file = snapshot.file().unwrap();
-                        let diff = diff.read(cx);
-                        let base_text = diff.base_text().text();
+                        let base_text = diff.read(cx).base_text(cx).text();
 
                         let hunks = diff
-                            .hunks(&snapshot, cx)
+                            .read(cx)
+                            .snapshot(cx)
+                            .hunks(&snapshot)
                             .map(|hunk| FileEditHunk {
                                 base_text: base_text[hunk.diff_base_byte_range.clone()].to_string(),
                                 text: snapshot

crates/git_ui/src/commit_view.rs 🔗

@@ -1,5 +1,5 @@
 use anyhow::{Context as _, Result};
-use buffer_diff::{BufferDiff, BufferDiffSnapshot};
+use buffer_diff::{BufferDiff};
 use editor::{Addon, Editor, EditorEvent, MultiBuffer};
 use git::repository::{CommitDetails, CommitDiff, RepoPath};
 use git::{GitHostingProviderRegistry, GitRemote, parse_git_remote_url};
@@ -195,7 +195,8 @@ impl CommitView {
                         let snapshot = buffer.read(cx).snapshot();
                         let path = snapshot.file().unwrap().path().clone();
 
-                        let hunks: Vec<_> = buffer_diff.read(cx).hunks(&snapshot, cx).collect();
+                        let hunks: Vec<_> =
+                            buffer_diff.read(cx).snapshot(cx).hunks(&snapshot).collect();
 
                         let excerpt_ranges = if hunks.is_empty() {
                             vec![language::Point::zero()..snapshot.max_point()]
@@ -796,35 +797,29 @@ async fn build_buffer_diff(
         LineEnding::normalize(old_text);
     }
 
+    let language = cx.update(|cx| buffer.read(cx).language().cloned())?;
     let buffer = cx.update(|cx| buffer.read(cx).snapshot())?;
 
-    let base_buffer = cx
-        .update(|cx| {
-            Buffer::build_snapshot(
-                old_text.as_deref().unwrap_or("").into(),
-                buffer.language().cloned(),
-                Some(language_registry.clone()),
-                cx,
-            )
-        })?
-        .await;
+    let diff = cx.new(|cx| BufferDiff::new(&buffer.text, cx))?;
 
-    let diff_snapshot = cx
-        .update(|cx| {
-            BufferDiffSnapshot::new_with_base_buffer(
+    let update = diff
+        .update(cx, |diff, cx| {
+            diff.update_diff(
                 buffer.text.clone(),
-                old_text.map(Arc::new),
-                base_buffer,
+                old_text.map(|old_text| Arc::from(old_text.as_str())),
+                true,
+                language.clone(),
                 cx,
             )
         })?
         .await;
 
-    cx.new(|cx| {
-        let mut diff = BufferDiff::new(&buffer.text, cx);
-        diff.set_snapshot(diff_snapshot, &buffer.text, cx);
-        diff
-    })
+    diff.update(cx, |diff, cx| {
+        diff.language_changed(language, Some(language_registry.clone()), cx);
+        diff.set_snapshot(update, &buffer.text, true, cx)
+    }).ok();
+
+    Ok(diff)
 }
 
 impl EventEmitter<EditorEvent> for CommitView {}

crates/git_ui/src/file_diff_view.rs 🔗

@@ -1,14 +1,14 @@
 //! FileDiffView provides a UI for displaying differences between two buffers.
 
 use anyhow::Result;
-use buffer_diff::{BufferDiff, BufferDiffSnapshot};
+use buffer_diff::BufferDiff;
 use editor::{Editor, EditorEvent, MultiBuffer};
 use futures::{FutureExt, select_biased};
 use gpui::{
     AnyElement, App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, FocusHandle,
     Focusable, IntoElement, Render, Task, Window,
 };
-use language::Buffer;
+use language::{Buffer, LanguageRegistry};
 use project::Project;
 use std::{
     any::{Any, TypeId},
@@ -52,8 +52,9 @@ impl FileDiffView {
             let new_buffer = project
                 .update(cx, |project, cx| project.open_local_buffer(&new_path, cx))?
                 .await?;
+            let languages = project.update(cx, |project, _| project.languages().clone())?;
 
-            let buffer_diff = build_buffer_diff(&old_buffer, &new_buffer, cx).await?;
+            let buffer_diff = build_buffer_diff(&old_buffer, &new_buffer, languages, cx).await?;
 
             workspace.update_in(cx, |workspace, window, cx| {
                 let diff_view = cx.new(|cx| {
@@ -143,19 +144,16 @@ impl FileDiffView {
                             this.new_buffer.read(cx).snapshot(),
                         )
                     })?;
-                    let diff_snapshot = cx
-                        .update(|cx| {
-                            BufferDiffSnapshot::new_with_base_buffer(
-                                new_snapshot.text.clone(),
-                                Some(old_snapshot.text().into()),
-                                old_snapshot,
-                                cx,
-                            )
-                        })?
-                        .await;
                     diff.update(cx, |diff, cx| {
-                        diff.set_snapshot(diff_snapshot, &new_snapshot, cx)
-                    })?;
+                        diff.set_base_text(
+                            Some(old_snapshot.text().as_str().into()),
+                            old_snapshot.language().cloned(),
+                            new_snapshot.text.clone(),
+                            cx,
+                        )
+                    })?
+                    .await
+                    .ok();
                     log::trace!("finish recalculating");
                 }
                 Ok(())
@@ -167,27 +165,36 @@ impl FileDiffView {
 async fn build_buffer_diff(
     old_buffer: &Entity<Buffer>,
     new_buffer: &Entity<Buffer>,
+    language_registry: Arc<LanguageRegistry>,
     cx: &mut AsyncApp,
 ) -> Result<Entity<BufferDiff>> {
     let old_buffer_snapshot = old_buffer.read_with(cx, |buffer, _| buffer.snapshot())?;
     let new_buffer_snapshot = new_buffer.read_with(cx, |buffer, _| buffer.snapshot())?;
 
-    let diff_snapshot = cx
-        .update(|cx| {
-            BufferDiffSnapshot::new_with_base_buffer(
+    let diff = cx.new(|cx| BufferDiff::new(&new_buffer_snapshot.text, cx))?;
+
+    let update = diff
+        .update(cx, |diff, cx| {
+            diff.update_diff(
                 new_buffer_snapshot.text.clone(),
                 Some(old_buffer_snapshot.text().into()),
-                old_buffer_snapshot,
+                true,
+                new_buffer_snapshot.language().cloned(),
                 cx,
             )
         })?
         .await;
 
-    cx.new(|cx| {
-        let mut diff = BufferDiff::new(&new_buffer_snapshot.text, cx);
-        diff.set_snapshot(diff_snapshot, &new_buffer_snapshot.text, cx);
-        diff
-    })
+    diff.update(cx, |diff, cx| {
+        diff.language_changed(
+            new_buffer_snapshot.language().cloned(),
+            Some(language_registry),
+            cx,
+        );
+        diff.set_snapshot(update, &new_buffer_snapshot.text, true, cx);
+    })?;
+
+    Ok(diff)
 }
 
 impl EventEmitter<EditorEvent> for FileDiffView {}

crates/git_ui/src/project_diff.rs 🔗

@@ -498,11 +498,11 @@ impl ProjectDiff {
 
         let snapshot = buffer.read(cx).snapshot();
         let diff_read = diff.read(cx);
-        let diff_hunk_ranges = diff_read
+        let diff_snapshot = diff_read.snapshot(cx);
+        let diff_hunk_ranges = diff_snapshot
             .hunks_intersecting_range(
                 Anchor::min_max_range_for_buffer(diff_read.buffer_id),
                 &snapshot,
-                cx,
             )
             .map(|diff_hunk| diff_hunk.buffer_range);
         let conflicts = conflict_addon

crates/git_ui/src/text_diff_view.rs 🔗

@@ -1,7 +1,7 @@
 //! TextDiffView currently provides a UI for displaying differences between the clipboard and selected text.
 
 use anyhow::Result;
-use buffer_diff::{BufferDiff, BufferDiffSnapshot};
+use buffer_diff::BufferDiff;
 use editor::{Editor, EditorEvent, MultiBuffer, ToPoint, actions::DiffClipboardWithSelectionData};
 use futures::{FutureExt, select_biased};
 use gpui::{
@@ -257,23 +257,25 @@ async fn update_diff_buffer(
     cx: &mut AsyncApp,
 ) -> Result<()> {
     let source_buffer_snapshot = source_buffer.read_with(cx, |buffer, _| buffer.snapshot())?;
+    let language = source_buffer_snapshot.language().cloned();
 
     let base_buffer_snapshot = clipboard_buffer.read_with(cx, |buffer, _| buffer.snapshot())?;
     let base_text = base_buffer_snapshot.text();
 
-    let diff_snapshot = cx
-        .update(|cx| {
-            BufferDiffSnapshot::new_with_base_buffer(
+    let update = diff
+        .update(cx, |diff, cx| {
+            diff.update_diff(
                 source_buffer_snapshot.text.clone(),
-                Some(Arc::new(base_text)),
-                base_buffer_snapshot,
+                Some(Arc::from(base_text.as_str())),
+                true,
+                language,
                 cx,
             )
         })?
         .await;
 
     diff.update(cx, |diff, cx| {
-        diff.set_snapshot(diff_snapshot, &source_buffer_snapshot.text, cx);
+        diff.set_snapshot(update, &source_buffer_snapshot.text, true, cx);
     })?;
     Ok(())
 }

crates/language/src/buffer.rs 🔗

@@ -3465,7 +3465,7 @@ impl BufferSnapshot {
     /// returned in chunks where each chunk has a single syntax highlighting style and
     /// diagnostic status.
     pub fn chunks<T: ToOffset>(&self, range: Range<T>, language_aware: bool) -> BufferChunks<'_> {
-        let range = dbg!(range.start.to_offset(self))..range.end.to_offset(self);
+        let range = range.start.to_offset(self)..range.end.to_offset(self);
 
         let mut syntax = None;
         if language_aware {

crates/multi_buffer/src/multi_buffer.rs 🔗

@@ -2286,6 +2286,7 @@ impl MultiBuffer {
             diff: diff.snapshot(cx),
             main_buffer: None,
         };
+        dbg!();
         self.snapshot.get_mut().diffs.insert(buffer_id, diff);
     }
 
@@ -2327,7 +2328,6 @@ impl MultiBuffer {
         let buffer = buffer_state.buffer.read(cx);
         let diff_change_range = range.to_offset(buffer);
 
-        dbg!();
         let new_diff = DiffStateSnapshot {
             diff: diff.snapshot(cx),
             main_buffer: None,
@@ -2338,6 +2338,7 @@ impl MultiBuffer {
             .get(&buffer_id)
             .is_none_or(|old_diff| !new_diff.base_texts_eq(old_diff));
 
+        dbg!();
         snapshot.diffs.insert_or_replace(buffer_id, new_diff);
 
         let mut excerpt_edits = Vec::new();
@@ -2376,7 +2377,7 @@ impl MultiBuffer {
             &mut snapshot,
             excerpt_edits,
             DiffChangeKind::DiffUpdated {
-                base_changed: dbg!(base_text_changed),
+                base_changed: base_text_changed,
             },
         );
         if !edits.is_empty() {
@@ -3178,7 +3179,6 @@ impl MultiBuffer {
             let edit_new_start =
                 MultiBufferOffset((edit_old_start.0 as isize + output_delta) as usize);
 
-            dbg!();
             let changed_diff_hunks = Self::recompute_diff_transforms_for_edit(
                 &edit,
                 &mut excerpts,
@@ -3314,6 +3314,7 @@ impl MultiBuffer {
             // Recompute the expanded hunks in the portion of the excerpt that
             // intersects the edit.
             if let Some(diff) = snapshot.diffs.get(&excerpt.buffer_id) {
+                dbg!();
                 let buffer = &excerpt.buffer;
                 let excerpt_start = *excerpts.start();
                 let excerpt_end = excerpt_start + excerpt.text_summary.len;
@@ -3417,19 +3418,13 @@ impl MultiBuffer {
                                 excerpt.id
                             );
 
-                            if !hunk.diff_base_byte_range.is_empty()
+                            if dbg!(!hunk.diff_base_byte_range.is_empty())
                                 && hunk_buffer_range.start >= edit_buffer_start
                                 && hunk_buffer_range.start <= excerpt_buffer_end
-                                && snapshot.show_deleted_hunks
+                                && dbg!(snapshot.show_deleted_hunks)
                             {
-                                dbg!(&hunk.diff_base_byte_range);
+                                dbg!();
                                 let base_text = diff.base_text();
-                                if cfg!(debug_assertions) {
-                                    dbg!();
-                                    base_text.text_summary_for_range::<TextSummary, _>(
-                                        hunk.diff_base_byte_range.clone(),
-                                    );
-                                }
                                 let mut text_cursor =
                                     base_text.as_rope().cursor(hunk.diff_base_byte_range.start);
                                 let mut base_text_summary = text_cursor
@@ -7856,7 +7851,6 @@ impl<'a> Iterator for MultiBufferChunks<'a> {
                 has_trailing_newline,
                 ..
             } => {
-                dbg!(&base_text_byte_range);
                 let base_text_start =
                     base_text_byte_range.start + (self.range.start - diff_transform_start);
                 let base_text_end =
@@ -7875,10 +7869,7 @@ impl<'a> Iterator for MultiBufferChunks<'a> {
                     chunks
                 } else {
                     let base_buffer = &self.diffs.get(buffer_id)?.base_text();
-                    base_buffer.chunks(
-                        dbg!(base_text_start)..dbg!(base_text_end),
-                        self.language_aware,
-                    )
+                    base_buffer.chunks(base_text_start..base_text_end, self.language_aware)
                 };
 
                 let chunk = if let Some(chunk) = chunks.next() {

crates/project/src/git_store.rs 🔗

@@ -819,6 +819,7 @@ impl GitStore {
 
             cx.subscribe(&diff, Self::on_buffer_diff_event).detach();
             diff_state.update(cx, |diff_state, cx| {
+                diff_state.language_changed = true;
                 diff_state.language = language;
                 diff_state.language_registry = language_registry;
 
@@ -3119,7 +3120,7 @@ impl BufferGitState {
             {
                 unstaged_diff.update(cx, |diff, cx| {
                     if language_changed {
-                        diff.language_changed(cx);
+                        diff.language_changed(language.clone(), language_registry.clone(), cx);
                     }
                     diff.set_snapshot(new_unstaged_diff, &buffer, index_changed, cx)
                 })?
@@ -3134,7 +3135,7 @@ impl BufferGitState {
             {
                 uncommitted_diff.update(cx, |diff, cx| {
                     if language_changed {
-                        diff.language_changed(cx);
+                        diff.language_changed(language, language_registry, cx);
                     }
                     diff.set_snapshot_with_secondary(
                         new_uncommitted_diff,

crates/project/src/project_tests.rs 🔗

@@ -7222,9 +7222,9 @@ async fn test_unstaged_diff_for_buffer(cx: &mut gpui::TestAppContext) {
     unstaged_diff.update(cx, |unstaged_diff, cx| {
         let snapshot = buffer.read(cx).snapshot();
         assert_hunks(
-            unstaged_diff.hunks(&snapshot, cx),
+            unstaged_diff.snapshot(cx).hunks(&snapshot),
             &snapshot,
-            &unstaged_diff.base_text_string().unwrap(),
+            &unstaged_diff.base_text_string(cx).unwrap(),
             &[
                 (0..1, "", "// print goodbye\n", DiffHunkStatus::added_none()),
                 (
@@ -7250,9 +7250,11 @@ async fn test_unstaged_diff_for_buffer(cx: &mut gpui::TestAppContext) {
     unstaged_diff.update(cx, |unstaged_diff, cx| {
         let snapshot = buffer.read(cx).snapshot();
         assert_hunks(
-            unstaged_diff.hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &snapshot, cx),
+            unstaged_diff
+                .snapshot(cx)
+                .hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &snapshot),
             &snapshot,
-            &unstaged_diff.base_text().text(),
+            &unstaged_diff.base_text(cx).text(),
             &[(
                 2..3,
                 "",
@@ -7332,16 +7334,17 @@ async fn test_uncommitted_diff_for_buffer(cx: &mut gpui::TestAppContext) {
         })
         .await
         .unwrap();
-    diff_1.read_with(cx, |diff, _| {
-        assert_eq!(diff.base_text().language().cloned(), Some(language))
+    diff_1.read_with(cx, |diff, cx| {
+        assert_eq!(diff.base_text(cx).language().cloned(), Some(language))
     });
     cx.run_until_parked();
     diff_1.update(cx, |diff, cx| {
         let snapshot = buffer_1.read(cx).snapshot();
         assert_hunks(
-            diff.hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &snapshot, cx),
+            diff.snapshot(cx)
+                .hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &snapshot),
             &snapshot,
-            &diff.base_text_string().unwrap(),
+            &diff.base_text_string(cx).unwrap(),
             &[
                 (
                     0..1,
@@ -7380,9 +7383,10 @@ async fn test_uncommitted_diff_for_buffer(cx: &mut gpui::TestAppContext) {
     diff_1.update(cx, |diff, cx| {
         let snapshot = buffer_1.read(cx).snapshot();
         assert_hunks(
-            diff.hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &snapshot, cx),
+            diff.snapshot(cx)
+                .hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &snapshot),
             &snapshot,
-            &diff.base_text().text(),
+            &diff.base_text(cx).text(),
             &[(
                 2..3,
                 "",
@@ -7409,9 +7413,10 @@ async fn test_uncommitted_diff_for_buffer(cx: &mut gpui::TestAppContext) {
     diff_2.update(cx, |diff, cx| {
         let snapshot = buffer_2.read(cx).snapshot();
         assert_hunks(
-            diff.hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &snapshot, cx),
+            diff.snapshot(cx)
+                .hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &snapshot),
             &snapshot,
-            &diff.base_text_string().unwrap(),
+            &diff.base_text_string(cx).unwrap(),
             &[(
                 0..0,
                 "// the-deleted-contents\n",
@@ -7430,9 +7435,10 @@ async fn test_uncommitted_diff_for_buffer(cx: &mut gpui::TestAppContext) {
     diff_2.update(cx, |diff, cx| {
         let snapshot = buffer_2.read(cx).snapshot();
         assert_hunks(
-            diff.hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &snapshot, cx),
+            diff.snapshot(cx)
+                .hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &snapshot),
             &snapshot,
-            &diff.base_text_string().unwrap(),
+            &diff.base_text_string(cx).unwrap(),
             &[(
                 0..0,
                 "// the-deleted-contents\n",
@@ -7501,9 +7507,9 @@ async fn test_staging_hunks(cx: &mut gpui::TestAppContext) {
     // The hunks are initially unstaged.
     uncommitted_diff.read_with(cx, |diff, cx| {
         assert_hunks(
-            diff.hunks(&snapshot, cx),
+            diff.snapshot(cx).hunks(&snapshot),
             &snapshot,
-            &diff.base_text_string().unwrap(),
+            &diff.base_text_string(cx).unwrap(),
             &[
                 (
                     0..0,
@@ -7532,14 +7538,15 @@ async fn test_staging_hunks(cx: &mut gpui::TestAppContext) {
         let range =
             snapshot.anchor_before(Point::new(1, 0))..snapshot.anchor_before(Point::new(2, 0));
         let hunks = diff
-            .hunks_intersecting_range(range, &snapshot, cx)
+            .snapshot(cx)
+            .hunks_intersecting_range(range, &snapshot)
             .collect::<Vec<_>>();
         diff.stage_or_unstage_hunks(true, &hunks, &snapshot, true, cx);
 
         assert_hunks(
-            diff.hunks(&snapshot, cx),
+            diff.snapshot(cx).hunks(&snapshot),
             &snapshot,
-            &diff.base_text_string().unwrap(),
+            &diff.base_text_string(cx).unwrap(),
             &[
                 (
                     0..0,
@@ -7584,9 +7591,9 @@ async fn test_staging_hunks(cx: &mut gpui::TestAppContext) {
     cx.run_until_parked();
     uncommitted_diff.update(cx, |diff, cx| {
         assert_hunks(
-            diff.hunks(&snapshot, cx),
+            diff.snapshot(cx).hunks(&snapshot),
             &snapshot,
-            &diff.base_text_string().unwrap(),
+            &diff.base_text_string(cx).unwrap(),
             &[
                 (
                     0..0,
@@ -7634,14 +7641,15 @@ async fn test_staging_hunks(cx: &mut gpui::TestAppContext) {
         let range =
             snapshot.anchor_before(Point::new(3, 0))..snapshot.anchor_before(Point::new(4, 0));
         let hunks = diff
-            .hunks_intersecting_range(range, &snapshot, cx)
+            .snapshot(cx)
+            .hunks_intersecting_range(range, &snapshot)
             .collect::<Vec<_>>();
         diff.stage_or_unstage_hunks(true, &hunks, &snapshot, true, cx);
 
         assert_hunks(
-            diff.hunks(&snapshot, cx),
+            diff.snapshot(cx).hunks(&snapshot),
             &snapshot,
-            &diff.base_text_string().unwrap(),
+            &diff.base_text_string(cx).unwrap(),
             &[
                 (
                     0..0,
@@ -7684,9 +7692,9 @@ async fn test_staging_hunks(cx: &mut gpui::TestAppContext) {
     cx.run_until_parked();
     uncommitted_diff.update(cx, |diff, cx| {
         assert_hunks(
-            diff.hunks(&snapshot, cx),
+            diff.snapshot(cx).hunks(&snapshot),
             &snapshot,
-            &diff.base_text_string().unwrap(),
+            &diff.base_text_string(cx).unwrap(),
             &[
                 (
                     0..0,
@@ -7727,7 +7735,7 @@ async fn test_staging_hunks(cx: &mut gpui::TestAppContext) {
 
     // Stage two hunks with separate operations.
     uncommitted_diff.update(cx, |diff, cx| {
-        let hunks = diff.hunks(&snapshot, cx).collect::<Vec<_>>();
+        let hunks = diff.snapshot(cx).hunks(&snapshot).collect::<Vec<_>>();
         diff.stage_or_unstage_hunks(true, &hunks[0..1], &snapshot, true, cx);
         diff.stage_or_unstage_hunks(true, &hunks[2..3], &snapshot, true, cx);
     });
@@ -7915,7 +7923,7 @@ async fn test_staging_hunks_with_delayed_fs_event(cx: &mut gpui::TestAppContext)
         let hunk = diff.snapshot(cx).hunks(&snapshot).nth(1).unwrap();
         diff.stage_or_unstage_hunks(true, &[hunk], &snapshot, true, cx);
         assert_hunks(
-            diff.hunks(&snapshot, cx),
+            diff.snapshot(cx).hunks(&snapshot),
             &snapshot,
             &diff.base_text_string(cx).unwrap(),
             &[
@@ -7947,7 +7955,7 @@ async fn test_staging_hunks_with_delayed_fs_event(cx: &mut gpui::TestAppContext)
 
     // Stage the third hunk before receiving the second FS event.
     uncommitted_diff.update(cx, |diff, cx| {
-        let hunk = diff.hunks(&snapshot, cx).nth(2).unwrap();
+        let hunk = diff.snapshot(cx).hunks(&snapshot).nth(2).unwrap();
         diff.stage_or_unstage_hunks(true, &[hunk], &snapshot, true, cx);
     });
 
@@ -7959,7 +7967,7 @@ async fn test_staging_hunks_with_delayed_fs_event(cx: &mut gpui::TestAppContext)
     cx.run_until_parked();
     uncommitted_diff.update(cx, |diff, cx| {
         assert_hunks(
-            diff.hunks(&snapshot, cx),
+            diff.snapshot(cx).hunks(&snapshot),
             &snapshot,
             &diff.base_text_string(cx).unwrap(),
             &[
@@ -8045,8 +8053,9 @@ async fn test_staging_random_hunks(
         .await
         .unwrap();
 
-    let mut hunks =
-        uncommitted_diff.update(cx, |diff, cx| diff.hunks(&snapshot, cx).collect::<Vec<_>>());
+    let mut hunks = uncommitted_diff.update(cx, |diff, cx| {
+        diff.snapshot(cx).hunks(&snapshot).collect::<Vec<_>>()
+    });
     assert_eq!(hunks.len(), 6);
 
     for _i in 0..operations {
@@ -8097,7 +8106,8 @@ async fn test_staging_random_hunks(
             .map(|hunk| (hunk.range.start.row, hunk.secondary_status))
             .collect::<Vec<_>>();
         let actual_hunks = diff
-            .hunks(&snapshot, cx)
+            .snapshot(cx)
+            .hunks(&snapshot)
             .map(|hunk| (hunk.range.start.row, hunk.secondary_status))
             .collect::<Vec<_>>();
         assert_eq!(actual_hunks, expected_hunks);
@@ -8162,7 +8172,7 @@ async fn test_single_file_diffs(cx: &mut gpui::TestAppContext) {
     uncommitted_diff.update(cx, |uncommitted_diff, cx| {
         let snapshot = buffer.read(cx).snapshot();
         assert_hunks(
-            uncommitted_diff.hunks(&snapshot, cx),
+            uncommitted_diff.snapshot(cx).hunks(&snapshot),
             &snapshot,
             &uncommitted_diff.base_text_string(cx).unwrap(),
             &[(
@@ -8227,7 +8237,7 @@ async fn test_staging_hunk_preserve_executable_permission(cx: &mut gpui::TestApp
         .unwrap();
 
     uncommitted_diff.update(cx, |diff, cx| {
-        let hunks = diff.hunks(&snapshot, cx).collect::<Vec<_>>();
+        let hunks = diff.snapshot(cx).hunks(&snapshot).collect::<Vec<_>>();
         diff.stage_or_unstage_hunks(true, &hunks, &snapshot, true, cx);
     });
 
@@ -10344,7 +10354,7 @@ async fn test_buffer_changed_file_path_updates_git_diff(cx: &mut gpui::TestAppCo
 
     cx.run_until_parked();
 
-    unstaged_diff.update(cx, |unstaged_diff, _cx| {
+    unstaged_diff.update(cx, |unstaged_diff, cx| {
         let base_text = unstaged_diff.base_text_string(cx).unwrap();
         assert_eq!(base_text, file_1_staged, "Should start with file_1 staged");
     });
@@ -10375,7 +10385,7 @@ async fn test_buffer_changed_file_path_updates_git_diff(cx: &mut gpui::TestAppCo
             "Diff bases should be automatically updated to file_2 staged content"
         );
 
-        let hunks: Vec<_> = unstaged_diff.hunks(&snapshot, cx).collect();
+        let hunks: Vec<_> = unstaged_diff.snapshot(cx).hunks(&snapshot).collect();
         assert!(!hunks.is_empty(), "Should have diff hunks for file_2");
     });
 
@@ -10388,7 +10398,7 @@ async fn test_buffer_changed_file_path_updates_git_diff(cx: &mut gpui::TestAppCo
 
     cx.run_until_parked();
 
-    uncommitted_diff.update(cx, |uncommitted_diff, _cx| {
+    uncommitted_diff.update(cx, |uncommitted_diff, cx| {
         let base_text = uncommitted_diff.base_text_string(cx).unwrap();
         assert_eq!(
             base_text, file_2_committed,

crates/remote_server/src/remote_editing_tests.rs 🔗

@@ -96,8 +96,11 @@ async fn test_basic_remote_editing(cx: &mut TestAppContext, server_cx: &mut Test
         .await
         .unwrap();
 
-    diff.update(cx, |diff, _| {
-        assert_eq!(diff.base_text_string().unwrap(), "fn one() -> usize { 0 }");
+    diff.update(cx, |diff, cx| {
+        assert_eq!(
+            diff.base_text_string(cx).unwrap(),
+            "fn one() -> usize { 0 }"
+        );
     });
 
     buffer.update(cx, |buffer, cx| {
@@ -157,9 +160,9 @@ async fn test_basic_remote_editing(cx: &mut TestAppContext, server_cx: &mut Test
         &[("src/lib2.rs", "fn one() -> usize { 100 }".into())],
     );
     cx.executor().run_until_parked();
-    diff.update(cx, |diff, _| {
+    diff.update(cx, |diff, cx| {
         assert_eq!(
-            diff.base_text_string().unwrap(),
+            diff.base_text_string(cx).unwrap(),
             "fn one() -> usize { 100 }"
         );
     });
@@ -1443,12 +1446,12 @@ async fn test_remote_git_diffs(cx: &mut TestAppContext, server_cx: &mut TestAppC
         .unwrap();
 
     diff.read_with(cx, |diff, cx| {
-        assert_eq!(diff.base_text_string().unwrap(), text_1);
+        assert_eq!(diff.base_text_string(cx).unwrap(), text_1);
         assert_eq!(
             diff.secondary_diff()
                 .unwrap()
                 .read(cx)
-                .base_text_string()
+                .base_text_string(cx)
                 .unwrap(),
             text_1
         );
@@ -1462,12 +1465,12 @@ async fn test_remote_git_diffs(cx: &mut TestAppContext, server_cx: &mut TestAppC
 
     cx.executor().run_until_parked();
     diff.read_with(cx, |diff, cx| {
-        assert_eq!(diff.base_text_string().unwrap(), text_1);
+        assert_eq!(diff.base_text_string(cx).unwrap(), text_1);
         assert_eq!(
             diff.secondary_diff()
                 .unwrap()
                 .read(cx)
-                .base_text_string()
+                .base_text_string(cx)
                 .unwrap(),
             text_2
         );
@@ -1482,12 +1485,12 @@ async fn test_remote_git_diffs(cx: &mut TestAppContext, server_cx: &mut TestAppC
 
     cx.executor().run_until_parked();
     diff.read_with(cx, |diff, cx| {
-        assert_eq!(diff.base_text_string().unwrap(), text_2);
+        assert_eq!(diff.base_text_string(cx).unwrap(), text_2);
         assert_eq!(
             diff.secondary_diff()
                 .unwrap()
                 .read(cx)
-                .base_text_string()
+                .base_text_string(cx)
                 .unwrap(),
             text_2
         );
@@ -1588,12 +1591,12 @@ async fn test_remote_git_diffs_when_recv_update_repository_delay(
         .unwrap();
 
     diff.read_with(cx, |diff, cx| {
-        assert_eq!(diff.base_text_string().unwrap(), text_1);
+        assert_eq!(diff.base_text_string(cx).unwrap(), text_1);
         assert_eq!(
             diff.secondary_diff()
                 .unwrap()
                 .read(cx)
-                .base_text_string()
+                .base_text_string(cx)
                 .unwrap(),
             text_1
         );
@@ -1607,12 +1610,12 @@ async fn test_remote_git_diffs_when_recv_update_repository_delay(
 
     cx.executor().run_until_parked();
     diff.read_with(cx, |diff, cx| {
-        assert_eq!(diff.base_text_string().unwrap(), text_1);
+        assert_eq!(diff.base_text_string(cx).unwrap(), text_1);
         assert_eq!(
             diff.secondary_diff()
                 .unwrap()
                 .read(cx)
-                .base_text_string()
+                .base_text_string(cx)
                 .unwrap(),
             text_2
         );
@@ -1627,12 +1630,12 @@ async fn test_remote_git_diffs_when_recv_update_repository_delay(
 
     cx.executor().run_until_parked();
     diff.read_with(cx, |diff, cx| {
-        assert_eq!(diff.base_text_string().unwrap(), text_2);
+        assert_eq!(diff.base_text_string(cx).unwrap(), text_2);
         assert_eq!(
             diff.secondary_diff()
                 .unwrap()
                 .read(cx)
-                .base_text_string()
+                .base_text_string(cx)
                 .unwrap(),
             text_2
         );

crates/rope/src/chunk.rs 🔗

@@ -132,7 +132,6 @@ impl Chunk {
             return true;
         }
         if PANIC || cfg!(debug_assertions) {
-            dbg!(offset);
             panic_char_boundary(&self.text, offset);
         } else {
             log_err_char_boundary(&self.text, offset);

crates/zeta/src/rate_prediction_modal.rs 🔗

@@ -319,22 +319,23 @@ impl RatePredictionsModal {
                 let start = Point::new(range.start.row.saturating_sub(5), 0);
                 let end = Point::new(range.end.row + 5, 0).min(new_buffer_snapshot.max_point());
 
-                let diff = cx.new::<BufferDiff>(|cx| {
-                    let diff_snapshot = BufferDiffSnapshot::new_with_base_buffer(
+                let language = new_buffer_snapshot.language().cloned();
+                let diff = cx.new(|cx| BufferDiff::new(&new_buffer_snapshot.text, cx));
+                diff.update(cx, |diff, cx| {
+                    let update = diff.update_diff(
                         new_buffer_snapshot.text.clone(),
                         Some(old_buffer_snapshot.text().into()),
-                        old_buffer_snapshot.clone(),
+                        true,
+                        language,
                         cx,
                     );
-                    let diff = BufferDiff::new(&new_buffer_snapshot, cx);
                     cx.spawn(async move |diff, cx| {
-                        let diff_snapshot = diff_snapshot.await;
+                        let update = update.await;
                         diff.update(cx, |diff, cx| {
-                            diff.set_snapshot(diff_snapshot, &new_buffer_snapshot.text, cx);
+                            diff.set_snapshot(update, &new_buffer_snapshot.text, true, cx);
                         })
                     })
                     .detach();
-                    diff
                 });
 
                 editor.disable_header_for_buffer(new_buffer_id, cx);