From 7499f0a8f1d0c2493b37a5938bd60a6ac092331d Mon Sep 17 00:00:00 2001 From: Cole Miller Date: Fri, 5 Dec 2025 20:46:07 -0500 Subject: [PATCH] wip Co-authored-by: Cameron --- crates/acp_thread/src/diff.rs | 12 +- crates/buffer_diff/src/buffer_diff.rs | 554 +++++++----------- crates/multi_buffer/src/anchor.rs | 4 +- crates/multi_buffer/src/multi_buffer.rs | 7 +- crates/multi_buffer/src/multi_buffer_tests.rs | 102 +++- crates/project/src/git_store.rs | 73 ++- crates/project/src/git_store/branch_diff.rs | 4 +- crates/rope/src/chunk.rs | 2 +- 8 files changed, 319 insertions(+), 439 deletions(-) diff --git a/crates/acp_thread/src/diff.rs b/crates/acp_thread/src/diff.rs index 6623c6df6e67a119370581e843423038af35970d..aa2e3e7f514704037f56c37acc47e0739b3fd87f 100644 --- a/crates/acp_thread/src/diff.rs +++ b/crates/acp_thread/src/diff.rs @@ -86,17 +86,9 @@ impl Diff { pub fn new(buffer: Entity, cx: &mut Context) -> Self { let buffer_text_snapshot = buffer.read(cx).text_snapshot(); - let base_text_snapshot = buffer.read(cx).snapshot(); - let base_text = base_text_snapshot.text(); - debug_assert_eq!(buffer_text_snapshot.text(), base_text); let buffer_diff = cx.new(|cx| { let mut diff = BufferDiff::new_unchanged(&buffer_text_snapshot, cx); - let snapshot = diff.snapshot(cx); - let secondary_diff = cx.new(|cx| { - let mut diff = BufferDiff::new(&buffer_text_snapshot, cx); - diff.set_snapshot(snapshot, &buffer_text_snapshot, cx); - diff - }); + let secondary_diff = cx.new(|cx| BufferDiff::new_unchanged(&buffer_text_snapshot, cx)); diff.set_secondary_diff(secondary_diff); diff }); @@ -109,7 +101,7 @@ impl Diff { Self::Pending(PendingDiff { multibuffer, - base_text: Arc::new(base_text), + base_text: Arc::from(buffer_text_snapshot.text().as_str()), _subscription: cx.observe(&buffer, |this, _, cx| { if let Diff::Pending(diff) = this { diff.update(cx); diff --git a/crates/buffer_diff/src/buffer_diff.rs b/crates/buffer_diff/src/buffer_diff.rs index 9ea39b2ddcaf10d519b13f97674c4e5841980dc5..6d400a91ccfa91927730e8bf153df5b873ef9252 100644 --- a/crates/buffer_diff/src/buffer_diff.rs +++ b/crates/buffer_diff/src/buffer_diff.rs @@ -1,6 +1,6 @@ use futures::channel::oneshot; use git2::{DiffLineType as GitDiffLineType, DiffOptions as GitOptions, Patch as GitPatch}; -use gpui::{App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, TaskLabel}; +use gpui::{App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Task, TaskLabel}; use language::{ BufferRow, DiffOptions, File, Language, LanguageName, LanguageRegistry, language_settings::language_settings, word_diff_ranges, @@ -42,8 +42,9 @@ impl std::fmt::Debug for BufferDiffSnapshot { } } +// FIXME figure out how to hide this #[derive(Clone)] -struct BufferDiffInner { +pub struct BufferDiffInner { hunks: SumTree, pending_hunks: SumTree, base_text: BaseText, @@ -197,131 +198,14 @@ impl BufferDiffSnapshot { self.inner.base_text.remote_id() } - fn unchanged( - buffer: &text::BufferSnapshot, - base_text: language::BufferSnapshot, - ) -> BufferDiffSnapshot { - debug_assert_eq!(buffer.text(), base_text.text()); - BufferDiffSnapshot { - inner: BufferDiffInner { - base_text, - hunks: SumTree::new(buffer), - pending_hunks: SumTree::new(buffer), - base_text_exists: false, - }, - secondary_diff: None, - } - } - - fn new_with_base_text( - base_text_buffer: Entity, - buffer: text::BufferSnapshot, - base_text: Option>, - language: Option>, - language_registry: Option>, - cx: &mut App, - ) -> impl Future + use<> { - let base_text_pair; - let base_text_exists; - let base_text_snapshot; - let diff_options = build_diff_options( - None, - language.as_ref().map(|l| l.name()), - language.as_ref().map(|l| l.default_scope()), - cx, - ); - - if let Some(text) = &base_text { - let text_str: &str = &text; - let base_text_rope = Rope::from(text_str); - base_text_pair = Some((text.clone(), base_text_rope.clone())); - base_text_buffer.update(cx, |base_text_buffer, cx| { - // TODO(split-diff) arguably an unnecessary allocation - base_text_buffer.set_text(dbg!(text.clone()), cx); - }); - base_text_exists = true; - } else { - base_text_pair = None; - base_text_buffer.update(cx, |base_text_buffer, cx| { - dbg!("SET TO EMPTY"); - base_text_buffer.set_text("", cx); - }); - base_text_exists = false; - }; - base_text_snapshot = base_text_buffer.read(cx).snapshot(); - dbg!(base_text_snapshot.text()); - - let hunks = cx - .background_executor() - .spawn_labeled(*CALCULATE_DIFF_TASK, { - let buffer = buffer.clone(); - async move { compute_hunks(base_text_pair, buffer, diff_options) } - }); - - async move { - let hunks = hunks.await; - Self { - inner: BufferDiffInner { - base_text: base_text_snapshot, - hunks, - base_text_exists, - pending_hunks: SumTree::new(&buffer), - }, - secondary_diff: None, - } - } - } - - /// Compute a diff using an existing/unchanged base text. - pub fn new_with_base_buffer( - buffer: text::BufferSnapshot, - base_text: Option>, - base_text_buffer: Entity, - cx: &App, - ) -> impl Future + use<> { - let base_text_snapshot = base_text_buffer.read(cx).snapshot(); - let diff_options = build_diff_options( - base_text_snapshot.file(), - base_text_snapshot.language().map(|l| l.name()), - base_text_snapshot.language().map(|l| l.default_scope()), - cx, - ); - let base_text_exists = base_text.is_some(); - let base_text_pair = base_text.map(|text| { - debug_assert_eq!(&*text, &base_text_snapshot.text()); - (text, base_text_snapshot.as_rope().clone()) - }); - cx.background_executor() - .spawn_labeled(*CALCULATE_DIFF_TASK, async move { - Self { - inner: BufferDiffInner { - base_text: base_text_snapshot, - pending_hunks: SumTree::new(&buffer), - hunks: compute_hunks(base_text_pair, buffer, diff_options), - base_text_exists, - }, - secondary_diff: None, - } - }) - } - #[cfg(test)] fn new_sync( buffer: text::BufferSnapshot, diff_base: String, cx: &mut gpui::TestAppContext, ) -> BufferDiffSnapshot { - cx.executor().block(cx.update(|cx| { - let base_text_buffer = cx.new(|cx| language::Buffer::local(diff_base.clone(), cx)); - Self::new_with_base_text( - base_text_buffer, - buffer, - Some(dbg!(diff_base.as_str().into())), - None, - None, - 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)) } pub fn is_empty(&self) -> bool { @@ -486,7 +370,7 @@ impl BufferDiffInner> { // If the file doesn't exist in either HEAD or the index, then the // entire file must be either created or deleted in the index. - let (index_text, head_text) = match (dbg!(index_text), head_text) { + let (index_text, head_text) = match (index_text, head_text) { (Some(index_text), Some(head_text)) if file_exists || !stage => (index_text, head_text), (index_text, head_text) => { let (new_index_text, new_status) = if stage { @@ -665,12 +549,11 @@ impl BufferDiffInner> { } let mut new_index_text = Rope::new(); - dbg!(index_text.len()); let mut index_cursor = index_text.cursor(0); for (old_range, replacement_text) in edits { - new_index_text.append(index_cursor.slice(dbg!(old_range.start))); - index_cursor.seek_forward(dbg!(old_range.end)); + new_index_text.append(index_cursor.slice(old_range.start)); + index_cursor.seek_forward(old_range.end); new_index_text.push(&replacement_text); } new_index_text.append(index_cursor.suffix()); @@ -850,91 +733,6 @@ impl BufferDiffInner { }) }) } - - fn compare( - &self, - old: &Self, - new_snapshot: &text::BufferSnapshot, - ) -> (Option>, Option>) { - let mut new_cursor = self.hunks.cursor::<()>(new_snapshot); - let mut old_cursor = old.hunks.cursor::<()>(new_snapshot); - old_cursor.next(); - new_cursor.next(); - let mut start = None; - let mut end = None; - let mut base_text_start = None; - let mut base_text_end = None; - - loop { - match (new_cursor.item(), old_cursor.item()) { - (Some(new_hunk), Some(old_hunk)) => { - match new_hunk - .buffer_range - .start - .cmp(&old_hunk.buffer_range.start, new_snapshot) - { - Ordering::Less => { - start.get_or_insert(new_hunk.buffer_range.start); - base_text_start.get_or_insert(new_hunk.diff_base_byte_range.start); - end.replace(new_hunk.buffer_range.end); - base_text_end.get_or_insert(new_hunk.diff_base_byte_range.end); - new_cursor.next(); - } - Ordering::Equal => { - if new_hunk != old_hunk { - start.get_or_insert(new_hunk.buffer_range.start); - base_text_start.get_or_insert(new_hunk.diff_base_byte_range.start); - if old_hunk - .buffer_range - .end - .cmp(&new_hunk.buffer_range.end, new_snapshot) - .is_ge() - { - end.replace(old_hunk.buffer_range.end); - base_text_end.replace(old_hunk.diff_base_byte_range.end); - } else { - end.replace(new_hunk.buffer_range.end); - base_text_end.replace(new_hunk.diff_base_byte_range.end); - } - } - - new_cursor.next(); - old_cursor.next(); - } - Ordering::Greater => { - start.get_or_insert(old_hunk.buffer_range.start); - base_text_start.get_or_insert(old_hunk.diff_base_byte_range.start); - end.replace(old_hunk.buffer_range.end); - base_text_end.replace(old_hunk.diff_base_byte_range.end); - old_cursor.next(); - } - } - } - (Some(new_hunk), None) => { - start.get_or_insert(new_hunk.buffer_range.start); - base_text_start.get_or_insert(new_hunk.diff_base_byte_range.start); - end.replace(new_hunk.buffer_range.end); - base_text_end.replace(new_hunk.diff_base_byte_range.end); - new_cursor.next(); - } - (None, Some(old_hunk)) => { - start.get_or_insert(old_hunk.buffer_range.start); - base_text_start.get_or_insert(old_hunk.diff_base_byte_range.start); - end.replace(old_hunk.buffer_range.end); - base_text_end.replace(old_hunk.diff_base_byte_range.end); - old_cursor.next(); - } - (None, None) => break, - } - } - - ( - start.zip(end).map(|(start, end)| start..end), - base_text_start - .zip(base_text_end) - .map(|(start, end)| start..end), - ) - } } fn build_diff_options( @@ -1029,6 +827,91 @@ fn compute_hunks( tree } +fn compare_hunks( + new_hunks: &SumTree, + old_hunks: &SumTree, + new_snapshot: &text::BufferSnapshot, +) -> (Option>, Option>) { + let mut new_cursor = new_hunks.cursor::<()>(new_snapshot); + let mut old_cursor = old_hunks.cursor::<()>(new_snapshot); + old_cursor.next(); + new_cursor.next(); + let mut start = None; + let mut end = None; + let mut base_text_start = None; + let mut base_text_end = None; + + loop { + match (new_cursor.item(), old_cursor.item()) { + (Some(new_hunk), Some(old_hunk)) => { + match new_hunk + .buffer_range + .start + .cmp(&old_hunk.buffer_range.start, new_snapshot) + { + Ordering::Less => { + start.get_or_insert(new_hunk.buffer_range.start); + base_text_start.get_or_insert(new_hunk.diff_base_byte_range.start); + end.replace(new_hunk.buffer_range.end); + base_text_end.get_or_insert(new_hunk.diff_base_byte_range.end); + new_cursor.next(); + } + Ordering::Equal => { + if new_hunk != old_hunk { + start.get_or_insert(new_hunk.buffer_range.start); + base_text_start.get_or_insert(new_hunk.diff_base_byte_range.start); + if old_hunk + .buffer_range + .end + .cmp(&new_hunk.buffer_range.end, new_snapshot) + .is_ge() + { + end.replace(old_hunk.buffer_range.end); + base_text_end.replace(old_hunk.diff_base_byte_range.end); + } else { + end.replace(new_hunk.buffer_range.end); + base_text_end.replace(new_hunk.diff_base_byte_range.end); + } + } + + new_cursor.next(); + old_cursor.next(); + } + Ordering::Greater => { + start.get_or_insert(old_hunk.buffer_range.start); + base_text_start.get_or_insert(old_hunk.diff_base_byte_range.start); + end.replace(old_hunk.buffer_range.end); + base_text_end.replace(old_hunk.diff_base_byte_range.end); + old_cursor.next(); + } + } + } + (Some(new_hunk), None) => { + start.get_or_insert(new_hunk.buffer_range.start); + base_text_start.get_or_insert(new_hunk.diff_base_byte_range.start); + end.replace(new_hunk.buffer_range.end); + base_text_end.replace(new_hunk.diff_base_byte_range.end); + new_cursor.next(); + } + (None, Some(old_hunk)) => { + start.get_or_insert(old_hunk.buffer_range.start); + base_text_start.get_or_insert(old_hunk.diff_base_byte_range.start); + end.replace(old_hunk.buffer_range.end); + base_text_end.replace(old_hunk.diff_base_byte_range.end); + old_cursor.next(); + } + (None, None) => break, + } + } + + ( + start.zip(end).map(|(start, end)| start..end), + base_text_start + .zip(base_text_end) + .map(|(start, end)| start..end), + ) +} + fn process_patch_hunk( patch: &GitPatch<'_>, hunk_index: usize, @@ -1198,30 +1081,19 @@ impl BufferDiff { pub fn new_with_base_text( base_text: &str, buffer: &text::BufferSnapshot, - cx: &mut App, + cx: &mut Context, ) -> Self { - let base_buffer = cx.new(|cx| language::Buffer::local(base_text, cx)); - let mut base_text = base_text.to_owned(); - text::LineEnding::normalize(&mut base_text); - let snapshot = BufferDiffSnapshot::new_with_base_text( - base_buffer.clone(), + let mut this = BufferDiff::new(&buffer, cx); + let executor = cx.background_executor().clone(); + let inner = executor.block(this.update_diff( buffer.clone(), - Some(base_text.into()), - None, + Some(Arc::from(base_text)), + true, None, cx, - ); - let snapshot = cx.background_executor().block(snapshot); - Self { - buffer_id: buffer.remote_id(), - inner: BufferDiffInner { - hunks: snapshot.inner.hunks.clone(), - pending_hunks: snapshot.inner.pending_hunks.clone(), - base_text: base_buffer, - base_text_exists: snapshot.inner.base_text_exists, - }, - secondary_diff: None, - } + )); + this.set_snapshot(inner, &buffer, true, cx); + this } pub fn set_secondary_diff(&mut self, diff: Entity) { @@ -1257,7 +1129,6 @@ impl BufferDiff { .secondary_diff .as_ref()? .update(cx, |secondary_diff, cx| { - dbg!(secondary_diff.base_text_string(cx)); self.inner.stage_or_unstage_hunks_impl( &secondary_diff.inner, stage, @@ -1283,62 +1154,72 @@ impl BufferDiff { new_index_text } - pub async fn update_diff( - this: Entity, + pub fn update_diff( + &self, buffer: text::BufferSnapshot, base_text: Option>, base_text_changed: bool, - language_changed: bool, language: Option>, - language_registry: Option>, - cx: &mut AsyncApp, - ) -> anyhow::Result { - Ok(if base_text_changed || language_changed { - cx.update(|cx| { - // in this path we should mutate the Entity on `this` - // we want to return a BufferDiffSnapshot for which the `base_text` is a BufferSnapshot of the persistent `Entity` - BufferDiffSnapshot::new_with_base_text( - this.read(cx).inner.base_text.clone(), - buffer.clone(), - base_text, - language.clone(), - language_registry.clone(), - cx, - ) - })? - .await - } else { - this.read_with(cx, |this, cx| { - // FIXME we think the base text hasn't changed, so we should _not_ mutate the base text buffer or create a new buffersnapshot - BufferDiffSnapshot::new_with_base_buffer( + cx: &App, + ) -> Task>> { + let prev_base_text = self.base_text(cx).as_rope().clone(); + let diff_options = build_diff_options( + None, + language.as_ref().map(|l| l.name()), + language.as_ref().map(|l| l.default_scope()), + cx, + ); + + cx.background_executor() + .spawn_labeled(*CALCULATE_DIFF_TASK, async move { + let base_text_rope = if let Some(base_text) = &base_text { + if base_text_changed { + Rope::from(base_text.as_ref()) + } else { + prev_base_text + } + } else { + Rope::new() + }; + let base_text_exists = base_text.is_some(); + let hunks = compute_hunks( + base_text + .clone() + .map(|base_text| (base_text, base_text_rope.clone())), buffer.clone(), - base_text, - this.inner.base_text.clone(), - cx, - ) - })? - .await - }) + diff_options, + ); + BufferDiffInner { + base_text: base_text.unwrap_or_default(), + hunks, + base_text_exists, + pending_hunks: SumTree::new(&buffer), + } + }) } pub fn language_changed(&mut self, cx: &mut Context) { cx.emit(BufferDiffEvent::LanguageChanged); } + // FIXME name pub fn set_snapshot( &mut self, - new_snapshot: BufferDiffSnapshot, + new_state: BufferDiffInner>, buffer: &text::BufferSnapshot, + base_text_changed: bool, cx: &mut Context, ) -> Option> { - self.set_snapshot_with_secondary(new_snapshot, buffer, None, false, cx) + self.set_snapshot_with_secondary(new_state, buffer, None, base_text_changed, false, cx) } + // FIXME name pub fn set_snapshot_with_secondary( &mut self, - new_snapshot: BufferDiffSnapshot, + new_state: BufferDiffInner>, buffer: &text::BufferSnapshot, secondary_diff_change: Option>, + base_text_changed: bool, clear_pending_hunks: bool, cx: &mut Context, ) -> Option> { @@ -1346,26 +1227,15 @@ impl BufferDiff { let old_snapshot = self.snapshot(cx); let state = &mut self.inner; - let new_state = new_snapshot.inner; - let (base_text_changed, (mut changed_range, mut base_text_changed_range)) = + let (mut changed_range, mut base_text_changed_range) = match (state.base_text_exists, new_state.base_text_exists) { - (false, false) => (true, (None, None)), - (true, true) - if old_snapshot.inner.base_text.version() == new_state.base_text.version() - && old_snapshot.inner.base_text.non_text_state_update_count() - == new_state.base_text.non_text_state_update_count() => - { - debug_assert!( - old_snapshot.inner.base_text.remote_id() == new_state.base_text.remote_id() - ); - (false, new_state.compare(&old_snapshot.inner, buffer)) + (false, false) => (None, None), + (true, true) if !base_text_changed => { + compare_hunks(&new_state.hunks, &old_snapshot.inner.hunks, buffer) } _ => ( - true, - ( - Some(text::Anchor::min_max_range_for_buffer(self.buffer_id)), - Some(0..new_state.base_text.len()), - ), + Some(text::Anchor::min_max_range_for_buffer(self.buffer_id)), + Some(0..new_state.base_text.len()), ), }; @@ -1390,7 +1260,11 @@ impl BufferDiff { let state = &mut self.inner; state.base_text_exists = new_state.base_text_exists; - // FIXME state.base_text = ...; + if base_text_changed { + state.base_text.update(cx, |base_text, cx| { + base_text.set_text(new_state.base_text, cx); + }) + } state.hunks = new_state.hunks; if base_text_changed || clear_pending_hunks { if let Some((first, last)) = state.pending_hunks.first().zip(state.pending_hunks.last()) @@ -1449,31 +1323,25 @@ impl BufferDiff { &mut self, base_text: Option>, language: Option>, - language_registry: Option>, buffer: text::BufferSnapshot, cx: &mut Context, ) -> oneshot::Receiver<()> { let (tx, rx) = oneshot::channel(); - let this = cx.weak_entity(); - - let snapshot = BufferDiffSnapshot::new_with_base_text( - self.inner.base_text.clone(), - buffer.clone(), - base_text, - language, - language_registry, - cx, - ); let complete_on_drop = util::defer(|| { tx.send(()).ok(); }); - cx.spawn(async move |_, cx| { - let snapshot = snapshot.await; - let Some(this) = this.upgrade() else { + cx.spawn(async move |this, cx| { + let Some(state) = this + .update(cx, |this, cx| { + this.update_diff(buffer.clone(), base_text, true, language, cx) + }) + .log_err() + else { return; }; + let state = state.await; this.update(cx, |this, cx| { - this.set_snapshot(snapshot, &buffer, cx); + this.set_snapshot(state, &buffer, true, cx); }) .log_err(); drop(complete_on_drop) @@ -1490,15 +1358,11 @@ impl BufferDiff { #[cfg(any(test, feature = "test-support"))] pub fn recalculate_diff_sync(&mut self, buffer: text::BufferSnapshot, cx: &mut Context) { + let language = self.base_text(cx).language().cloned(); let base_text = self.base_text_string(cx).map(|s| s.as_str().into()); - let snapshot = BufferDiffSnapshot::new_with_base_buffer( - buffer.clone(), - base_text, - self.inner.base_text.clone(), - cx, - ); - let snapshot = cx.background_executor().block(snapshot); - self.set_snapshot(snapshot, &buffer, cx); + let fut = self.update_diff(buffer.clone(), base_text, false, language, cx); + let snapshot = cx.background_executor().block(fut); + self.set_snapshot(snapshot, &buffer, false, cx); } } @@ -1824,19 +1688,7 @@ mod tests { .unindent(); let buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), buffer_text); - let base_text_buffer = cx.new(|cx| language::Buffer::local(diff_base.clone(), cx)); - let diff = cx - .update(|cx| { - BufferDiffSnapshot::new_with_base_text( - base_text_buffer, - buffer.snapshot(), - Some(diff_base.as_str().into()), - None, - None, - cx, - ) - }) - .await; + let diff = BufferDiffSnapshot::new_sync(buffer.snapshot(), diff_base.clone(), cx); assert_eq!( diff.hunks_intersecting_range( Anchor::min_max_range_for_buffer(buffer.remote_id()), @@ -2090,37 +1942,19 @@ mod tests { ]; for example in table { - dbg!("----------------------------------"); let (buffer_text, ranges) = marked_text_ranges(&example.buffer_marked_text, false); let buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), buffer_text); let hunk_range = buffer.anchor_before(ranges[0].start)..buffer.anchor_before(ranges[0].end); - dbg!(); - - let unstaged = - BufferDiffSnapshot::new_sync(buffer.clone(), dbg!(example.index_text.clone()), cx); - dbg!(unstaged.base_text().text()); - let uncommitted = - BufferDiffSnapshot::new_sync(buffer.clone(), example.head_text.clone(), cx); - dbg!(); - - let unstaged_diff = cx.new(|cx| { - let mut diff = BufferDiff::new(&buffer, cx); - diff.set_snapshot(unstaged, &buffer, cx); - dbg!(diff.base_text(cx).text()); - diff - }); - dbg!(); + let unstaged_diff = + cx.new(|cx| BufferDiff::new_with_base_text(&example.index_text, &buffer, cx)); let uncommitted_diff = cx.new(|cx| { - let mut diff = BufferDiff::new(&buffer, cx); - diff.set_snapshot(uncommitted, &buffer, cx); - dbg!(unstaged_diff.read(cx).base_text_string(cx)); + let mut diff = BufferDiff::new_with_base_text(&example.head_text, &buffer, cx); diff.set_secondary_diff(unstaged_diff); diff }); - dbg!(); uncommitted_diff.update(cx, |diff, cx| { let hunks = diff @@ -2134,12 +1968,10 @@ mod tests { ) } - dbg!(); let new_index_text = diff .stage_or_unstage_hunks(true, &hunks, &buffer, true, cx) .unwrap() .to_string(); - dbg!(); let hunks = diff .snapshot(cx) @@ -2182,16 +2014,9 @@ mod tests { BufferId::new(1).unwrap(), buffer_text.clone(), ); - let unstaged = BufferDiffSnapshot::new_sync(buffer.clone(), index_text, cx); - let uncommitted = BufferDiffSnapshot::new_sync(buffer.clone(), head_text.clone(), cx); - let unstaged_diff = cx.new(|cx| { - let mut diff = BufferDiff::new(&buffer, cx); - diff.set_snapshot(unstaged, &buffer, cx); - diff - }); + let unstaged_diff = cx.new(|cx| BufferDiff::new_with_base_text(&index_text, &buffer, cx)); let uncommitted_diff = cx.new(|cx| { - let mut diff = BufferDiff::new(&buffer, cx); - diff.set_snapshot(uncommitted, &buffer, cx); + let mut diff = BufferDiff::new_with_base_text(&head_text, &buffer, cx); diff.set_secondary_diff(unstaged_diff.clone()); diff }); @@ -2258,7 +2083,10 @@ mod tests { 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 range = diff_1.inner.compare(&empty_diff.inner, &buffer).0.unwrap(); + // FIXME assert the other half of the range + let (range, base_text_range) = + compare_hunks(&diff_1.inner.hunks, &empty_diff.inner.hunks, &buffer); + let range = range.unwrap(); assert_eq!(range.to_point(&buffer), Point::new(0, 0)..Point::new(8, 0)); // Edit does not affect the diff. @@ -2276,7 +2104,10 @@ mod tests { .unindent(), ); let diff_2 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx); - assert_eq!(None, diff_2.inner.compare(&diff_1.inner, &buffer).0); + assert_eq!( + (None, None), + compare_hunks(&diff_2.inner.hunks, &diff_1.inner.hunks, &buffer) + ); // Edit turns a deletion hunk into a modification. buffer.edit_via_marked_text( @@ -2293,7 +2124,10 @@ mod tests { .unindent(), ); let diff_3 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx); - let range = diff_3.inner.compare(&diff_2.inner, &buffer).0.unwrap(); + // FIXME assert the other range + let (range, base_text_range) = + compare_hunks(&diff_3.inner.hunks, &diff_2.inner.hunks, &buffer); + let range = range.unwrap(); assert_eq!(range.to_point(&buffer), Point::new(1, 0)..Point::new(2, 0)); // Edit turns a modification hunk into a deletion. @@ -2310,7 +2144,9 @@ mod tests { .unindent(), ); let diff_4 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx); - let range = diff_4.inner.compare(&diff_3.inner, &buffer).0.unwrap(); + let (range, base_text_range) = + compare_hunks(&diff_4.inner.hunks, &diff_3.inner.hunks, &buffer); + let range = range.unwrap(); assert_eq!(range.to_point(&buffer), Point::new(3, 4)..Point::new(4, 0)); // Edit introduces a new insertion hunk. @@ -2328,7 +2164,9 @@ mod tests { .unindent(), ); let diff_5 = BufferDiffSnapshot::new_sync(buffer.snapshot(), base_text.clone(), cx); - let range = diff_5.inner.compare(&diff_4.inner, &buffer).0.unwrap(); + let (range, base_text_range) = + compare_hunks(&diff_5.inner.hunks, &diff_4.inner.hunks, &buffer); + let range = range.unwrap(); assert_eq!(range.to_point(&buffer), Point::new(3, 0)..Point::new(4, 0)); // Edit removes a hunk. @@ -2346,7 +2184,9 @@ mod tests { .unindent(), ); let diff_6 = BufferDiffSnapshot::new_sync(buffer.snapshot(), base_text, cx); - let range = diff_6.inner.compare(&diff_5.inner, &buffer).0.unwrap(); + let (range, base_text_range) = + compare_hunks(&diff_6.inner.hunks, &diff_5.inner.hunks, &buffer); + let range = range.unwrap(); assert_eq!(range.to_point(&buffer), Point::new(7, 0)..Point::new(8, 0)); } diff --git a/crates/multi_buffer/src/anchor.rs b/crates/multi_buffer/src/anchor.rs index 51696ba09e4bdb1c6be065f63d3ee7ff634e6b1a..1c3be4b171890e871880d8a3fe9788872a98d789 100644 --- a/crates/multi_buffer/src/anchor.rs +++ b/crates/multi_buffer/src/anchor.rs @@ -111,8 +111,8 @@ impl Anchor { .get(&excerpt.buffer_id) .map(|diff| diff.base_text()) { - let self_anchor = self.diff_base_anchor.filter(|a| base_text.can_resolve(a)); - let other_anchor = other.diff_base_anchor.filter(|a| base_text.can_resolve(a)); + let self_anchor = self.diff_base_anchor.filter(|a| a.is_valid(base_text)); + let other_anchor = other.diff_base_anchor.filter(|a| a.is_valid(base_text)); return match (self_anchor, other_anchor) { (Some(a), Some(b)) => a.cmp(&b, base_text), (Some(_), None) => match other.text_anchor.bias { diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index fc749a83e0742ed9ebe29269019c82493ccc0504..98bcf75040d7d31c784dace6314fa841bb2e86c7 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -506,8 +506,6 @@ struct BufferState { _subscriptions: [gpui::Subscription; 2], } - - struct DiffState { diff: Entity, /// Whether the [`MultiBuffer`] this is associated with is inverted (i.e. @@ -1123,6 +1121,7 @@ impl MultiBuffer { buffer.read(cx).capability(), MultiBufferSnapshot { singleton: true, + show_deleted_hunks: true, ..MultiBufferSnapshot::default() }, ); @@ -3420,7 +3419,7 @@ impl MultiBuffer { if !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) { let base_text = diff.base_text(); let mut text_cursor = @@ -5036,7 +5035,7 @@ impl MultiBufferSnapshot { if let Some(diff_base_anchor) = &anchor.diff_base_anchor && let Some(base_text) = self.diffs.get(buffer_id).map(|diff| diff.base_text()) - && base_text.can_resolve(diff_base_anchor) + && diff_base_anchor.is_valid(&base_text) { let base_text_offset = diff_base_anchor.to_offset(base_text); if base_text_offset >= base_text_byte_range.start diff --git a/crates/multi_buffer/src/multi_buffer_tests.rs b/crates/multi_buffer/src/multi_buffer_tests.rs index 2004555df8de644700935cd474c2781130cf0704..7c896fb9e582fa78d331f860cf22f4165a7ea522 100644 --- a/crates/multi_buffer/src/multi_buffer_tests.rs +++ b/crates/multi_buffer/src/multi_buffer_tests.rs @@ -361,7 +361,8 @@ async fn test_diff_boundary_anchors(cx: &mut TestAppContext) { let base_text = "one\ntwo\nthree\n"; let text = "one\nthree\n"; let buffer = cx.new(|cx| Buffer::local(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 multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx)); multibuffer.update(cx, |multibuffer, cx| multibuffer.add_diff(diff, cx)); @@ -403,7 +404,8 @@ async fn test_diff_hunks_in_range(cx: &mut TestAppContext) { let base_text = "one\ntwo\nthree\nfour\nfive\nsix\nseven\neight\n"; let text = "one\nfour\nseven\n"; let buffer = cx.new(|cx| Buffer::local(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 multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx)); let (mut snapshot, mut subscription) = multibuffer.update(cx, |multibuffer, cx| { (multibuffer.snapshot(cx), multibuffer.subscribe()) @@ -483,7 +485,8 @@ async fn test_editing_text_in_diff_hunks(cx: &mut TestAppContext) { let base_text = "one\ntwo\nfour\nfive\nsix\nseven\n"; let text = "one\ntwo\nTHREE\nfour\nfive\nseven\n"; let buffer = cx.new(|cx| Buffer::local(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 multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx)); let (mut snapshot, mut subscription) = multibuffer.update(cx, |multibuffer, cx| { @@ -916,7 +919,8 @@ async fn test_empty_diff_excerpt(cx: &mut TestAppContext) { let buffer = cx.new(|cx| Buffer::local("", cx)); let base_text = "a\nb\nc"; - 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.update(cx, |multibuffer, cx| { multibuffer.push_excerpts(buffer.clone(), [ExcerptRange::new(0..0)], cx); multibuffer.set_all_diff_hunks_expanded(cx); @@ -1263,7 +1267,8 @@ async fn test_basic_diff_hunks(cx: &mut TestAppContext) { ); let buffer = cx.new(|cx| Buffer::local(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)); cx.run_until_parked(); let multibuffer = cx.new(|cx| { @@ -1507,7 +1512,8 @@ async fn test_repeatedly_expand_a_diff_hunk(cx: &mut TestAppContext) { ); let buffer = cx.new(|cx| Buffer::local(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)); cx.run_until_parked(); let multibuffer = cx.new(|cx| { @@ -2042,8 +2048,12 @@ async fn test_diff_hunks_with_multiple_excerpts(cx: &mut TestAppContext) { let buffer_1 = cx.new(|cx| Buffer::local(text_1, cx)); let buffer_2 = cx.new(|cx| Buffer::local(text_2, cx)); - let diff_1 = cx.new(|cx| BufferDiff::new_with_base_text(base_text_1, &buffer_1, cx)); - let diff_2 = cx.new(|cx| BufferDiff::new_with_base_text(base_text_2, &buffer_2, cx)); + let diff_1 = cx.new(|cx| { + BufferDiff::new_with_base_text(base_text_1, &buffer_1.read(cx).text_snapshot(), cx) + }); + let diff_2 = cx.new(|cx| { + BufferDiff::new_with_base_text(base_text_2, &buffer_2.read(cx).text_snapshot(), cx) + }); cx.run_until_parked(); let multibuffer = cx.new(|cx| { @@ -2119,8 +2129,8 @@ async fn test_diff_hunks_with_multiple_excerpts(cx: &mut TestAppContext) { let id_1 = buffer_1.read_with(cx, |buffer, _| buffer.remote_id()); let id_2 = buffer_2.read_with(cx, |buffer, _| buffer.remote_id()); - let base_id_1 = diff_1.read_with(cx, |diff, _| diff.base_text().remote_id()); - let base_id_2 = diff_2.read_with(cx, |diff, _| diff.base_text().remote_id()); + let base_id_1 = diff_1.read_with(cx, |diff, cx| diff.base_text(cx).remote_id()); + let base_id_2 = diff_2.read_with(cx, |diff, cx| diff.base_text(cx).remote_id()); let buffer_lines = (0..=snapshot.max_row().0) .map(|row| { @@ -2336,7 +2346,11 @@ impl ReferenceMultibuffer { return; }; let excerpt_range = excerpt.range.to_offset(&buffer); - for hunk in diff.read(cx).hunks_intersecting_range(range, &buffer, cx) { + for hunk in diff + .read(cx) + .snapshot(cx) + .hunks_intersecting_range(range, &buffer) + { let hunk_range = hunk.buffer_range.to_offset(&buffer); if hunk_range.start < excerpt_range.start || hunk_range.start > excerpt_range.end { continue; @@ -2374,12 +2388,17 @@ impl ReferenceMultibuffer { excerpt_boundary_rows.insert(MultiBufferRow(text.matches('\n').count() as u32)); let buffer = excerpt.buffer.read(cx); let buffer_range = excerpt.range.to_offset(buffer); - let diff = self.diffs.get(&buffer.remote_id()).unwrap().read(cx); + let diff = self + .diffs + .get(&buffer.remote_id()) + .unwrap() + .read(cx) + .snapshot(cx); let base_buffer = diff.base_text(); let mut offset = buffer_range.start; let hunks = diff - .hunks_intersecting_range(excerpt.range.clone(), buffer, cx) + .hunks_intersecting_range(excerpt.range.clone(), buffer) .peekable(); for hunk in hunks { @@ -2587,8 +2606,8 @@ impl ReferenceMultibuffer { let buffer = excerpt.buffer.read(cx).snapshot(); let excerpt_range = excerpt.range.to_offset(&buffer); let buffer_id = buffer.remote_id(); - let diff = self.diffs.get(&buffer_id).unwrap().read(cx); - let mut hunks = diff.hunks_in_row_range(0..u32::MAX, &buffer, cx).peekable(); + let diff = self.diffs.get(&buffer_id).unwrap().read(cx).snapshot(cx); + let mut hunks = diff.hunks_in_row_range(0..u32::MAX, &buffer).peekable(); excerpt.expanded_diff_hunks.retain(|hunk_anchor| { if !hunk_anchor.is_valid(&buffer) { return false; @@ -2936,8 +2955,13 @@ async fn test_random_multibuffer_impl( let id = buffer_handle.read(cx).remote_id(); if multibuffer.diff_for(id).is_none() { let base_text = base_texts.get(&id).unwrap(); - let diff = cx - .new(|cx| BufferDiff::new_with_base_text(base_text, buffer_handle, cx)); + let diff = cx.new(|cx| { + BufferDiff::new_with_base_text( + base_text, + &buffer_handle.read(cx).text_snapshot(), + cx, + ) + }); reference.add_diff(diff.clone(), cx); multibuffer.add_diff(diff, cx) } @@ -3456,8 +3480,12 @@ async fn test_summaries_for_anchors(cx: &mut TestAppContext) { let buffer_1 = cx.new(|cx| Buffer::local(text_1, cx)); let buffer_2 = cx.new(|cx| Buffer::local(text_2, cx)); - let diff_1 = cx.new(|cx| BufferDiff::new_with_base_text(base_text_1, &buffer_1, cx)); - let diff_2 = cx.new(|cx| BufferDiff::new_with_base_text(base_text_2, &buffer_2, cx)); + let diff_1 = cx.new(|cx| { + BufferDiff::new_with_base_text(base_text_1, &buffer_1.read(cx).text_snapshot(), cx) + }); + let diff_2 = cx.new(|cx| { + BufferDiff::new_with_base_text(base_text_2, &buffer_2.read(cx).text_snapshot(), cx) + }); cx.run_until_parked(); let mut ids = vec![]; @@ -3514,7 +3542,9 @@ async fn test_trailing_deletion_without_newline(cx: &mut TestAppContext) { let text_1 = "one\n".to_owned(); let buffer_1 = cx.new(|cx| Buffer::local(text_1, cx)); - let diff_1 = cx.new(|cx| BufferDiff::new_with_base_text(&base_text_1, &buffer_1, cx)); + let diff_1 = cx.new(|cx| { + BufferDiff::new_with_base_text(&base_text_1, &buffer_1.read(cx).text_snapshot(), cx) + }); cx.run_until_parked(); let multibuffer = cx.new(|cx| { @@ -3707,7 +3737,8 @@ async fn test_inverted_diff(cx: &mut TestAppContext) { ); let buffer = cx.new(|cx| Buffer::local(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)); cx.run_until_parked(); let base_text_buffer = cx.new(|cx| Buffer::local(base_text, cx)); @@ -3762,6 +3793,22 @@ async fn test_inverted_diff(cx: &mut TestAppContext) { ); }); cx.run_until_parked(); + let update = diff + .update(cx, |diff, cx| { + diff.update_diff( + buffer.read(cx).text_snapshot(), + Some(base_text.into()), + false, + None, + cx, + ) + }) + .await; + diff.update(cx, |diff, cx| { + diff.set_snapshot(update, &buffer.read(cx).text_snapshot(), false, cx) + }); + cx.run_until_parked(); + assert_new_snapshot( &multibuffer, &mut snapshot, @@ -3771,6 +3818,7 @@ async fn test_inverted_diff(cx: &mut TestAppContext) { " one - two + - three - four - five six @@ -4231,8 +4279,13 @@ fn test_random_chunk_bitmaps_with_diffs(cx: &mut App, mut rng: StdRng) { } } - let diff = - cx.new(|cx| BufferDiff::new_with_base_text(&base_text, &buffer_handle, cx)); + let diff = cx.new(|cx| { + BufferDiff::new_with_base_text( + &base_text, + &buffer_handle.read(cx).text_snapshot(), + cx, + ) + }); diffs.push(diff.clone()); multibuffer.add_diff(diff, cx); } @@ -4342,7 +4395,8 @@ fn collect_word_diffs( cx: &mut TestAppContext, ) -> Vec { let buffer = cx.new(|cx| Buffer::local(modified_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)); cx.run_until_parked(); let multibuffer = cx.new(|cx| { diff --git a/crates/project/src/git_store.rs b/crates/project/src/git_store.rs index 8f5f42295c64e5d2e72272d45d35343b4cb165e1..34c82216f0f8b3bdbf593cbe568dd531e0bc7e3d 100644 --- a/crates/project/src/git_store.rs +++ b/crates/project/src/git_store.rs @@ -129,8 +129,8 @@ struct BufferGitState { hunk_staging_operation_count: usize, hunk_staging_operation_count_as_of_write: usize, - head_text: Option>, - index_text: Option>, + head_text: Option>, + index_text: Option>, head_changed: bool, index_changed: bool, language_changed: bool, @@ -691,7 +691,6 @@ impl GitStore { oid: Option, buffer: Entity, repo: Entity, - languages: Arc, cx: &mut Context, ) -> Task>> { cx.spawn(async move |this, cx| { @@ -708,9 +707,8 @@ impl GitStore { buffer_diff .update(cx, |buffer_diff, cx| { buffer_diff.set_base_text( - content.map(|s| s.as_ref().into()), + content.map(|s| s.as_str().into()), buffer_snapshot.language().cloned(), - Some(languages.clone()), buffer_snapshot.text, cx, ) @@ -2611,7 +2609,7 @@ impl GitStore { .or_default(); shared_diffs.entry(buffer_id).or_default().unstaged = Some(diff.clone()); })?; - let staged_text = diff.read_with(&cx, |diff, _| diff.base_text_string(cx))?; + let staged_text = diff.read_with(&cx, |diff, cx| diff.base_text_string(cx))?; Ok(proto::OpenUnstagedDiffResponse { staged_text }) } @@ -2984,21 +2982,21 @@ impl BufferGitState { Some(DiffBasesChange::SetIndex(index)) => { self.index_text = index.map(|mut index| { text::LineEnding::normalize(&mut index); - Arc::new(index) + Arc::from(index.as_str()) }); self.index_changed = true; } Some(DiffBasesChange::SetHead(head)) => { self.head_text = head.map(|mut head| { text::LineEnding::normalize(&mut head); - Arc::new(head) + Arc::from(head.as_str()) }); self.head_changed = true; } Some(DiffBasesChange::SetBoth(text)) => { let text = text.map(|mut text| { text::LineEnding::normalize(&mut text); - Arc::new(text) + Arc::from(text.as_str()) }); self.head_text = text.clone(); self.index_text = text; @@ -3008,12 +3006,12 @@ impl BufferGitState { Some(DiffBasesChange::SetEach { index, head }) => { self.index_text = index.map(|mut index| { text::LineEnding::normalize(&mut index); - Arc::new(index) + Arc::from(index.as_str()) }); self.index_changed = true; self.head_text = head.map(|mut head| { text::LineEnding::normalize(&mut head); - Arc::new(head) + Arc::from(head.as_str()) }); self.head_changed = true; } @@ -3050,17 +3048,16 @@ impl BufferGitState { let mut new_unstaged_diff = None; if let Some(unstaged_diff) = &unstaged_diff { new_unstaged_diff = Some( - BufferDiff::update_diff( - unstaged_diff.clone(), - buffer.clone(), - index, - index_changed, - language_changed, - language.clone(), - language_registry.clone(), - cx, - ) - .await?, + cx.update(|cx| { + unstaged_diff.read(cx).update_diff( + buffer.clone(), + index, + index_changed, + language.clone(), + cx, + ) + })? + .await, ); } @@ -3074,17 +3071,16 @@ impl BufferGitState { new_unstaged_diff.clone() } else { Some( - BufferDiff::update_diff( - uncommitted_diff.clone(), - buffer.clone(), - head, - head_changed, - language_changed, - language.clone(), - language_registry.clone(), - cx, - ) - .await?, + cx.update(|cx| { + uncommitted_diff.read(cx).update_diff( + buffer.clone(), + head, + head_changed, + language.clone(), + cx, + ) + })? + .await, ) } } @@ -3125,7 +3121,7 @@ impl BufferGitState { if language_changed { diff.language_changed(cx); } - diff.set_snapshot(new_unstaged_diff, &buffer, cx) + diff.set_snapshot(new_unstaged_diff, &buffer, index_changed, cx) })? } else { None @@ -3144,6 +3140,7 @@ impl BufferGitState { new_uncommitted_diff, &buffer, unstaged_changed_range, + head_changed, true, cx, ); @@ -3644,9 +3641,9 @@ impl Repository { match (current_index_text.as_ref(), current_head_text.as_ref()) { (Some(current_index), Some(current_head)) => { let index_changed = - index_text.as_ref() != current_index.as_deref(); + index_text.as_deref() != current_index.as_deref(); let head_changed = - head_text.as_ref() != current_head.as_deref(); + head_text.as_deref() != current_head.as_deref(); if index_changed && head_changed { if index_text == head_text { Some(DiffBasesChange::SetBoth(head_text)) @@ -3666,13 +3663,13 @@ impl Repository { } (Some(current_index), None) => { let index_changed = - index_text.as_ref() != current_index.as_deref(); + index_text.as_deref() != current_index.as_deref(); index_changed .then_some(DiffBasesChange::SetIndex(index_text)) } (None, Some(current_head)) => { let head_changed = - head_text.as_ref() != current_head.as_deref(); + head_text.as_deref() != current_head.as_deref(); head_changed.then_some(DiffBasesChange::SetHead(head_text)) } (None, None) => None, diff --git a/crates/project/src/git_store/branch_diff.rs b/crates/project/src/git_store/branch_diff.rs index 5065eafe4e185e65ce144f6d797ac8ccd616d5fa..2d61146c9c2c8bd41534563c140e67bd7ea09114 100644 --- a/crates/project/src/git_store/branch_diff.rs +++ b/crates/project/src/git_store/branch_diff.rs @@ -329,8 +329,6 @@ impl BranchDiff { .update(cx, |project, cx| project.open_buffer(project_path, cx))? .await?; - let languages = project.update(cx, |project, _cx| project.languages().clone())?; - let changes = if let Some(entry) = branch_diff { let oid = match entry { git::status::TreeDiffStatus::Added { .. } => None, @@ -340,7 +338,7 @@ impl BranchDiff { project .update(cx, |project, cx| { project.git_store().update(cx, |git_store, cx| { - git_store.open_diff_since(oid, buffer.clone(), repo, languages, cx) + git_store.open_diff_since(oid, buffer.clone(), repo, cx) }) })? .await? diff --git a/crates/rope/src/chunk.rs b/crates/rope/src/chunk.rs index a2a8e8d58df2d5ddc3336e8e56dd8446f4dcf118..4cb3c74d011cf44c3cdeb00f22a854783405d334 100644 --- a/crates/rope/src/chunk.rs +++ b/crates/rope/src/chunk.rs @@ -131,7 +131,7 @@ impl Chunk { if self.is_char_boundary(offset) { return true; } - if PANIC { + if PANIC || cfg!(debug_assertions) { panic_char_boundary(&self.text, offset); } else { log_err_char_boundary(&self.text, offset);