From b74ea0fc3c1ecbf394a0f091f6011d391518f24b Mon Sep 17 00:00:00 2001 From: Cole Miller Date: Wed, 3 Dec 2025 13:28:10 -0500 Subject: [PATCH] checkpoint Co-authored-by: cameron --- crates/buffer_diff/src/buffer_diff.rs | 1 - crates/multi_buffer/src/multi_buffer.rs | 83 ++++++++++++++++--------- crates/multi_buffer/src/path_key.rs | 75 +++++++++++++++++++++- 3 files changed, 127 insertions(+), 32 deletions(-) diff --git a/crates/buffer_diff/src/buffer_diff.rs b/crates/buffer_diff/src/buffer_diff.rs index fea0c379a5fee11fe9318b28a6701b88f11a4dea..175031e77a65561b24e144b1fc2f839404d29193 100644 --- a/crates/buffer_diff/src/buffer_diff.rs +++ b/crates/buffer_diff/src/buffer_diff.rs @@ -36,7 +36,6 @@ pub struct BufferDiffSnapshot { #[derive(Clone)] struct BufferDiffInner { hunks: SumTree, - // Used for making staging mo pending_hunks: SumTree, base_text: language::BufferSnapshot, base_text_exists: bool, diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index abdd6e36257769ac11ee0010000c5c7878e34604..56a54b64fdda158c971a21a36ba2a97ce3fce896 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -87,6 +87,8 @@ pub struct MultiBuffer { /// The writing capability of the multi-buffer. capability: Capability, buffer_changed_since_sync: Rc>, + follower: Option>, + base_text_buffers_by_main_buffer_id: HashMap>, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -506,20 +508,14 @@ struct BufferState { struct DiffState { diff: Entity, - base_text_buffer_id: Option, + is_inverted: bool, _subscription: gpui::Subscription, } #[derive(Clone)] struct DiffStateSnapshot { diff: BufferDiffSnapshot, - base_text_buffer_id: Option, -} - -impl DiffStateSnapshot { - fn is_inverted(&self) -> bool { - self.base_text_buffer_id.is_some() - } + is_inverted: bool, } // FIXME @@ -548,13 +544,13 @@ impl DiffState { _ => {} }), diff, - base_text_buffer_id: None, + is_inverted: false, } } fn new_inverted( diff: Entity, - base_text_buffer_id: BufferId, + base_text_buffer: Entity, cx: &mut Context, ) -> Self { DiffState { @@ -567,7 +563,7 @@ impl DiffState { this.inverted_buffer_diff_changed( diff, base_text_changed_range, - base_text_buffer_id, + base_text_buffer.clone(), cx, ) } @@ -578,7 +574,7 @@ impl DiffState { _ => {} }), diff, - base_text_buffer_id: Some(base_text_buffer_id), + is_inverted: true, } } @@ -607,12 +603,24 @@ pub struct MultiBufferSnapshot { show_headers: bool, } +// follower: None +// - BufferContent(Some) +// - BufferContent(None) +// - DeletedHunk +// +// follower: Some +// - BufferContent(Some) +// - BufferContent(None) + #[derive(Debug, Clone)] enum DiffTransform { + // RealText BufferContent { summary: MBTextSummary, + // modified_hunk_info inserted_hunk_info: Option, }, + // ExpandedHunkText DeletedHunk { summary: TextSummary, buffer_id: BufferId, @@ -2322,27 +2330,25 @@ impl MultiBuffer { }); } + // FIXME should take main_text_buffer_id right? fn inverted_buffer_diff_changed( &mut self, diff: Entity, diff_change_range: Range, - base_text_buffer_id: BufferId, + base_text_buffer: Entity, cx: &mut Context, ) { - let diff = diff.read(cx); - let new_diff = diff.snapshot(cx); + let base_text_buffer_id = base_text_buffer.read(cx).remote_id(); let snapshot = self.snapshot.get_mut(); + let new_diff = diff.read(cx).snapshot(cx); let base_text_changed = snapshot .diffs .get(&base_text_buffer_id) .is_none_or(|old_diff| !new_diff.base_texts_eq(old_diff)); if base_text_changed { - let Some(base_text_buffer) = self.buffers.get(&base_text_buffer_id) else { - return; - }; - base_text_buffer.buffer.update(cx, |buffer, cx| { - // FIXME take the rope directly - buffer.set_text(new_diff.base_text().text(), cx); + base_text_buffer.update(cx, |buffer, cx| { + // FIXME use the rope directly + buffer.set_text(diff.read(cx).base_text().text(), cx); }); } self.sync_mut(cx); @@ -2352,9 +2358,13 @@ impl MultiBuffer { }; self.buffer_changed_since_sync.replace(true); let mut snapshot = self.snapshot.get_mut(); - snapshot - .diffs - .insert_or_replace(base_text_buffer_id, new_diff); + snapshot.diffs.insert_or_replace( + base_text_buffer_id, + DiffStateSnapshot { + diff: new_diff, + is_inverted: true, + }, + ); let mut excerpt_edits = Vec::new(); for locator in &buffer_state.excerpts { @@ -2556,11 +2566,27 @@ impl MultiBuffer { text::Anchor::min_max_range_for_buffer(buffer_id), cx, ); - self.diffs.insert(buffer_id, DiffState::new(diff, cx)); + self.diffs + .insert(buffer_id, DiffState::new(diff.clone(), cx)); + + if let Some(follower) = &self.follower { + follower.update(cx, |follower, cx| { + let diff_change_range = 0..diff.read(cx).base_text().len(); + let base_text_buffer: Entity = create_base_text_buffer(&diff); + follower.inverted_buffer_diff_changed( + diff, + diff_change_range, + base_text_buffer.clone(), + cx, + ); + follower.diffs.insert( + base_text_buffer.read(cx).remote_id(), + DiffState::new_inverted(diff, base_text_buffer, cx), + ); + }); + } } - // FIXME add_inverted_diff - pub fn diff_for(&self, buffer_id: BufferId) -> Option> { self.diffs.get(&buffer_id).map(|state| state.diff.clone()) } @@ -3226,7 +3252,7 @@ impl MultiBuffer { excerpt_buffer_start + edit.new.end.saturating_sub(excerpt_start); let edit_buffer_end = edit_buffer_end.min(excerpt_buffer_end); - if diff.is_inverted() { + if diff.is_inverted { for hunk in diff.hunks_intersecting_base_text_range(edit_buffer_start..edit_buffer_end) { @@ -3245,6 +3271,7 @@ impl MultiBuffer { hunk_excerpt_start, *end_of_current_insert, ); + // FIXME record that the status for this region should be "deleted" if !hunk_buffer_range.is_empty() { let hunk_info = DiffTransformHunkInfo { excerpt_id: excerpt.id, diff --git a/crates/multi_buffer/src/path_key.rs b/crates/multi_buffer/src/path_key.rs index 1685e7a27329b1beea5f0d2c9563acfab07d8d8b..79092d06a00a5720959f22289a8e2013a6e12d89 100644 --- a/crates/multi_buffer/src/path_key.rs +++ b/crates/multi_buffer/src/path_key.rs @@ -6,7 +6,7 @@ use itertools::Itertools; use language::{Buffer, BufferSnapshot}; use rope::Point; use text::{Bias, BufferId, OffsetRangeExt, locator::Locator}; -use util::{post_inc, rel_path::RelPath}; +use util::{debug_panic, post_inc, rel_path::RelPath}; use crate::{ Anchor, ExcerptId, ExcerptRange, ExpandExcerptDirection, MultiBuffer, build_excerpt_ranges, @@ -68,6 +68,26 @@ impl MultiBuffer { self.excerpts_by_path.keys() } + + // need: + // MultiBuffer::add_inverted_diff + // + // SplittableEditor will handle: + // - creating diff base buffers + // - calling add_diff on one side and add_inverted_diff on the other side + // - calling set_excerpts_for_path on both sides (using the diff base buffers for the LHS) + // - and translating excerpt ranges for the LHS + // - we have to make very sure that at all times, the sequence of excerpts on the two sides is the same + + // let b = Buffer::new(text); + // let mb = MutliBuffer::new([b1, b2, b3], ...); + // mb.attach_diff_hunks(...); + // let mb2 = mb.inverted(); + // + // fn inverted(&self) -> Self { + // + // } + /// Sets excerpts, returns `true` if at least one new excerpt was added. pub fn set_excerpts_for_path( &mut self, @@ -153,6 +173,7 @@ impl MultiBuffer { } } + // FIXME need to sync excerpt removal to the follower pub fn remove_excerpts_for_buffer(&mut self, buffer: BufferId, cx: &mut Context) { self.remove_excerpts( self.excerpts_for_buffer(buffer, cx) @@ -273,6 +294,14 @@ impl MultiBuffer { (result, added_a_new_excerpt) } + // SplittableEditor (SplitEditor | Editor) + // - lhs: Editor + // - mb: MultiBuffer + // - rhs: Editor + // - mb: MultiBuffer + // + // editor.rhs.mb.follower = Some(editor.lhs.mb) + // editor.lhs.mb.has_inverted_diffs = true fn update_path_excerpts( &mut self, path: PathKey, @@ -293,7 +322,7 @@ impl MultiBuffer { .get(&path) .cloned() .unwrap_or_default(); - let mut new_iter = new.into_iter().peekable(); + let mut new_iter = new.iter().cloned().peekable(); let mut existing_iter = existing.into_iter().peekable(); let mut excerpt_ids = Vec::new(); @@ -427,7 +456,47 @@ impl MultiBuffer { let snapshot = &*self.snapshot.get_mut(); let mut excerpt_ids: Vec<_> = excerpt_ids.iter().dedup().cloned().collect(); excerpt_ids.sort_by_cached_key(|&id| snapshot.excerpt_locator_for_id(id)); - self.excerpts_by_path.insert(path, excerpt_ids); + self.excerpts_by_path.insert(path.clone(), excerpt_ids); + } + + if let Some(follower) = &self.follower { + if let Some(diff) = snapshot.diffs.get(&buffer_snapshot.remote_id()) { + follower.update(cx, |follower, cx| { + let Some(base_text_buffer) = follower + .base_text_buffers_by_main_buffer_id + .get(&buffer_snapshot.remote_id()) + .cloned() + else { + return; + }; + let new = new + .into_iter() + .map(|range| { + let point_to_base_text_point = |point: Point| { + let row = diff.row_to_base_text_row(point.row, buffer_snapshot); + let column = diff.base_text().line_len(row); + Point::new(row, column) + }; + ExcerptRange { + primary: point_to_base_text_point(range.primary.start) + ..point_to_base_text_point(range.primary.end), + context: point_to_base_text_point(range.context.start) + ..point_to_base_text_point(range.context.end), + } + }) + .collect(); + let base_text_buffer_snapshot = base_text_buffer.read(cx).snapshot(); + follower.update_path_excerpts( + path, + base_text_buffer, + &base_text_buffer_snapshot, + new, + cx, + ); + }); + } else { + // FIXME + } } (excerpt_ids, added_a_new_excerpt)