@@ -15,7 +15,7 @@ use buffer_diff::{
};
use clock::ReplicaId;
use collections::{BTreeMap, Bound, HashMap, HashSet};
-use gpui::{App, Context, Entity, EntityId, EventEmitter, WeakEntity};
+use gpui::{App, Context, Entity, EntityId, EventEmitter};
use itertools::Itertools;
use language::{
AutoindentMode, BracketMatch, Buffer, BufferChunks, BufferRow, BufferSnapshot, Capability,
@@ -499,10 +499,7 @@ struct BufferState {
struct DiffState {
diff: Entity<BufferDiff>,
- /// If set, this diff is "inverted" (i.e. showing additions as deletions).
- /// This is used in the side-by-side diff view. The main_buffer is the
- /// editable buffer, while the excerpt shows the base text.
- main_buffer: Option<WeakEntity<Buffer>>,
+ is_inverted: bool,
_subscription: gpui::Subscription,
}
@@ -510,11 +507,7 @@ impl DiffState {
fn snapshot(&self, cx: &App) -> DiffStateSnapshot {
DiffStateSnapshot {
diff: self.diff.read(cx).snapshot(cx),
- main_buffer: self.main_buffer.as_ref().and_then(|main_buffer| {
- main_buffer
- .read_with(cx, |main_buffer, _| main_buffer.text_snapshot())
- .ok()
- }),
+ is_inverted: self.is_inverted,
}
}
}
@@ -522,7 +515,7 @@ impl DiffState {
#[derive(Clone)]
struct DiffStateSnapshot {
diff: BufferDiffSnapshot,
- main_buffer: Option<text::BufferSnapshot>,
+ is_inverted: bool,
}
impl std::ops::Deref for DiffStateSnapshot {
@@ -557,19 +550,13 @@ impl DiffState {
_ => {}
}),
diff,
- main_buffer: None,
+ is_inverted: false,
}
}
- fn new_inverted(
- diff: Entity<BufferDiff>,
- main_buffer: Entity<Buffer>,
- cx: &mut Context<MultiBuffer>,
- ) -> Self {
- let main_buffer = main_buffer.downgrade();
+ fn new_inverted(diff: Entity<BufferDiff>, cx: &mut Context<MultiBuffer>) -> Self {
DiffState {
_subscription: cx.subscribe(&diff, {
- let main_buffer = main_buffer.clone();
move |this, diff, event, cx| match event {
BufferDiffEvent::DiffChanged(DiffChanged {
changed_range: _,
@@ -577,23 +564,18 @@ impl DiffState {
extended_range: _,
}) => {
if let Some(base_text_changed_range) = base_text_changed_range.clone() {
- this.inverted_buffer_diff_changed(
- diff,
- base_text_changed_range,
- main_buffer.clone(),
- cx,
- )
+ this.inverted_buffer_diff_changed(diff, base_text_changed_range, cx)
}
cx.emit(Event::BufferDiffChanged);
}
BufferDiffEvent::LanguageChanged => {
- this.inverted_buffer_diff_language_changed(diff, main_buffer.clone(), cx)
+ this.inverted_buffer_diff_language_changed(diff, cx)
}
_ => {}
}
}),
diff,
- main_buffer: Some(main_buffer),
+ is_inverted: true,
}
}
}
@@ -2223,10 +2205,7 @@ impl MultiBuffer {
// Recalculate has_inverted_diff after removing diffs
if !removed_buffer_ids.is_empty() {
- snapshot.has_inverted_diff = snapshot
- .diffs
- .iter()
- .any(|(_, diff)| diff.main_buffer.is_some());
+ snapshot.has_inverted_diff = snapshot.diffs.iter().any(|(_, diff)| diff.is_inverted);
}
if changed_trailing_excerpt {
@@ -2328,7 +2307,7 @@ impl MultiBuffer {
let buffer_id = diff.buffer_id;
let diff = DiffStateSnapshot {
diff: diff.snapshot(cx),
- main_buffer: None,
+ is_inverted: false,
};
self.snapshot.get_mut().diffs.insert(buffer_id, diff);
}
@@ -2336,16 +2315,13 @@ impl MultiBuffer {
fn inverted_buffer_diff_language_changed(
&mut self,
diff: Entity<BufferDiff>,
- main_buffer: WeakEntity<Buffer>,
cx: &mut Context<Self>,
) {
let base_text_buffer_id = diff.read(cx).base_text(cx).remote_id();
let diff = diff.read(cx);
let diff = DiffStateSnapshot {
diff: diff.snapshot(cx),
- main_buffer: main_buffer
- .update(cx, |main_buffer, _| main_buffer.text_snapshot())
- .ok(),
+ is_inverted: true,
};
self.snapshot
.get_mut()
@@ -2369,7 +2345,7 @@ impl MultiBuffer {
};
let new_diff = DiffStateSnapshot {
diff: diff.snapshot(cx),
- main_buffer: None,
+ is_inverted: false,
};
let mut snapshot = self.snapshot.get_mut();
let base_text_changed = snapshot
@@ -2401,7 +2377,6 @@ impl MultiBuffer {
&mut self,
diff: Entity<BufferDiff>,
diff_change_range: Range<usize>,
- main_buffer: WeakEntity<Buffer>,
cx: &mut Context<Self>,
) {
self.sync_mut(cx);
@@ -2411,13 +2386,10 @@ impl MultiBuffer {
let Some(buffer_state) = self.buffers.get(&base_text_buffer_id) else {
return;
};
- self.buffer_changed_since_sync.replace(true);
let new_diff = DiffStateSnapshot {
diff: diff.snapshot(cx),
- main_buffer: main_buffer
- .update(cx, |main_buffer, _| main_buffer.text_snapshot())
- .ok(),
+ is_inverted: true,
};
let mut snapshot = self.snapshot.get_mut();
snapshot
@@ -2429,7 +2401,7 @@ impl MultiBuffer {
&mut snapshot,
excerpt_edits,
DiffChangeKind::DiffUpdated {
- // We don't use read this field for inverted diffs.
+ // We don't read this field for inverted diffs.
base_changed: false,
},
);
@@ -2602,28 +2574,13 @@ impl MultiBuffer {
self.diffs.insert(buffer_id, DiffState::new(diff, cx));
}
- pub fn add_inverted_diff(
- &mut self,
- diff: Entity<BufferDiff>,
- main_buffer: Entity<Buffer>,
- cx: &mut Context<Self>,
- ) {
+ pub fn add_inverted_diff(&mut self, diff: Entity<BufferDiff>, cx: &mut Context<Self>) {
let base_text_buffer_id = diff.read(cx).base_text(cx).remote_id();
let diff_change_range = 0..diff.read(cx).base_text(cx).len();
self.snapshot.get_mut().has_inverted_diff = true;
- main_buffer.update(cx, |buffer, _| {
- buffer.record_changes(Rc::downgrade(&self.buffer_changed_since_sync));
- });
- self.inverted_buffer_diff_changed(
- diff.clone(),
- diff_change_range,
- main_buffer.downgrade(),
- cx,
- );
- self.diffs.insert(
- base_text_buffer_id,
- DiffState::new_inverted(diff, main_buffer, cx),
- );
+ self.inverted_buffer_diff_changed(diff.clone(), diff_change_range, cx);
+ self.diffs
+ .insert(base_text_buffer_id, DiffState::new_inverted(diff, cx));
}
pub fn diff_for(&self, buffer_id: BufferId) -> Option<Entity<BufferDiff>> {
@@ -3048,71 +3005,12 @@ impl MultiBuffer {
}
for (id, diff) in diffs.iter() {
- if buffer_diff.get(id).is_none() {
+ if buffer_diff.get(id).is_none() || diff.is_inverted {
buffer_diff.insert(*id, diff.snapshot(cx));
}
}
- // Check for main buffer changes in inverted diffs
- let mut main_buffer_changed_diffs = Vec::new();
- for (id, diff_state) in diffs.iter() {
- if let Some(main_buffer) = &diff_state.main_buffer {
- if let Ok(current_main_buffer) =
- main_buffer.read_with(cx, |buffer, _| buffer.text_snapshot())
- {
- if let Some(stored_diff) = buffer_diff.get(id) {
- if let Some(stored_main_buffer) = &stored_diff.main_buffer {
- if current_main_buffer
- .version()
- .changed_since(stored_main_buffer.version())
- {
- main_buffer_changed_diffs.push((
- *id,
- stored_diff.clone(),
- current_main_buffer,
- ));
- }
- }
- }
- }
- }
- }
-
- let mut inverted_diff_touch_info: HashMap<
- Locator,
- (
- BufferDiffSnapshot,
- text::BufferSnapshot,
- text::BufferSnapshot,
- ),
- > = HashMap::default();
- for (buffer_id, old_diff_snapshot, new_main_buffer) in &main_buffer_changed_diffs {
- if let Some(old_main_buffer) = &old_diff_snapshot.main_buffer {
- if let Some(buffer_state) = buffers.get(buffer_id) {
- for locator in &buffer_state.excerpts {
- inverted_diff_touch_info.insert(
- locator.clone(),
- (
- old_diff_snapshot.diff.clone(),
- old_main_buffer.clone(),
- new_main_buffer.clone(),
- ),
- );
- excerpts_to_edit.push((locator, buffer_state.buffer.clone(), false));
- }
- }
- }
- }
-
excerpts_to_edit.sort_unstable_by_key(|(locator, _, _)| *locator);
- excerpts_to_edit.dedup_by(|a, b| {
- if a.0 == b.0 {
- b.2 |= a.2;
- true
- } else {
- false
- }
- });
let mut edits = Vec::new();
let mut new_excerpts = SumTree::default();
@@ -3124,59 +3022,6 @@ impl MultiBuffer {
let buffer = buffer.read(cx);
let buffer_id = buffer.remote_id();
- let excerpt_old_start = cursor.start().1;
- let excerpt_new_start = ExcerptDimension(new_excerpts.summary().text.len);
-
- if !buffer_edited
- && let Some((old_diff, old_main_buffer, new_main_buffer)) =
- inverted_diff_touch_info.get(locator)
- {
- // TODO(split-diff) this iterates over all excerpts for all edited buffers;
- // it would be nice to be able to skip excerpts that weren't edited using
- // new_main_buffer.has_edits_since_in_range.
- let excerpt_buffer_start = old_excerpt
- .range
- .context
- .start
- .to_offset(&old_excerpt.buffer);
- let excerpt_buffer_end = excerpt_buffer_start + old_excerpt.text_summary.len;
-
- let mut hunks = old_diff
- .hunks_intersecting_base_text_range(
- excerpt_buffer_start..excerpt_buffer_end,
- old_main_buffer,
- )
- .chain(old_diff.hunks_intersecting_base_text_range(
- excerpt_buffer_start..excerpt_buffer_end,
- new_main_buffer,
- ))
- .filter(|hunk| {
- hunk.buffer_range.start.is_valid(&old_main_buffer)
- != hunk.buffer_range.start.is_valid(&new_main_buffer)
- })
- .collect::<Vec<_>>();
- hunks.sort_by(|l, r| {
- l.diff_base_byte_range
- .start
- .cmp(&r.diff_base_byte_range.start)
- });
-
- for hunk in hunks {
- let hunk_buffer_start = hunk.diff_base_byte_range.start;
- if hunk_buffer_start >= excerpt_buffer_start
- && hunk_buffer_start <= excerpt_buffer_end
- {
- let hunk_offset = hunk_buffer_start - excerpt_buffer_start;
- let old_hunk_pos = excerpt_old_start + hunk_offset;
- let new_hunk_pos = excerpt_new_start + hunk_offset;
- edits.push(Edit {
- old: old_hunk_pos..old_hunk_pos,
- new: new_hunk_pos..new_hunk_pos,
- });
- }
- }
- }
-
let mut new_excerpt;
if buffer_edited {
edits.extend(
@@ -3221,14 +3066,6 @@ impl MultiBuffer {
drop(cursor);
*excerpts = new_excerpts;
- for (buffer_id, _, new_main_buffer) in main_buffer_changed_diffs {
- if let Some(stored) = buffer_diff.get(&buffer_id) {
- let mut updated = stored.clone();
- updated.main_buffer = Some(new_main_buffer);
- buffer_diff.insert(buffer_id, updated);
- }
- }
-
Self::sync_diff_transforms(snapshot, edits, DiffChangeKind::BufferEdited)
}
@@ -3320,9 +3157,9 @@ impl MultiBuffer {
..
}) => excerpts.item().is_some_and(|excerpt| {
if let Some(diff) = snapshot.diffs.get(&excerpt.buffer_id)
- && let Some(main_buffer) = &diff.main_buffer
+ && diff.is_inverted
{
- return hunk.hunk_start_anchor.is_valid(main_buffer);
+ return true;
}
hunk.hunk_start_anchor.is_valid(&excerpt.buffer)
}),
@@ -3432,10 +3269,10 @@ impl MultiBuffer {
excerpt_buffer_start + edit.new.end.saturating_sub(excerpt_start);
let edit_buffer_end = edit_buffer_end.min(excerpt_buffer_end);
- if let Some(main_buffer) = &diff.main_buffer {
+ if diff.is_inverted {
for hunk in diff.hunks_intersecting_base_text_range(
edit_buffer_start..edit_buffer_end,
- main_buffer,
+ diff.original_buffer_snapshot(),
) {
did_expand_hunks = true;
let hunk_buffer_range = hunk.diff_base_byte_range.clone();
@@ -3974,13 +3811,13 @@ impl MultiBufferSnapshot {
self.lift_buffer_metadata(query_range.clone(), move |buffer, buffer_range| {
let diff = self.diffs.get(&buffer.remote_id())?;
let iter: Box<dyn Iterator<Item = (DiffHunk, &BufferSnapshot, bool)>> =
- if let Some(main_buffer) = &diff.main_buffer {
+ if diff.is_inverted {
let buffer_start = buffer.point_to_offset(buffer_range.start);
let buffer_end = buffer.point_to_offset(buffer_range.end);
Box::new(
diff.hunks_intersecting_base_text_range(
buffer_start..buffer_end,
- main_buffer,
+ diff.original_buffer_snapshot(),
)
.map(move |hunk| (hunk, buffer, true)),
)
@@ -4433,10 +4270,10 @@ impl MultiBufferSnapshot {
.to_offset(&excerpt.buffer);
if let Some(diff) = self.diffs.get(&excerpt.buffer_id) {
- if let Some(main_buffer) = diff.main_buffer.as_ref() {
+ if diff.is_inverted {
for hunk in diff.hunks_intersecting_base_text_range_rev(
excerpt_start..excerpt_end,
- &main_buffer,
+ diff.original_buffer_snapshot(),
) {
if hunk.diff_base_byte_range.end >= current_position {
continue;
@@ -4471,11 +4308,11 @@ impl MultiBufferSnapshot {
let Some(diff) = self.diffs.get(&excerpt.buffer_id) else {
continue;
};
- if let Some(main_buffer) = diff.main_buffer.as_ref() {
+ if diff.is_inverted {
let Some(hunk) = diff
.hunks_intersecting_base_text_range_rev(
excerpt.range.context.to_offset(&excerpt.buffer),
- main_buffer,
+ diff.original_buffer_snapshot(),
)
.next()
else {
@@ -6811,15 +6648,6 @@ impl MultiBufferSnapshot {
self.diffs.get(&buffer_id).map(|diff| &diff.diff)
}
- /// For inverted diffs (used in side-by-side diff view), returns the main buffer
- /// snapshot that the diff's anchors refer to. Returns `None` if the diff is not
- /// inverted or if there's no diff for the given buffer ID.
- pub fn inverted_diff_main_buffer(&self, buffer_id: BufferId) -> Option<&text::BufferSnapshot> {
- self.diffs
- .get(&buffer_id)
- .and_then(|diff| diff.main_buffer.as_ref())
- }
-
/// Visually annotates a position or range with the `Debug` representation of a value. The
/// callsite of this function is used as a key - previous annotations will be removed.
#[cfg(debug_assertions)]
@@ -487,7 +487,7 @@ async fn test_inverted_diff_hunks_in_range(cx: &mut TestAppContext) {
});
multibuffer.update(cx, |multibuffer, cx| {
- multibuffer.add_inverted_diff(diff, buffer.clone(), cx);
+ multibuffer.add_inverted_diff(diff, cx);
});
assert_new_snapshot(
@@ -2295,7 +2295,7 @@ async fn test_diff_hunks_with_multiple_excerpts(cx: &mut TestAppContext) {
struct ReferenceMultibuffer {
excerpts: Vec<ReferenceExcerpt>,
diffs: HashMap<BufferId, Entity<BufferDiff>>,
- inverted_diffs: HashMap<BufferId, (Entity<BufferDiff>, WeakEntity<Buffer>)>,
+ inverted_diffs: HashMap<BufferId, Entity<BufferDiff>>,
}
#[derive(Debug)]
@@ -2442,16 +2442,14 @@ impl ReferenceMultibuffer {
let buffer_id = buffer.remote_id();
let buffer_range = excerpt.range.to_offset(buffer);
- if let Some((diff, main_buffer)) = self.inverted_diffs.get(&buffer_id) {
+ if let Some(diff) = self.inverted_diffs.get(&buffer_id) {
let diff_snapshot = diff.read(cx).snapshot(cx);
- let main_buffer_snapshot = main_buffer
- .read_with(cx, |main_buffer, _| main_buffer.snapshot())
- .unwrap();
let mut offset = buffer_range.start;
- for hunk in diff_snapshot
- .hunks_intersecting_base_text_range(buffer_range.clone(), &main_buffer_snapshot)
- {
+ for hunk in diff_snapshot.hunks_intersecting_base_text_range(
+ buffer_range.clone(),
+ diff_snapshot.original_buffer_snapshot(),
+ ) {
let mut hunk_base_range = hunk.diff_base_byte_range.clone();
hunk_base_range.end = hunk_base_range.end.min(buffer_range.end);
@@ -2461,10 +2459,6 @@ impl ReferenceMultibuffer {
continue;
}
- if !hunk.buffer_range.start.is_valid(&main_buffer_snapshot) {
- continue;
- }
-
// Add the text before the hunk
if hunk_base_range.start >= offset {
let len = text.len();
@@ -2753,15 +2747,9 @@ impl ReferenceMultibuffer {
self.diffs.insert(buffer_id, diff);
}
- fn add_inverted_diff(
- &mut self,
- diff: Entity<BufferDiff>,
- main_buffer: Entity<Buffer>,
- cx: &App,
- ) {
+ fn add_inverted_diff(&mut self, diff: Entity<BufferDiff>, cx: &App) {
let base_text_buffer_id = diff.read(cx).base_text(cx).remote_id();
- self.inverted_diffs
- .insert(base_text_buffer_id, (diff, main_buffer.downgrade()));
+ self.inverted_diffs.insert(base_text_buffer_id, diff);
}
}
@@ -3133,9 +3121,9 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
excerpt_buffer.read_with(cx, |buffer, _| buffer.remote_id());
multibuffer.update(cx, |multibuffer, cx| {
if multibuffer.diff_for(excerpt_buffer_id).is_none() {
- if let Some(main_buffer) = inverted_main_buffer {
- reference.add_inverted_diff(diff.clone(), main_buffer.clone(), cx);
- multibuffer.add_inverted_diff(diff, main_buffer, cx);
+ if inverted_main_buffer.is_some() {
+ reference.add_inverted_diff(diff.clone(), cx);
+ multibuffer.add_inverted_diff(diff, cx);
} else {
reference.add_diff(diff.clone(), cx);
multibuffer.add_diff(diff, cx);
@@ -3877,62 +3865,6 @@ fn format_diff(
// .join("\n")
// }
-#[gpui::test]
-async fn test_inverted_diff_hunk_invalidation_on_main_buffer_edit(cx: &mut TestAppContext) {
- let text = "one\ntwo\nthree\n";
- let base_text = "one\nTWO\nthree\n";
-
- let buffer = cx.new(|cx| Buffer::local(text, 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 = diff.read_with(cx, |diff, _| diff.base_text_buffer());
-
- let multibuffer = cx.new(|cx| {
- let mut multibuffer = MultiBuffer::singleton(base_text_buffer.clone(), cx);
- multibuffer.add_inverted_diff(diff.clone(), buffer.clone(), cx);
- multibuffer
- });
-
- let (mut snapshot, mut subscription) = multibuffer.update(cx, |multibuffer, cx| {
- (multibuffer.snapshot(cx), multibuffer.subscribe())
- });
-
- assert_new_snapshot(
- &multibuffer,
- &mut snapshot,
- &mut subscription,
- cx,
- indoc!(
- "
- one
- - TWO
- three
- "
- ),
- );
-
- buffer.update(cx, |buffer, cx| {
- buffer.edit([(3..5, "")], None, cx);
- });
- cx.run_until_parked();
-
- assert_new_snapshot(
- &multibuffer,
- &mut snapshot,
- &mut subscription,
- cx,
- indoc!(
- "
- one
- TWO
- three
- "
- ),
- );
-}
-
#[gpui::test]
async fn test_singleton_with_inverted_diff(cx: &mut TestAppContext) {
let text = indoc!(
@@ -3965,7 +3897,7 @@ async fn test_singleton_with_inverted_diff(cx: &mut TestAppContext) {
let multibuffer = cx.new(|cx| {
let mut multibuffer = MultiBuffer::singleton(base_text_buffer.clone(), cx);
multibuffer.set_all_diff_hunks_expanded(cx);
- multibuffer.add_inverted_diff(diff.clone(), buffer.clone(), cx);
+ multibuffer.add_inverted_diff(diff.clone(), cx);
multibuffer
});
@@ -4102,6 +4034,65 @@ async fn test_singleton_with_inverted_diff(cx: &mut TestAppContext) {
);
}
+#[gpui::test]
+async fn test_inverted_diff_base_text_change(cx: &mut TestAppContext) {
+ let base_text = "aaa\nbbb\nccc\n";
+ let text = "ddd\n";
+ let buffer = cx.new(|cx| Buffer::local(text, 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 = diff.read_with(cx, |diff, _| diff.base_text_buffer());
+
+ let multibuffer = cx.new(|cx| {
+ let mut multibuffer = MultiBuffer::singleton(base_text_buffer.clone(), cx);
+ multibuffer.set_all_diff_hunks_expanded(cx);
+ multibuffer.add_inverted_diff(diff.clone(), cx);
+ multibuffer
+ });
+
+ let (mut snapshot, mut subscription) = multibuffer.update(cx, |multibuffer, cx| {
+ (multibuffer.snapshot(cx), multibuffer.subscribe())
+ });
+
+ assert_eq!(snapshot.text(), base_text);
+ assert_new_snapshot(
+ &multibuffer,
+ &mut snapshot,
+ &mut subscription,
+ cx,
+ indoc!(
+ "
+ - aaa
+ - bbb
+ - ccc
+ "
+ ),
+ );
+
+ let update = diff
+ .update(cx, |diff, cx| {
+ diff.update_diff(
+ buffer.read(cx).text_snapshot(),
+ Some("ddd\n".into()),
+ Some(true),
+ None,
+ cx,
+ )
+ })
+ .await;
+ diff.update(cx, |diff, cx| {
+ diff.set_snapshot(update, &buffer.read(cx).text_snapshot(), cx)
+ })
+ .detach();
+
+ let _hunks: Vec<_> = multibuffer
+ .read_with(cx, |multibuffer, cx| multibuffer.snapshot(cx))
+ .diff_hunks()
+ .collect();
+}
+
#[track_caller]
fn assert_excerpts_match(
multibuffer: &Entity<MultiBuffer>,