From 93d47c5d6db1714e90f8b49608d6298e277e19de Mon Sep 17 00:00:00 2001 From: Cole Miller Date: Wed, 4 Mar 2026 16:56:28 -0500 Subject: [PATCH] wip fixing tests Co-authored-by: Conrad Irwin --- crates/multi_buffer/src/anchor.rs | 3 +- crates/multi_buffer/src/multi_buffer.rs | 82 ++++++---- crates/multi_buffer/src/multi_buffer_tests.rs | 145 ++++++++++-------- crates/multi_buffer/src/path_key.rs | 66 +++++--- crates/multi_buffer/src/transaction.rs | 5 +- crates/text/src/patch.rs | 3 + crates/text/src/text.rs | 2 + 7 files changed, 184 insertions(+), 122 deletions(-) diff --git a/crates/multi_buffer/src/anchor.rs b/crates/multi_buffer/src/anchor.rs index 20fbe725237089bf8ff38b0272464002195ee519..ecb71344d1fbd055d099bb2d1faae48b571a151f 100644 --- a/crates/multi_buffer/src/anchor.rs +++ b/crates/multi_buffer/src/anchor.rs @@ -14,7 +14,7 @@ use text::BufferId; /// A multibuffer anchor derived from an anchor into a specific excerpted buffer. #[derive(Clone, Copy, Eq, PartialEq, Hash)] -pub(crate) struct ExcerptAnchor { +pub struct ExcerptAnchor { /// The position within the excerpt's underlying buffer. This is a stable /// reference that remains valid as the buffer text is edited. pub(crate) timestamp: clock::Lamport, @@ -190,6 +190,7 @@ impl ExcerptAnchor { } } + #[track_caller] pub(crate) fn in_buffer(path: PathKeyIndex, text_anchor: text::Anchor) -> Self { let Some(buffer_id) = text_anchor.buffer_id else { panic!("text_anchor must have a buffer_id"); diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index e4d8ad154089d241d1e694e72e4fc3071dff78ac..e174e481182cc1f5c544359df3ed3d1438141b38 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -101,6 +101,12 @@ pub struct ExcerptInfo { range: ExcerptRange, } +impl ExcerptInfo { + pub fn end_anchor(&self) -> Anchor { + Anchor::in_buffer(self.path_key_index, self.range.context.end) + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub enum Event { BufferUpdated { @@ -624,7 +630,6 @@ struct BufferStateSnapshot { pub struct MultiBufferSnapshot { excerpts: SumTree, buffers: TreeMap, - path_keys_by_buffer: TreeMap, path_keys_by_index: TreeMap, diffs: TreeMap, diff_transforms: SumTree, @@ -1749,7 +1754,6 @@ impl MultiBuffer { show_deleted_hunks: _, use_extended_diff_range: _, show_headers: _, - path_keys_by_buffer: _, path_keys_by_index: _, buffers, } = self.snapshot.get_mut(); @@ -1800,12 +1804,13 @@ impl MultiBuffer { .cursor::>>(()); diff_transforms.next(); let mut result = Vec::new(); - let Some(path_key) = snapshot.path_keys_by_buffer.get(&buffer_id) else { + let Some(buffer_state) = snapshot.buffers.get(&buffer_id) else { return result; }; + let path_key = buffer_state.path_key.clone(); while let Some(excerpt) = excerpts.item() - && &excerpt.path_key == path_key + && excerpt.path_key == path_key { let excerpt_start = excerpts.start().1; let excerpt_end = excerpt_start + excerpt.text_summary.lines; @@ -2456,7 +2461,7 @@ impl MultiBuffer { let snapshot = self.snapshot.borrow().clone(); let ranges = ranges.iter().map(move |range| { let excerpt_end = snapshot - .excerpt_at(range.end) + .excerpt_containing(range.end..range.end) .map(|excerpt| excerpt.end_anchor().into()) .unwrap_or(Anchor::Max); let range = range.to_point(&snapshot); @@ -2500,6 +2505,7 @@ impl MultiBuffer { return snapshot; } let edits = Self::sync_from_buffer_changes(snapshot, &self.buffers, &self.diffs, cx); + dbg!(&edits); if !edits.is_empty() { self.subscriptions.publish(edits); @@ -2518,7 +2524,6 @@ impl MultiBuffer { excerpts, diffs: buffer_diff, buffers: buffer_snapshots, - path_keys_by_buffer, path_keys_by_index: _, diff_transforms: _, non_text_state_update_count, @@ -2579,13 +2584,15 @@ impl MultiBuffer { let buffer_edited = current_version.changed_since(last_snapshot.buffer_snapshot.version()); + dbg!( + ¤t_version, + last_snapshot.buffer_snapshot.version().clone() + ); let buffer_non_text_state_updated = non_text_state_update_count > last_snapshot.buffer_snapshot.non_text_state_update_count(); - if (buffer_edited || buffer_non_text_state_updated) - && let Some(path_key) = path_keys_by_buffer.get(&buffer.remote_id()) - { + if buffer_edited || buffer_non_text_state_updated { paths_to_edit.push(( - path_key.clone(), + last_snapshot.path_key.clone(), buffer_state.buffer.clone(), if buffer_edited { Some(last_snapshot.buffer_snapshot.version().clone()) @@ -2617,6 +2624,7 @@ impl MultiBuffer { let mut cursor = excerpts.cursor::(()); for (path, buffer, prev_version) in paths_to_edit { + dbg!(&prev_version, buffer.read(cx).version()); new_excerpts.append(cursor.slice(&path, Bias::Left), ()); let old_excerpt = cursor.item().unwrap(); let buffer = buffer.read(cx); @@ -2635,9 +2643,10 @@ impl MultiBuffer { buffer .edits_since_in_range::( &prev_version, - old_excerpt.range.context.clone(), + dbg!(old_excerpt.range.context.clone()), ) .map(|edit| { + dbg!(&edit); let excerpt_old_start = cursor.start().len(); let excerpt_new_start = ExcerptDimension(new_excerpts.summary().text.len); @@ -2679,6 +2688,7 @@ impl MultiBuffer { excerpt_edits: Vec>, change_kind: DiffChangeKind, ) -> Vec> { + dbg!(&excerpt_edits); if excerpt_edits.is_empty() { return vec![]; } @@ -3121,7 +3131,7 @@ impl MultiBuffer { pub fn toggle_single_diff_hunk(&mut self, range: Range, cx: &mut Context) { let snapshot = self.snapshot(cx); let excerpt_end = snapshot - .excerpt_at(range.end) + .excerpt_containing(range.end..range.end) .map(|excerpt| excerpt.end_anchor().into()) .unwrap_or(Anchor::Max); let point_range = range.to_point(&snapshot); @@ -3316,10 +3326,11 @@ impl MultiBuffer { let path_key = self .snapshot .borrow() - .path_keys_by_buffer + .buffers .get(&buffer_ids.choose(rng).unwrap()) - .cloned() - .unwrap(); + .unwrap() + .path_key + .clone(); log::info!("Removing excerpts {:?}", path_key); self.remove_excerpts_for_path(path_key, cx); } @@ -3588,7 +3599,8 @@ impl MultiBufferSnapshot { let mut same_buffer_anchors = anchors.peeking_take_while(|a| a.buffer_id.is_some_and(|b| buffer_id == b)); - if let Some(path) = self.path_keys_by_buffer.get(&buffer_id) { + if let Some(buffer) = self.buffers.get(&buffer_id) { + let path = &buffer.path_key; let Some(mut next) = same_buffer_anchors.next() else { continue 'anchors; }; @@ -3664,6 +3676,7 @@ impl MultiBufferSnapshot { }) } + // todo!() can we make this not RangeBounds pub fn range_to_buffer_ranges( &self, range: R, @@ -4219,6 +4232,7 @@ impl MultiBufferSnapshot { } pub fn row_infos(&self, start_row: MultiBufferRow) -> MultiBufferRows<'_> { + dbg!(self.diff_transforms.is_empty()); let mut cursor = self.cursor::(); cursor.seek(&Point::new(start_row.0, 0)); let mut result = MultiBufferRows { @@ -5184,12 +5198,13 @@ impl MultiBufferSnapshot { } fn excerpts_for_buffer(&self, buffer_id: BufferId) -> impl Iterator { - if let Some(path_key) = self.path_keys_by_buffer.get(&buffer_id) { + if let Some(buffer_state) = self.buffers.get(&buffer_id) { + let path_key = buffer_state.path_key.clone(); let mut cursor = self.excerpts.cursor::(()); - cursor.seek_forward(path_key, Bias::Left); + cursor.seek_forward(&path_key, Bias::Left); Some(iter::from_fn(move || { let excerpt = cursor.item()?; - if &excerpt.path_key != path_key { + if excerpt.path_key != path_key { return None; } cursor.next(); @@ -6262,7 +6277,7 @@ impl MultiBufferSnapshot { } pub fn path_for_buffer(&self, buffer_id: BufferId) -> Option<&PathKey> { - self.path_keys_by_buffer.get(&buffer_id) + Some(&self.buffers.get(&buffer_id)?.path_key) } pub fn path_key_index_for_buffer(&self, buffer_id: BufferId) -> Option { @@ -6271,8 +6286,8 @@ impl MultiBufferSnapshot { } pub fn first_excerpt_for_buffer(&self, buffer_id: BufferId) -> Option<&Excerpt> { - let path_key = self.path_keys_by_buffer.get(&buffer_id)?; - self.first_excerpt_for_path(&path_key) + let path_key = &self.buffers.get(&buffer_id)?.path_key; + self.first_excerpt_for_path(path_key) } pub fn first_excerpt_for_path(&self, path_key: &PathKey) -> Option<&Excerpt> { @@ -6283,7 +6298,7 @@ impl MultiBufferSnapshot { } pub fn buffer_for_id(&self, id: BufferId) -> Option<&BufferSnapshot> { - self.buffer_for_path(self.path_keys_by_buffer.get(&id)?) + self.buffer_for_path(self.path_for_buffer(id)?) } fn try_path_for_anchor(&self, anchor: ExcerptAnchor) -> Option { @@ -6327,11 +6342,6 @@ impl MultiBufferSnapshot { // } } - // todo!() sort out excerpt_containing etc. - fn excerpt_at(&self, _position: impl ToOffset) -> Option<&Excerpt> { - todo!() - } - /// Returns the excerpt containing range and its offset start within the multibuffer or none if `range` spans multiple excerpts pub fn excerpt_containing( &self, @@ -6545,7 +6555,8 @@ impl MultiBufferSnapshot { fn check_invariants(&self) { let excerpts = self.excerpts.items(()); - let all_buffer_path_keys = HashSet::from_iter(self.path_keys_by_buffer.values().cloned()); + let all_buffer_path_keys = + HashSet::from_iter(self.buffers.values().map(|b| b.path_key.clone())); let all_excerpt_path_keys = HashSet::from_iter(excerpts.iter().map(|e| e.path_key.clone())); for (ix, excerpt) in excerpts.iter().enumerate() { @@ -6644,7 +6655,8 @@ where } self.excerpts.seek(&excerpt_position, Bias::Right); - if self.excerpts.item().is_none() && excerpt_position == *self.excerpts.start() { + if self.excerpts.item().is_none() && dbg!(excerpt_position) == *self.excerpts.start() { + dbg!("NONE"); self.excerpts.prev(); } } @@ -6813,6 +6825,7 @@ where fn build_region(&self) -> Option> { let excerpt = self.excerpts.item()?; + dbg!("GOT EXCERPT"); match self.diff_transforms.item()? { DiffTransform::DeletedHunk { buffer_id, @@ -6945,6 +6958,7 @@ impl Excerpt { range: ExcerptRange, has_trailing_newline: bool, ) -> Self { + dbg!(&path_key, &range, has_trailing_newline); Excerpt { path_key, path_key_index, @@ -7059,11 +7073,11 @@ impl Excerpt { } fn start_anchor(&self) -> ExcerptAnchor { - todo!() + ExcerptAnchor::in_buffer(self.path_key_index, self.range.context.start) } fn end_anchor(&self) -> ExcerptAnchor { - todo!() + ExcerptAnchor::in_buffer(self.path_key_index, self.range.context.end) } } @@ -7604,11 +7618,13 @@ impl Iterator for MultiBufferRows<'_> { } let mut region = self.cursor.region()?.clone(); + dbg!("REGION"); while self.point >= region.range.end { self.cursor.next(); if let Some(next_region) = self.cursor.region() { region = next_region.clone(); - } else if self.point == self.cursor.diff_transforms.end().output_dimension.0 { + } else if dbg!(self.point) == dbg!(self.cursor.diff_transforms.end().output_dimension.0) + { let multibuffer_row = MultiBufferRow(self.point.row); let last_excerpt = self .cursor diff --git a/crates/multi_buffer/src/multi_buffer_tests.rs b/crates/multi_buffer/src/multi_buffer_tests.rs index a7ef0fba2fbae126597e6462e53ee2bbb52ea751..876323b4bfb1031d7961d52e4e956acab08cc4be 100644 --- a/crates/multi_buffer/src/multi_buffer_tests.rs +++ b/crates/multi_buffer/src/multi_buffer_tests.rs @@ -1109,6 +1109,7 @@ fn test_singleton_multibuffer_anchors(cx: &mut App) { buffer.edit([(0..0, "X")], None, cx); buffer.edit([(5..5, "Y")], None, cx); }); + dbg!("-------"); let new_snapshot = multibuffer.read(cx).snapshot(cx); assert_eq!(old_snapshot.text(), "abcd"); @@ -2279,7 +2280,7 @@ struct ReferenceExcerpt { range: Range, } -#[derive(Debug)] +#[derive(Clone, Debug)] struct ReferenceRegion { buffer_id: Option, range: Range, @@ -2293,7 +2294,7 @@ impl ReferenceExcerpt { ExcerptInfo { path_key_index: self.path_key_index, buffer_id: self.buffer.read(cx).remote_id(), - range: self.range.clone(), + range: ExcerptRange::new(self.range.clone()), } } } @@ -2321,32 +2322,6 @@ impl ReferenceMultibuffer { } } - fn remove_excerpt(&mut self, info: ExcerptInfo, cx: &App) { - let ix = self - .excerpts - .iter() - .position(|excerpt| excerpt.info(cx) == info) - .unwrap(); - let excerpt = self.excerpts.remove(ix); - let buffer = excerpt.buffer.read(cx); - let buffer_id = buffer.remote_id(); - log::info!( - "Removing excerpt {}: {:?}", - ix, - buffer - .text_for_range(excerpt.range.to_offset(buffer)) - .collect::(), - ); - if !self - .excerpts - .iter() - .any(|excerpt| excerpt.buffer.read(cx).remote_id() == buffer_id) - { - self.diffs.remove(&buffer_id); - self.inverted_diffs.remove(&buffer_id); - } - } - fn set_excerpts( &mut self, path_key: PathKey, @@ -2631,7 +2606,7 @@ impl ReferenceMultibuffer { .iter() .position(|region| region.range.contains(&ix)) .map_or(RowInfo::default(), |region_ix| { - let region = ®ions[region_ix]; + let region = regions[region_ix].clone(); let buffer_row = region.buffer_range.as_ref().map(|buffer_range| { buffer_range.start.row + text[region.range.start..ix].matches('\n').count() as u32 @@ -2639,7 +2614,7 @@ impl ReferenceMultibuffer { let main_buffer = self .excerpts .iter() - .find(|e| e.info(cx) == region.excerpt_info.unwrap()) + .find(|e| e.info(cx) == region.excerpt_info.clone().unwrap()) .map(|e| e.buffer.clone()); let is_excerpt_start = region_ix == 0 || ®ions[region_ix - 1].excerpt_info != ®ion.excerpt_info @@ -2811,9 +2786,9 @@ async fn test_random_set_ranges(cx: &mut TestAppContext, mut rng: StdRng) { let mut last_end = None; let mut seen_ranges = Vec::default(); - for (_, buf, range) in snapshot.excerpts() { - let start = range.context.start.to_point(buf); - let end = range.context.end.to_point(buf); + for (buf, info) in snapshot.excerpts() { + let start = info.range.context.start.to_point(buf); + let end = info.range.context.end.to_point(buf); seen_ranges.push(start..end); if let Some(last_end) = last_end.take() { @@ -3228,23 +3203,30 @@ fn check_multibuffer( .unwrap() + 1 ); - let reference_ranges = reference - .excerpts - .iter() - .map(|excerpt| { - ( - excerpt.id, - excerpt.range.to_offset(&excerpt.buffer.read(cx).snapshot()), - ) - }) - .collect::>(); + // let reference_ranges = reference + // .excerpts + // .iter() + // .map(|excerpt| { + // ( + // excerpt.info(cx), + // excerpt.range.to_offset(&excerpt.buffer.read(cx).snapshot()), + // ) + // }) + // .collect::>(); for i in 0..snapshot.len().0 { + // todo!() this seems not useful let excerpt = snapshot .excerpt_containing(MultiBufferOffset(i)..MultiBufferOffset(i)) .unwrap(); + let reference_range = reference + .excerpts + .iter() + .find(|reference_excerpt| reference_excerpt.info(cx) == excerpt.excerpt.info()) + .map(|excerpt| excerpt.range.to_offset(&excerpt.buffer.read(cx).snapshot())) + .expect("corresponding excerpt should exist in reference multibuffer"); assert_eq!( - excerpt.buffer_range().start.0..excerpt.buffer_range().end.0, - reference_ranges[&excerpt.id()] + excerpt.buffer_range(&snapshot).start.0..excerpt.buffer_range(&snapshot).end.0, + reference_range ); } @@ -3641,7 +3623,6 @@ async fn test_summaries_for_anchors(cx: &mut TestAppContext) { }); cx.run_until_parked(); - let mut ids = vec![]; let multibuffer = cx.new(|cx| { let mut multibuffer = MultiBuffer::new(Capability::ReadWrite); multibuffer.set_all_diff_hunks_expanded(cx); @@ -3661,7 +3642,6 @@ async fn test_summaries_for_anchors(cx: &mut TestAppContext) { ); multibuffer.add_diff(diff_1.clone(), cx); multibuffer.add_diff(diff_2.clone(), cx); - ids = multibuffer.excerpt_ids(); multibuffer }); @@ -3685,11 +3665,27 @@ async fn test_summaries_for_anchors(cx: &mut TestAppContext) { ), ); - let anchor_1 = Anchor::text(ids[0], text::Anchor::MIN); + let anchor_1 = multibuffer.read_with(cx, |multibuffer, cx| { + multibuffer + .buffer_anchor_to_anchor( + &buffer_1, + text::Anchor::min_for_buffer(buffer_1.read(cx).remote_id()), + cx, + ) + .unwrap() + }); let point_1 = snapshot.summaries_for_anchors::([&anchor_1])[0]; assert_eq!(point_1, Point::new(0, 0)); - let anchor_2 = Anchor::text(ids[1], text::Anchor::MIN); + let anchor_2 = multibuffer.read_with(cx, |multibuffer, cx| { + multibuffer + .buffer_anchor_to_anchor( + &buffer_2, + text::Anchor::min_for_buffer(buffer_2.read(cx).remote_id()), + cx, + ) + .unwrap() + }); let point_2 = snapshot.summaries_for_anchors::([&anchor_2])[0]; assert_eq!(point_2, Point::new(3, 0)); } @@ -3748,7 +3744,7 @@ async fn test_trailing_deletion_without_newline(cx: &mut TestAppContext) { let (_, translated_offset) = snapshot.point_to_buffer_offset(Point::new(2, 0)).unwrap(); assert_eq!(translated_offset.0, "one\n".len()); - let (_, translated_point, _) = snapshot.point_to_buffer_point(Point::new(2, 0)).unwrap(); + let (_, translated_point) = snapshot.point_to_buffer_point(Point::new(2, 0)).unwrap(); assert_eq!(translated_point, Point::new(1, 0)); // The same, for an excerpt that's not at the end of the multibuffer. @@ -3791,7 +3787,7 @@ async fn test_trailing_deletion_without_newline(cx: &mut TestAppContext) { let (buffer, translated_offset) = snapshot.point_to_buffer_offset(Point::new(2, 0)).unwrap(); assert_eq!(buffer.remote_id(), buffer_1_id); assert_eq!(translated_offset.0, "one\n".len()); - let (buffer, translated_point, _) = snapshot.point_to_buffer_point(Point::new(2, 0)).unwrap(); + let (buffer, translated_point) = snapshot.point_to_buffer_point(Point::new(2, 0)).unwrap(); assert_eq!(buffer.remote_id(), buffer_1_id); assert_eq!(translated_point, Point::new(1, 0)); } @@ -3831,6 +3827,7 @@ fn format_diff( }; let expand = info .expand_info + .as_ref() .map(|expand_info| match expand_info.direction { ExpandExcerptDirection::Up => " [↑]", ExpandExcerptDirection::Down => " [↓]", @@ -4174,9 +4171,9 @@ fn assert_excerpts_match( ) { let mut output = String::new(); multibuffer.read_with(cx, |multibuffer, cx| { - for (_, buffer, range) in multibuffer.snapshot(cx).excerpts() { + for (buffer, info) in multibuffer.snapshot(cx).excerpts() { output.push_str("-----\n"); - output.extend(buffer.text_for_range(range.context)); + output.extend(buffer.text_for_range(info.range.context)); if !output.ends_with('\n') { output.push('\n'); } @@ -4396,7 +4393,7 @@ fn assert_position_translation(snapshot: &MultiBufferSnapshot) { fn assert_line_indents(snapshot: &MultiBufferSnapshot) { let max_row = snapshot.max_point().row; - let buffer_id = snapshot.excerpts().next().unwrap().1.remote_id(); + let buffer_id = snapshot.excerpts().next().unwrap().1.buffer_id; let text = text::Buffer::new(ReplicaId::LOCAL, buffer_id, snapshot.text()); let mut line_indents = text .line_indents_in_row_range(0..max_row + 1) @@ -4584,7 +4581,7 @@ fn test_random_chunk_bitmaps_with_diffs(cx: &mut App, mut rng: StdRng) { let mut diffs = Vec::new(); multibuffer.update(cx, |multibuffer, cx| { - for buffer_id in multibuffer.excerpt_buffer_ids() { + for buffer_id in multibuffer.all_buffer_ids() { if rng.random_bool(0.7) { if let Some(buffer_handle) = multibuffer.buffer(buffer_id) { let buffer_text = buffer_handle.read(cx).text(); @@ -4976,7 +4973,7 @@ fn test_range_to_buffer_ranges_with_range_bounds(cx: &mut App) { let buffer_2 = cx.new(|cx| Buffer::local("ccc", cx)); let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite)); - let (excerpt_1_id, excerpt_2_id) = multibuffer.update(cx, |multibuffer, cx| { + multibuffer.update(cx, |multibuffer, cx| { multibuffer.set_excerpts_for_path( PathKey::sorted(0), buffer_1.clone(), @@ -4992,10 +4989,6 @@ fn test_range_to_buffer_ranges_with_range_bounds(cx: &mut App) { 0, cx, ); - - let excerpt_ids = multibuffer.excerpt_ids(); - - (excerpt_ids[0], excerpt_ids[1]) }); let snapshot = multibuffer.read(cx).snapshot(cx); @@ -5009,7 +5002,7 @@ fn test_range_to_buffer_ranges_with_range_bounds(cx: &mut App) { 1, "Half-open range ending at excerpt start should EXCLUDE that excerpt" ); - assert_eq!(ranges_half_open[0].2, excerpt_1_id); + assert_eq!(ranges_half_open[0].1, BufferOffset(0)..BufferOffset(7)); let ranges_inclusive = snapshot.range_to_buffer_ranges(Point::zero()..=excerpt_2_start); assert_eq!( @@ -5017,8 +5010,16 @@ fn test_range_to_buffer_ranges_with_range_bounds(cx: &mut App) { 2, "Inclusive range ending at excerpt start should INCLUDE that excerpt" ); - assert_eq!(ranges_inclusive[0].2, excerpt_1_id); - assert_eq!(ranges_inclusive[1].2, excerpt_2_id); + assert_eq!(ranges_half_open[0].1, BufferOffset(0)..BufferOffset(7)); + assert_eq!( + ranges_half_open[0].0.remote_id(), + buffer_1.read(cx).remote_id() + ); + assert_eq!(ranges_half_open[1].1, BufferOffset(0)..BufferOffset(0)); + assert_eq!( + ranges_half_open[1].0.remote_id(), + buffer_2.read(cx).remote_id() + ); let ranges_unbounded = snapshot.range_to_buffer_ranges((Bound::Included(Point::zero()), Bound::Unbounded)); @@ -5027,8 +5028,16 @@ fn test_range_to_buffer_ranges_with_range_bounds(cx: &mut App) { 2, "Unbounded end should include all excerpts" ); - assert_eq!(ranges_unbounded[0].2, excerpt_1_id); - assert_eq!(ranges_unbounded[1].2, excerpt_2_id); + assert_eq!(ranges_half_open[0].1, BufferOffset(0)..BufferOffset(7)); + assert_eq!( + ranges_half_open[0].0.remote_id(), + buffer_1.read(cx).remote_id() + ); + assert_eq!(ranges_half_open[1].1, BufferOffset(0)..BufferOffset(3)); + assert_eq!( + ranges_half_open[1].0.remote_id(), + buffer_2.read(cx).remote_id() + ); let ranges_excluded_end = snapshot.range_to_buffer_ranges(( Bound::Included(Point::zero()), @@ -5039,7 +5048,11 @@ fn test_range_to_buffer_ranges_with_range_bounds(cx: &mut App) { 1, "Excluded end bound should exclude excerpt starting at that point" ); - assert_eq!(ranges_excluded_end[0].2, excerpt_1_id); + assert_eq!(ranges_half_open[0].1, BufferOffset(0)..BufferOffset(7)); + assert_eq!( + ranges_half_open[0].0.remote_id(), + buffer_1.read(cx).remote_id() + ); let buffer_empty = cx.new(|cx| Buffer::local("", cx)); let multibuffer_trailing_empty = cx.new(|_| MultiBuffer::new(Capability::ReadWrite)); diff --git a/crates/multi_buffer/src/path_key.rs b/crates/multi_buffer/src/path_key.rs index 67cd1b7a27e7c0575301596c0a62415e0c4a2c96..7a693a4e7113ceaad33e455b754f048c9b2d5784 100644 --- a/crates/multi_buffer/src/path_key.rs +++ b/crates/multi_buffer/src/path_key.rs @@ -278,8 +278,10 @@ impl MultiBuffer { let anchor_ranges = new .into_iter() .map(|r| ExcerptRange { - context: buffer_snapshot.anchor_range_around(r.context), - primary: buffer_snapshot.anchor_range_around(r.primary), + context: buffer_snapshot.anchor_before(r.context.start) + ..buffer_snapshot.anchor_after(r.context.end), + primary: buffer_snapshot.anchor_before(r.primary.start) + ..buffer_snapshot.anchor_after(r.primary.end), }) .collect::>(); self.update_path_excerpts(path, buffer, buffer_snapshot, &anchor_ranges, cx) @@ -335,26 +337,13 @@ impl MultiBuffer { let buffer_id = buffer_snapshot.remote_id(); - self.buffers.entry(buffer_id).or_insert_with(|| { - self.buffer_changed_since_sync.replace(true); - buffer.update(cx, |buffer, _| { - buffer.record_changes(Rc::downgrade(&self.buffer_changed_since_sync)); - }); - BufferState { - _subscriptions: [ - cx.observe(&buffer, |_, _, cx| cx.notify()), - cx.subscribe(&buffer, Self::on_buffer_event), - ], - buffer: buffer.clone(), - } - }); - let mut snapshot = self.snapshot.get_mut(); let mut cursor = snapshot .excerpts .cursor::>(()); let mut new_excerpts = SumTree::new(()); + let new_ranges = to_insert.clone(); let mut to_insert = to_insert.iter().peekable(); let mut patch = Patch::empty(); let mut added_new_excerpt = false; @@ -410,18 +399,19 @@ impl MultiBuffer { let next_excerpt = to_insert.next().unwrap(); added_new_excerpt = true; let before = new_excerpts.summary().len(); + new_excerpts.update_last(|excerpt| excerpt.has_trailing_newline = true, ()); new_excerpts.push( Excerpt::new( path_key.clone(), path_key_index, &buffer_snapshot, next_excerpt.clone(), - to_insert.peek().is_some(), + to_insert.peek().is_some() || cursor.item().is_some(), ), (), ); let after = new_excerpts.summary().len(); - patch.push(Edit { + patch.push_maybe_empty(Edit { old: cursor.position.1..cursor.position.1, new: before..after, }); @@ -437,6 +427,27 @@ impl MultiBuffer { new: new_excerpts.summary().len()..new_excerpts.summary().len(), }); + while let Some(next_excerpt) = to_insert.next() { + added_new_excerpt = true; + let before = new_excerpts.summary().len(); + new_excerpts.update_last(|excerpt| excerpt.has_trailing_newline = true, ()); + new_excerpts.push( + Excerpt::new( + path_key.clone(), + path_key_index, + &buffer_snapshot, + next_excerpt.clone(), + to_insert.peek().is_some() || cursor.item().is_some(), + ), + (), + ); + let after = new_excerpts.summary().len(); + patch.push_maybe_empty(Edit { + old: cursor.position.1..cursor.position.1, + new: before..after, + }); + } + let suffix = cursor.suffix(); let changed_trailing_excerpt = suffix.is_empty(); new_excerpts.append(suffix, ()); @@ -449,13 +460,28 @@ impl MultiBuffer { buffer_snapshot: buffer_snapshot.clone(), }, ); + + self.buffers.entry(buffer_id).or_insert_with(|| { + self.buffer_changed_since_sync.replace(true); + buffer.update(cx, |buffer, _| { + buffer.record_changes(Rc::downgrade(&self.buffer_changed_since_sync)); + }); + BufferState { + _subscriptions: [ + cx.observe(&buffer, |_, _, cx| cx.notify()), + cx.subscribe(&buffer, Self::on_buffer_event), + ], + buffer: buffer.clone(), + } + }); + if changed_trailing_excerpt { snapshot.trailing_excerpt_update_count += 1; } let edits = Self::sync_diff_transforms( &mut snapshot, - patch.into_inner(), + dbg!(patch.into_inner()), DiffChangeKind::BufferEdited, ); if !edits.is_empty() { @@ -468,7 +494,7 @@ impl MultiBuffer { cx.emit(Event::BufferUpdated { buffer, path_key: path_key.clone(), - ranges: to_insert, + ranges: new_ranges, }); cx.notify(); diff --git a/crates/multi_buffer/src/transaction.rs b/crates/multi_buffer/src/transaction.rs index cffbdc12704ab81d9557b3f0555dcdc19ce9e234..a3afe55cd6928b9e908d0249af5fb8fe7fc4bbe4 100644 --- a/crates/multi_buffer/src/transaction.rs +++ b/crates/multi_buffer/src/transaction.rs @@ -333,6 +333,7 @@ impl MultiBuffer { let Some(excerpt) = snapshot.first_excerpt_for_buffer(*buffer_id) else { continue; }; + let buffer_snapshot = buffer.read(cx).snapshot(); for range in buffer .read(cx) @@ -340,11 +341,11 @@ impl MultiBuffer { { buffer_anchors.push(Anchor::in_buffer( excerpt.path_key_index, - excerpt.buffer_snapshot.anchor_at(range.start, Bias::Left), + buffer_snapshot.anchor_at(range.start, Bias::Left), )); buffer_anchors.push(Anchor::in_buffer( excerpt.path_key_index, - excerpt.buffer_snapshot.anchor_at(range.end, Bias::Right), + buffer_snapshot.anchor_at(range.end, Bias::Right), )); } } diff --git a/crates/text/src/patch.rs b/crates/text/src/patch.rs index eff3d0af110763074d7ca9fdc7842d45eece03c1..376d284473d09df16b93a609c8d49c443aa8a4ab 100644 --- a/crates/text/src/patch.rs +++ b/crates/text/src/patch.rs @@ -56,7 +56,10 @@ where if edit.is_empty() { return; } + self.push_maybe_empty(edit); + } + pub fn push_maybe_empty(&mut self, edit: Edit) { if let Some(last) = self.0.last_mut() { if last.old.end >= edit.old.start { last.old.end = edit.old.end; diff --git a/crates/text/src/text.rs b/crates/text/src/text.rs index 79c6e746d010fc597b7ebdf7c407b63330665e52..300eee01ea6def3b99167cfcc517d3f21d41d7d3 100644 --- a/crates/text/src/text.rs +++ b/crates/text/src/text.rs @@ -2509,11 +2509,13 @@ impl BufferSnapshot { } /// Returns an anchor range for the given input position range that is anchored to the text in the range. + /// todo!() this name seems misleading pub fn anchor_range_around(&self, position: Range) -> Range { self.anchor_after(position.start)..self.anchor_before(position.end) } /// Returns an anchor range for the given input position range that is anchored to the text before and after. + /// todo!() this name seems misleading pub fn anchor_range_between(&self, position: Range) -> Range { self.anchor_before(position.start)..self.anchor_after(position.end) }