diff --git a/crates/buffer_diff/src/buffer_diff.rs b/crates/buffer_diff/src/buffer_diff.rs index 55de3f968bc1cc9ff5d640b0d3ca30221e413632..fea0c379a5fee11fe9318b28a6701b88f11a4dea 100644 --- a/crates/buffer_diff/src/buffer_diff.rs +++ b/crates/buffer_diff/src/buffer_diff.rs @@ -336,6 +336,14 @@ impl BufferDiffSnapshot { self.inner.hunks_intersecting_range_rev(range, buffer) } + pub fn hunks_intersecting_base_text_range<'a>( + &'a self, + range: Range, + ) -> impl 'a + Iterator { + // FIXME + std::iter::empty() + } + pub fn base_text(&self) -> &language::BufferSnapshot { &self.inner.base_text } @@ -1060,6 +1068,7 @@ impl std::fmt::Debug for BufferDiff { pub enum BufferDiffEvent { DiffChanged { changed_range: Option>, + base_text_changed_range: Option>, }, LanguageChanged, HunksStagedOrUnstaged(Option), @@ -1127,6 +1136,7 @@ impl BufferDiff { }); cx.emit(BufferDiffEvent::DiffChanged { changed_range: Some(Anchor::min_max_range_for_buffer(self.buffer_id)), + base_text_changed_range: todo!(), }); } } @@ -1154,6 +1164,7 @@ impl BufferDiff { let changed_range = first.buffer_range.start..last.buffer_range.end; cx.emit(BufferDiffEvent::DiffChanged { changed_range: Some(changed_range), + base_text_changed_range: todo!(), }); } new_index_text @@ -1284,6 +1295,7 @@ impl BufferDiff { cx.emit(BufferDiffEvent::DiffChanged { changed_range: changed_range.clone(), + base_text_changed_range: todo!(), }); changed_range } diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index 3e9d113a300fad6f8c29221d0f886497793fafc9..abdd6e36257769ac11ee0010000c5c7878e34604 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -506,14 +506,39 @@ struct BufferState { struct DiffState { diff: Entity, + base_text_buffer_id: Option, _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() + } +} + +// FIXME +impl std::ops::Deref for DiffStateSnapshot { + type Target = BufferDiffSnapshot; + + fn deref(&self) -> &Self::Target { + &self.diff + } +} + impl DiffState { fn new(diff: Entity, cx: &mut Context) -> Self { DiffState { _subscription: cx.subscribe(&diff, |this, diff, event, cx| match event { - BufferDiffEvent::DiffChanged { changed_range } => { + BufferDiffEvent::DiffChanged { + changed_range, + base_text_changed_range: _, + } => { if let Some(changed_range) = changed_range.clone() { this.buffer_diff_changed(diff, changed_range, cx) } @@ -523,15 +548,50 @@ impl DiffState { _ => {} }), diff, + base_text_buffer_id: None, + } + } + + fn new_inverted( + diff: Entity, + base_text_buffer_id: BufferId, + cx: &mut Context, + ) -> Self { + DiffState { + _subscription: cx.subscribe(&diff, move |this, diff, event, cx| match event { + BufferDiffEvent::DiffChanged { + changed_range: _, + base_text_changed_range, + } => { + if let Some(base_text_changed_range) = base_text_changed_range.clone() { + this.inverted_buffer_diff_changed( + diff, + base_text_changed_range, + base_text_buffer_id, + cx, + ) + } + cx.emit(Event::BufferDiffChanged); + } + // FIXME + BufferDiffEvent::LanguageChanged => this.buffer_diff_language_changed(diff, cx), + _ => {} + }), + diff, + base_text_buffer_id: Some(base_text_buffer_id), } } + + fn snapshot(&self, cx: &App) -> DiffStateSnapshot { + todo!() + } } /// The contents of a [`MultiBuffer`] at a single point in time. #[derive(Clone, Default)] pub struct MultiBufferSnapshot { excerpts: SumTree, - diffs: TreeMap, + diffs: TreeMap, diff_transforms: SumTree, non_text_state_update_count: usize, edit_count: usize, @@ -2262,6 +2322,87 @@ impl MultiBuffer { }); } + fn inverted_buffer_diff_changed( + &mut self, + diff: Entity, + diff_change_range: Range, + base_text_buffer_id: BufferId, + cx: &mut Context, + ) { + let diff = diff.read(cx); + let new_diff = diff.snapshot(cx); + let snapshot = self.snapshot.get_mut(); + 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); + }); + } + self.sync_mut(cx); + + let Some(buffer_state) = self.buffers.get(&base_text_buffer_id) else { + return; + }; + 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); + + let mut excerpt_edits = Vec::new(); + for locator in &buffer_state.excerpts { + let mut cursor = snapshot + .excerpts + .cursor::, ExcerptOffset>>(()); + cursor.seek_forward(&Some(locator), Bias::Left); + if let Some(excerpt) = cursor.item() + && excerpt.locator == *locator + { + let excerpt_buffer_range = excerpt.range.context.to_offset(&excerpt.buffer); + if diff_change_range.end < excerpt_buffer_range.start + || diff_change_range.start > excerpt_buffer_range.end + { + continue; + } + let excerpt_start = cursor.start().1; + let excerpt_len = excerpt.text_summary.len; + let diff_change_start_in_excerpt = diff_change_range + .start + .saturating_sub(excerpt_buffer_range.start); + let diff_change_end_in_excerpt = diff_change_range + .end + .saturating_sub(excerpt_buffer_range.start); + let edit_start = excerpt_start + diff_change_start_in_excerpt.min(excerpt_len); + let edit_end = excerpt_start + diff_change_end_in_excerpt.min(excerpt_len); + excerpt_edits.push(Edit { + old: edit_start..edit_end, + new: edit_start..edit_end, + }); + } + } + + let edits = Self::sync_diff_transforms( + &mut snapshot, + excerpt_edits, + DiffChangeKind::DiffUpdated { + base_changed: base_text_changed, + }, + ); + if !edits.is_empty() { + self.subscriptions.publish(edits); + } + cx.emit(Event::Edited { + edited_buffer: None, + }); + } + pub fn all_buffers(&self) -> HashSet> { self.buffers .values() @@ -2418,6 +2559,8 @@ impl MultiBuffer { self.diffs.insert(buffer_id, DiffState::new(diff, cx)); } + // FIXME add_inverted_diff + pub fn diff_for(&self, buffer_id: BufferId) -> Option> { self.diffs.get(&buffer_id).map(|state| state.diff.clone()) } @@ -3082,9 +3225,41 @@ impl MultiBuffer { let edit_buffer_end = 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() { + for hunk in + diff.hunks_intersecting_base_text_range(edit_buffer_start..edit_buffer_end) + { + let hunk_buffer_range = hunk.diff_base_byte_range.clone(); + if hunk_buffer_range.start < excerpt_buffer_start { + log::trace!("skipping hunk that starts before excerpt"); + continue; + } + let hunk_excerpt_start = excerpt_start + + hunk_buffer_range.start.saturating_sub(excerpt_buffer_start); + let hunk_excerpt_end = excerpt_end + .min(excerpt_start + (hunk_buffer_range.end - excerpt_buffer_start)); + Self::push_buffer_content_transform( + snapshot, + new_diff_transforms, + hunk_excerpt_start, + *end_of_current_insert, + ); + if !hunk_buffer_range.is_empty() { + let hunk_info = DiffTransformHunkInfo { + excerpt_id: excerpt.id, + hunk_start_anchor: hunk.buffer_range.start, + hunk_secondary_status: hunk.secondary_status, + }; + *end_of_current_insert = + Some((hunk_excerpt_end.min(excerpt_end), hunk_info)); + } + } + continue; + } + let edit_anchor_range = buffer.anchor_before(edit_buffer_start)..buffer.anchor_after(edit_buffer_end); - for hunk in diff.hunks_intersecting_range(edit_anchor_range, buffer) { if hunk.is_created_file() && !all_diff_hunks_expanded { continue;