From eaea701a2f3b8fc024492250bc80b47a1e0567c7 Mon Sep 17 00:00:00 2001 From: Cole Miller Date: Wed, 4 Mar 2026 18:59:38 -0500 Subject: [PATCH] start fixing randomized test Co-authored-by: Conrad Irwin --- crates/agent_ui/src/agent_diff.rs | 2 +- crates/agent_ui/src/inline_assistant.rs | 7 +- crates/diagnostics/src/diagnostics.rs | 2 +- crates/editor/src/editor_tests.rs | 2 +- crates/editor/src/inlays/inlay_hints.rs | 2 +- crates/editor/src/split.rs | 8 +- crates/multi_buffer/src/multi_buffer.rs | 226 +++++++----------- crates/multi_buffer/src/multi_buffer_tests.rs | 152 ++++-------- crates/multi_buffer/src/path_key.rs | 33 ++- crates/text/src/anchor.rs | 5 + 10 files changed, 170 insertions(+), 269 deletions(-) diff --git a/crates/agent_ui/src/agent_diff.rs b/crates/agent_ui/src/agent_diff.rs index b02af97881cff92714641b7f4e3fd10601e0685f..c218d3f46e64f1d5ff0f1cd4f7fde36be09c661a 100644 --- a/crates/agent_ui/src/agent_diff.rs +++ b/crates/agent_ui/src/agent_diff.rs @@ -212,7 +212,7 @@ impl AgentDiffPane { self.multibuffer.update(cx, |multibuffer, cx| { for path in paths_to_delete { - multibuffer.remove_excerpts_for_path(path, cx); + multibuffer.remove_excerpts(path, cx); } }); diff --git a/crates/agent_ui/src/inline_assistant.rs b/crates/agent_ui/src/inline_assistant.rs index 980a4e9f22407d5cf490e2e37996a31e1ffb7c55..972856d71b74dd067444f3e695d94b46a8aec86c 100644 --- a/crates/agent_ui/src/inline_assistant.rs +++ b/crates/agent_ui/src/inline_assistant.rs @@ -442,10 +442,9 @@ impl InlineAssistant { let newest_selection = newest_selection.unwrap(); let mut codegen_ranges = Vec::new(); - for (buffer, buffer_range, excerpt_id) in - snapshot.ranges_to_buffer_ranges(selections.iter().map(|selection| { - snapshot.anchor_before(selection.start)..snapshot.anchor_after(selection.end) - })) + for (buffer, buffer_range, excerpt_id) in selections + .iter() + .map(|selection| selection.range_to_buffer_range(selection.start..selection.end)) { let anchor_range = Anchor::range_in_buffer( excerpt_id, diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 57ce6f03d2b56c9441bee763a28dcc7010f8311e..486a81e09c5b044ee51181d91120236d746a93e2 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -329,7 +329,7 @@ impl ProjectDiagnosticsEditor { continue; } self.multibuffer.update(cx, |b, cx| { - b.remove_excerpts_for_path(PathKey::for_buffer(&buffer, cx), cx); + b.remove_excerpts(PathKey::for_buffer(&buffer, cx), cx); }); } } diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 330ff08067cfc3ef392433ae2ca7b9c49be4aa70..2b5b319e655386f1a31c8cdcd839be37a972f447 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -18550,7 +18550,7 @@ async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) { // Remove some excerpts. leader.update(cx, |leader, cx| { leader.buffer.update(cx, |multibuffer, cx| { - multibuffer.remove_excerpts_for_path( + multibuffer.remove_excerpts( PathKey::with_sort_prefix(1, rel_path("b.txt").into_arc()), cx, ); diff --git a/crates/editor/src/inlays/inlay_hints.rs b/crates/editor/src/inlays/inlay_hints.rs index 4158ebbf7c5c3594dc4f9f43e8c3a7f1a19c38cb..621f147133aff5d54292e3d8a9cd3286a1ee848b 100644 --- a/crates/editor/src/inlays/inlay_hints.rs +++ b/crates/editor/src/inlays/inlay_hints.rs @@ -3060,7 +3060,7 @@ let c = 3;"# editor .update(cx, |editor, _, cx| { editor.buffer().update(cx, |multibuffer, cx| { - multibuffer.remove_excerpts_for_path(PathKey::sorted(1), cx); + multibuffer.remove_excerpts(PathKey::sorted(1), cx); }) }) .unwrap(); diff --git a/crates/editor/src/split.rs b/crates/editor/src/split.rs index 4155846f401357c558f692f6b45396c3b252156e..77ca7dfb86ee88af630c12a4cce68e31f95e42b7 100644 --- a/crates/editor/src/split.rs +++ b/crates/editor/src/split.rs @@ -1074,7 +1074,7 @@ impl SplittableEditor { pub fn remove_excerpts_for_path(&mut self, path: PathKey, cx: &mut Context) { let Some(lhs) = &self.lhs else { self.rhs_multibuffer.update(cx, |rhs_multibuffer, cx| { - rhs_multibuffer.remove_excerpts_for_path(path, cx); + rhs_multibuffer.remove_excerpts(path, cx); }); return; }; @@ -1095,10 +1095,10 @@ impl SplittableEditor { } self.rhs_multibuffer.update(cx, |rhs_multibuffer, cx| { - rhs_multibuffer.remove_excerpts_for_path(path.clone(), cx); + rhs_multibuffer.remove_excerpts(path.clone(), cx); }); lhs.multibuffer.update(cx, |lhs_multibuffer, cx| { - lhs_multibuffer.remove_excerpts_for_path(path, cx); + lhs_multibuffer.remove_excerpts(path, cx); }); } @@ -1142,7 +1142,7 @@ impl SplittableEditor { rhs_multibuffer.excerpts_for_path(&path).collect(); let Some(excerpt_id) = rhs_excerpt_ids.first().copied() else { lhs.multibuffer.update(cx, |lhs_multibuffer, lhs_cx| { - lhs_multibuffer.remove_excerpts_for_path(path, lhs_cx); + lhs_multibuffer.remove_excerpts(path, lhs_cx); }); continue; }; diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index 495ddfc84f639bc2ca56c5119ab9a4179e0ca49c..80f8e685047a00d0dde5ed1537936db080a3b769 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -51,9 +51,9 @@ use std::{ sync::{Arc, OnceLock}, time::Duration, }; -use sum_tree::{Bias, Cursor, Dimension, Dimensions, SumTree, TreeMap}; +use sum_tree::{Bias, Cursor, Dimension, Dimensions, Item as _, SumTree, TreeMap}; use text::{ - BufferId, Edit, LineIndent, TextSummary, + AnchorRangeExt as _, BufferId, Edit, LineIndent, TextSummary, subscription::{Subscription, Topic}, }; use theme::SyntaxTheme; @@ -98,12 +98,12 @@ pub struct PathKeyIndex(u64); pub struct ExcerptInfo { path_key_index: PathKeyIndex, buffer_id: BufferId, - range: ExcerptRange, + range: Range, } impl ExcerptInfo { pub fn end_anchor(&self) -> Anchor { - Anchor::in_buffer(self.path_key_index, self.range.context.end) + Anchor::in_buffer(self.path_key_index, self.range.end) } } @@ -1883,27 +1883,17 @@ impl MultiBuffer { let snapshot = buffer.read(cx).snapshot(); let text_anchor = snapshot.anchor_after(&point); for excerpt in self.excerpts_for_buffer(snapshot.remote_id(), cx) { - if excerpt.range.contains(&text_anchor, &snapshot) { - found = Some(Anchor::in_buffer(excerpt.path_key_index, text_anchor)); - break; - } - if excerpt - .range - .context - .start - .cmp(&text_anchor, &snapshot) - .is_gt() - { + if excerpt.range.start.cmp(&text_anchor, &snapshot).is_gt() { found = Some(Anchor::in_buffer( excerpt.path_key_index, - excerpt.range.context.start, + excerpt.range.start, )); break; + } else if excerpt.range.end.cmp(&text_anchor, &snapshot).is_ge() { + found = Some(Anchor::in_buffer(excerpt.path_key_index, text_anchor)); + break; } - found = Some(Anchor::in_buffer( - excerpt.path_key_index, - excerpt.range.context.end, - )); + found = Some(Anchor::in_buffer(excerpt.path_key_index, excerpt.range.end)); } found @@ -1918,7 +1908,7 @@ impl MultiBuffer { ) -> Option { let snapshot = buffer.read(cx).snapshot(); for excerpt in self.excerpts_for_buffer(snapshot.remote_id(), cx) { - if excerpt.range.contains(&anchor, &snapshot) { + if excerpt.range.contains_anchor(anchor, &snapshot) { return Some(Anchor::in_buffer(excerpt.path_key_index, anchor)); } } @@ -2509,7 +2499,6 @@ 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); @@ -2588,10 +2577,6 @@ 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 { @@ -2622,15 +2607,14 @@ impl MultiBuffer { } paths_to_edit.sort_unstable_by_key(|(path, _, _)| path.clone()); + dbg!(&paths_to_edit); let mut edits = Vec::new(); let mut new_excerpts = SumTree::default(); 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); let buffer_id = buffer.remote_id(); @@ -2642,42 +2626,44 @@ impl MultiBuffer { }, ); - let new_excerpt = if let Some(prev_version) = prev_version { - edits.extend( - buffer - .edits_since_in_range::( - &prev_version, - 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); - let old_start = excerpt_old_start + edit.old.start; - let old_end = excerpt_old_start + edit.old.end; - let new_start = excerpt_new_start + edit.new.start; - let new_end = excerpt_new_start + edit.new.end; - Edit { - old: old_start..old_end, - new: new_start..new_end, - } - }), - ); + if let Some(prev_version) = &prev_version { + while let Some(old_excerpt) = cursor.item() + && &old_excerpt.path_key == &path + { + edits.extend( + buffer + .edits_since_in_range::( + prev_version, + old_excerpt.range.context.clone(), + ) + .map(|edit| { + let excerpt_old_start = cursor.start().len(); + let excerpt_new_start = + ExcerptDimension(new_excerpts.summary().text.len); + let old_start = excerpt_old_start + edit.old.start; + let old_end = excerpt_old_start + edit.old.end; + let new_start = excerpt_new_start + edit.new.start; + let new_end = excerpt_new_start + edit.new.end; + Edit { + old: old_start..old_end, + new: new_start..new_end, + } + }), + ); - Excerpt::new( - path, - old_excerpt.path_key_index, - &buffer.snapshot(), - old_excerpt.range.clone(), - old_excerpt.has_trailing_newline, - ) + let excerpt = Excerpt::new( + old_excerpt.path_key.clone(), + old_excerpt.path_key_index, + &buffer.snapshot(), + old_excerpt.range.clone(), + old_excerpt.has_trailing_newline, + ); + new_excerpts.push(excerpt, ()); + cursor.next(); + } } else { - old_excerpt.clone() + new_excerpts.append(cursor.slice(&path, Bias::Right), ()); }; - - new_excerpts.push(new_excerpt, ()); - cursor.next(); } new_excerpts.append(cursor.suffix(), ()); @@ -2692,7 +2678,6 @@ impl MultiBuffer { excerpt_edits: Vec>, change_kind: DiffChangeKind, ) -> Vec> { - dbg!(&excerpt_edits); if excerpt_edits.is_empty() { return vec![]; } @@ -3336,7 +3321,7 @@ impl MultiBuffer { .path_key .clone(); log::info!("Removing excerpts {:?}", path_key); - self.remove_excerpts_for_path(path_key, cx); + self.remove_excerpts(path_key, cx); } } } @@ -3665,72 +3650,35 @@ impl MultiBufferSnapshot { result } - pub fn ranges_to_buffer_ranges( - &self, - ranges: impl Iterator>, - ) -> impl Iterator)> { - ranges.flat_map(|range| { - self.range_to_buffer_ranges((Bound::Included(range.start), Bound::Included(range.end))) - .into_iter() - }) - } - // todo!() can we make this not RangeBounds - pub fn range_to_buffer_ranges( + pub fn range_to_buffer_ranges( &self, - range: R, - ) -> Vec<(&BufferSnapshot, Range)> - where - R: RangeBounds, - T: ToOffset, - { + range: Range, + ) -> Vec<(&BufferSnapshot, Range)> { self.range_to_buffer_ranges_with_context(range) .into_iter() .map(|(buffer, range, _context)| (buffer, range)) .collect() } - pub fn range_to_buffer_ranges_with_context( + pub fn range_to_buffer_ranges_with_context( &self, - range: R, - ) -> Vec<(&BufferSnapshot, Range, Range)> - where - R: RangeBounds, - T: ToOffset, - { - let start = match range.start_bound() { - Bound::Included(start) => start.to_offset(self), - Bound::Excluded(_) => panic!("excluded start bound not supported"), - Bound::Unbounded => MultiBufferOffset::ZERO, - }; - let end_bound = match range.end_bound() { - Bound::Included(end) => Bound::Included(end.to_offset(self)), - Bound::Excluded(end) => Bound::Excluded(end.to_offset(self)), - Bound::Unbounded => Bound::Unbounded, - }; - let bounds = (Bound::Included(start), end_bound); - + range: Range, + ) -> Vec<(&BufferSnapshot, Range, Range)> { let mut cursor = self.cursor::(); + let start = range.start.to_offset(self); + let end = range.end.to_offset(self); cursor.seek(&start); let mut result: Vec<(&BufferSnapshot, Range, Range)> = Vec::new(); - let mut last_excerpt = None; while let Some(region) = cursor.region() { - let dominated_by_end_bound = match end_bound { - Bound::Included(end) => region.range.start > end, - Bound::Excluded(end) => region.range.start >= end, - Bound::Unbounded => false, - }; - if dominated_by_end_bound { + if region.range.start >= end { break; } if region.is_main_buffer { let start_overshoot = start.saturating_sub(region.range.start); - let end_offset = match end_bound { - Bound::Included(end) | Bound::Excluded(end) => end, - Bound::Unbounded => region.range.end, - }; + let end_offset = end; let end_overshoot = end_offset.saturating_sub(region.range.start); let start = region .buffer_range @@ -3741,35 +3689,34 @@ impl MultiBufferSnapshot { .end .min(region.buffer_range.start + end_overshoot); let context = region.excerpt.range.context.clone(); - if last_excerpt - .as_ref() - .is_some_and(|prev_excerpt| prev_excerpt == ®ion.excerpt.info()) - && let Some(prev) = result - .last_mut() - .filter(|(_, prev_range, _)| prev_range.end == start) - { + if let Some(prev) = result.last_mut().filter(|(prev_buffer, prev_range, _)| { + prev_buffer.remote_id() == region.buffer.remote_id() && prev_range.end == start + }) { prev.1.end = end; } else { result.push((region.buffer, start..end, context)); - // todo!() is there a better way to do this? - last_excerpt = Some(region.excerpt.info()) } } cursor.next(); } - if let Some(excerpt) = cursor.excerpt() { - let dominated_by_prev_excerpt = - last_excerpt.is_some_and(|last_excerpt| last_excerpt == excerpt.info()); - if !dominated_by_prev_excerpt && excerpt.text_summary.len == 0 { - let excerpt_position = self.len(); - let buffer_snapshot = excerpt.buffer_snapshot(self); - if bounds.contains(&excerpt_position) { - let buffer_offset = - BufferOffset(excerpt.range.context.start.to_offset(buffer_snapshot)); - let context = excerpt.range.context.clone(); - result.push((buffer_snapshot, buffer_offset..buffer_offset, context)); - } + // Handle empty trailing excerpt, which doesn't have a region + if let Some(excerpt) = cursor.excerpt() + && excerpt.text_summary.len == 0 + && end == self.len() + { + let buffer_snapshot = excerpt.buffer_snapshot(self); + + let buffer_offset = + BufferOffset(excerpt.range.context.start.to_offset(buffer_snapshot)); + let context = excerpt.range.context.clone(); + if result + .last_mut() + .is_none_or(|(prev_buffer, prev_range, _)| { + prev_buffer.remote_id() != excerpt.buffer_id || prev_range.end != buffer_offset + }) + { + result.push((buffer_snapshot, buffer_offset..buffer_offset, context)); } } @@ -4231,7 +4178,6 @@ 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 { @@ -5073,7 +5019,6 @@ impl MultiBufferSnapshot { let mut summaries = Vec::new(); while let Some(anchor) = anchors.peek() { - dbg!(&anchor); let target = anchor.seek_target(self); let excerpt_anchor = match anchor { Anchor::Min => { @@ -6476,7 +6421,7 @@ impl MultiBufferSnapshot { .to_multi_buffer_debug_ranges(self) .into_iter() .flat_map(|range| { - self.range_to_buffer_ranges(range.start..=range.end) + self.range_to_buffer_ranges(range) .into_iter() .map(|(buffer, range)| { buffer.anchor_after(range.start)..buffer.anchor_before(range.end) @@ -6653,8 +6598,7 @@ where } self.excerpts.seek(&excerpt_position, Bias::Right); - if self.excerpts.item().is_none() && dbg!(excerpt_position) == *self.excerpts.start() { - dbg!("NONE"); + if self.excerpts.item().is_none() && excerpt_position == *self.excerpts.start() { self.excerpts.prev(); } } @@ -6823,7 +6767,6 @@ where fn build_region(&self) -> Option> { let excerpt = self.excerpts.item()?; - dbg!("GOT EXCERPT"); match self.diff_transforms.item()? { DiffTransform::DeletedHunk { buffer_id, @@ -6956,12 +6899,11 @@ impl Excerpt { range: ExcerptRange, has_trailing_newline: bool, ) -> Self { - dbg!(&path_key, &range, has_trailing_newline); Excerpt { path_key, path_key_index, buffer_id: buffer_snapshot.remote_id(), - max_buffer_row: range.context.end.to_point(&buffer_snapshot).row, + max_buffer_row: dbg!(range.context.end.to_point(&buffer_snapshot).row), text_summary: buffer_snapshot.text_summary_for_range::( range.context.to_offset(&buffer_snapshot), ), @@ -6988,7 +6930,7 @@ impl Excerpt { ExcerptInfo { path_key_index: self.path_key_index, buffer_id: self.buffer_id, - range: self.range.clone(), + range: self.range.context.clone(), } } @@ -7615,13 +7557,11 @@ 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 dbg!(self.point) == dbg!(self.cursor.diff_transforms.end().output_dimension.0) - { + } else if self.point == 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 f28ae0101dab783cf97c50233d64a9d325b6d278..2846c5ea0c4fbe1536a9afbee8395fd1194b37a2 100644 --- a/crates/multi_buffer/src/multi_buffer_tests.rs +++ b/crates/multi_buffer/src/multi_buffer_tests.rs @@ -319,7 +319,7 @@ fn test_excerpt_boundaries_and_clipping(cx: &mut App) { ); let snapshot = multibuffer.update(cx, |multibuffer, cx| { - multibuffer.remove_excerpts_for_path(PathKey::sorted(1), cx); + multibuffer.remove_excerpts(PathKey::sorted(1), cx); multibuffer.snapshot(cx) }); @@ -875,11 +875,11 @@ fn test_expand_excerpts(cx: &mut App) { multibuffer.update(cx, |multibuffer, cx| { let line_zero = multibuffer.snapshot(cx).anchor_before(Point::new(0, 0)); - multibuffer.expand_excerpts_with_paths( + multibuffer.expand_excerpts( multibuffer .snapshot(cx) .excerpts() - .map(|(_, info)| Anchor::in_buffer(info.path_key_index, info.range.context.end)), + .map(|(_, info)| Anchor::in_buffer(info.path_key_index, info.range.end)), 1, ExpandExcerptDirection::UpAndDown, cx, @@ -1109,7 +1109,6 @@ 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"); @@ -2294,7 +2293,7 @@ impl ReferenceExcerpt { ExcerptInfo { path_key_index: self.path_key_index, buffer_id: self.buffer.read(cx).remote_id(), - range: ExcerptRange::new(self.range.clone()), + range: self.range.clone(), } } } @@ -2345,7 +2344,8 @@ impl ReferenceMultibuffer { path_key: path_key.clone(), path_key_index, buffer: buffer.clone(), - range: buffer_snapshot.anchor_range_around(range.context), + range: buffer_snapshot.anchor_before(range.context.start) + ..buffer_snapshot.anchor_after(range.context.end), }), ); } @@ -2669,7 +2669,7 @@ impl ReferenceMultibuffer { expand_info: expand_direction.zip(region.excerpt_info.clone()).map( |(direction, excerpt_info)| ExpandInfo { direction, - excerpt_range: excerpt_info.range.context, + excerpt_range: excerpt_info.range, }, ), } @@ -2787,8 +2787,8 @@ async fn test_random_set_ranges(cx: &mut TestAppContext, mut rng: StdRng) { let mut seen_ranges = Vec::default(); for (buf, info) in snapshot.excerpts() { - let start = info.range.context.start.to_point(buf); - let end = info.range.context.end.to_point(buf); + let start = info.range.start.to_point(buf); + let end = info.range.end.to_point(buf); seen_ranges.push(start..end); if let Some(last_end) = last_end.take() { @@ -2865,10 +2865,10 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) { }) .collect::>(); log::info!("Expanding excerpts {excerpt_ixs:?} by {line_count} lines"); - multibuffer.expand_excerpts_with_paths( - excerpts.iter().map(|info| { - Anchor::in_buffer(info.path_key_index, info.range.context.end) - }), + multibuffer.expand_excerpts( + excerpts + .iter() + .map(|info| Anchor::in_buffer(info.path_key_index, info.range.end)), line_count, ExpandExcerptDirection::UpAndDown, cx, @@ -3160,6 +3160,17 @@ fn check_multibuffer( ); log::info!("Multibuffer content:\n{}", actual_diff); + dbg!( + snapshot + .excerpts() + .map(|(buffer_snapshot, excerpt)| { + ( + buffer_snapshot.remote_id(), + excerpt.range.to_point(&buffer_snapshot), + ) + }) + .collect::>() + ); assert_eq!( actual_row_infos.len(), @@ -3182,6 +3193,27 @@ fn check_multibuffer( start_row ); } + // dbg!(&expected_row_infos); + + // dbg!( + // snapshot + // .excerpts() + // .map(|(buffer_snapshot, excerpt)| { + // ( + // buffer_snapshot.remote_id(), + // excerpt.range.to_point(&buffer_snapshot), + // ) + // }) + // .collect::>() + // ); + // dbg!(&expected_row_infos); + // dbg!( + // reference + // .excerpts + // .iter() + // .map(|excerpt| { excerpt.range.to_point(&excerpt.buffer.read(cx).snapshot()) }) + // .collect::>() + // ); assert_eq!( snapshot.widest_line_number(), @@ -3623,7 +3655,6 @@ async fn test_summaries_for_anchors(cx: &mut TestAppContext) { }); cx.run_until_parked(); - dbg!("BEFORE"); let multibuffer = cx.new(|cx| { let mut multibuffer = MultiBuffer::new(Capability::ReadWrite); multibuffer.set_all_diff_hunks_expanded(cx); @@ -3646,12 +3677,10 @@ async fn test_summaries_for_anchors(cx: &mut TestAppContext) { multibuffer }); - dbg!("BEFORE"); let (mut snapshot, mut subscription) = multibuffer.update(cx, |multibuffer, cx| { (multibuffer.snapshot(cx), multibuffer.subscribe()) }); - dbg!("BEFORE"); assert_new_snapshot( &multibuffer, &mut snapshot, @@ -3668,7 +3697,6 @@ async fn test_summaries_for_anchors(cx: &mut TestAppContext) { ), ); - dbg!("BEFORE"); let anchor_1 = multibuffer.read_with(cx, |multibuffer, cx| { multibuffer .buffer_anchor_to_anchor( @@ -3678,11 +3706,9 @@ async fn test_summaries_for_anchors(cx: &mut TestAppContext) { ) .unwrap() }); - dbg!("BEFORE"); let point_1 = snapshot.summaries_for_anchors::([&anchor_1])[0]; assert_eq!(point_1, Point::new(0, 0)); - dbg!("BEFORE"); let anchor_2 = multibuffer.read_with(cx, |multibuffer, cx| { multibuffer .buffer_anchor_to_anchor( @@ -3692,9 +3718,7 @@ async fn test_summaries_for_anchors(cx: &mut TestAppContext) { ) .unwrap() }); - dbg!("BEFORE"); let point_2 = snapshot.summaries_for_anchors::([&anchor_2])[0]; - dbg!("AFTER"); assert_eq!(point_2, Point::new(3, 0)); } @@ -4181,7 +4205,7 @@ fn assert_excerpts_match( multibuffer.read_with(cx, |multibuffer, cx| { for (buffer, info) in multibuffer.snapshot(cx).excerpts() { output.push_str("-----\n"); - output.extend(buffer.text_for_range(info.range.context)); + output.extend(buffer.text_for_range(info.range)); if !output.ends_with('\n') { output.push('\n'); } @@ -4974,9 +4998,7 @@ fn test_excerpts_containment_functions(cx: &mut App) { } #[gpui::test] -fn test_range_to_buffer_ranges_with_range_bounds(cx: &mut App) { - use std::ops::Bound; - +fn test_range_to_buffer_ranges(cx: &mut App) { let buffer_1 = cx.new(|cx| Buffer::local("aaa\nbbb", cx)); let buffer_2 = cx.new(|cx| Buffer::local("ccc", cx)); @@ -5011,52 +5033,6 @@ fn test_range_to_buffer_ranges_with_range_bounds(cx: &mut App) { "Half-open range ending at excerpt start should EXCLUDE that excerpt" ); 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!( - ranges_inclusive.len(), - 2, - "Inclusive range ending at excerpt start should INCLUDE that excerpt" - ); - 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)); - assert_eq!( - ranges_unbounded.len(), - 2, - "Unbounded end should include all excerpts" - ); - 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()), - Bound::Excluded(excerpt_2_start), - )); - assert_eq!( - ranges_excluded_end.len(), - 1, - "Excluded end bound should exclude excerpt starting at that point" - ); - 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() @@ -5096,36 +5072,10 @@ fn test_range_to_buffer_ranges_with_range_bounds(cx: &mut App) { snapshot_trailing.range_to_buffer_ranges_with_context(Point::zero()..max_point); assert_eq!( ranges_half_open_max.len(), - 1, - "Half-open range to max_point should EXCLUDE trailing empty excerpt at max_point" - ); - assert_eq!(ranges_half_open_max[0].2, te_excerpt_1_info.range.context); - - let ranges_inclusive_max = - snapshot_trailing.range_to_buffer_ranges_with_context(Point::zero()..=max_point); - assert_eq!( - ranges_inclusive_max.len(), 2, - "Inclusive range to max_point should INCLUDE trailing empty excerpt" - ); - assert_eq!(ranges_inclusive_max[0].2, te_excerpt_1_info.range.context); - assert_eq!(ranges_inclusive_max[1].2, te_excerpt_2_info.range.context); - - let ranges_unbounded_trailing = snapshot_trailing - .range_to_buffer_ranges_with_context((Bound::Included(Point::zero()), Bound::Unbounded)); - assert_eq!( - ranges_unbounded_trailing.len(), - 2, - "Unbounded end should include trailing empty excerpt" - ); - assert_eq!( - ranges_unbounded_trailing[0].2, - te_excerpt_1_info.range.context - ); - assert_eq!( - ranges_unbounded_trailing[1].2, - te_excerpt_2_info.range.context + "Should include trailing empty excerpts" ); + assert_eq!(ranges_half_open_max[0].2, te_excerpt_1_info.range); } #[gpui::test] @@ -5177,8 +5127,8 @@ fn test_cannot_seek_backward_after_excerpt_replacement(cx: &mut TestAppContext) let e_b2_info = excerpt_infos[1].clone(); let e_b3_info = excerpt_infos[2].clone(); - let anchor_b2 = Anchor::in_buffer(e_b2_info.path_key_index, e_b2_info.range.context.start); - let anchor_b3 = Anchor::in_buffer(e_b3_info.path_key_index, e_b3_info.range.context.start); + let anchor_b2 = Anchor::in_buffer(e_b2_info.path_key_index, e_b2_info.range.start); + let anchor_b3 = Anchor::in_buffer(e_b3_info.path_key_index, e_b3_info.range.start); (anchor_b2, anchor_b3) }); diff --git a/crates/multi_buffer/src/path_key.rs b/crates/multi_buffer/src/path_key.rs index fa16b79fc03a9eac0563c9440aad873742a3dc5d..e81af7ef0fb42754815c0b9e29a66b2456af4494 100644 --- a/crates/multi_buffer/src/path_key.rs +++ b/crates/multi_buffer/src/path_key.rs @@ -69,7 +69,7 @@ impl MultiBuffer { let excerpt = snapshot.excerpts_for_path(path).next()?; Some(Anchor::in_buffer( excerpt.path_key_index, - excerpt.range.context.start, + excerpt.range.start, )) } @@ -178,7 +178,7 @@ impl MultiBuffer { } } - pub(super) fn expand_excerpts_with_paths( + pub fn expand_excerpts( &mut self, anchors: impl IntoIterator, line_count: u32, @@ -199,6 +199,11 @@ impl MultiBuffer { .path_keys_by_index .get(&path_index) .expect("anchor from wrong multibuffer"); + + let mut excerpt_anchors = excerpt_anchors.peekable(); + let mut ranges = Vec::new(); + + cursor.seek_forward(path, Bias::Left); let Some((buffer, buffer_snapshot)) = cursor .item() .map(|excerpt| (excerpt.buffer(&self), excerpt.buffer_snapshot(&snapshot))) @@ -206,10 +211,6 @@ impl MultiBuffer { continue; }; - let mut excerpt_anchors = excerpt_anchors.peekable(); - let mut ranges = Vec::new(); - - cursor.seek_forward(path, Bias::Left); while let Some(excerpt) = cursor.item() && &excerpt.path_key == path { @@ -324,11 +325,11 @@ impl MultiBuffer { .path_for_buffer(buffer_snapshot.remote_id()) && old_path_key != &path_key { - self.remove_excerpts_for_path(old_path_key.clone(), cx); + self.remove_excerpts(old_path_key.clone(), cx); } if to_insert.len() == 0 { - self.remove_excerpts_for_path(path_key.clone(), cx); + self.remove_excerpts(path_key.clone(), cx); return (false, path_key_index); } @@ -406,7 +407,10 @@ impl MultiBuffer { path_key_index, &buffer_snapshot, next_excerpt.clone(), - to_insert.peek().is_some() || cursor.item().is_some(), + to_insert.peek().is_some() + || cursor + .item() + .is_some_and(|item| item.path_key_index != path_key_index), ), (), ); @@ -437,7 +441,10 @@ impl MultiBuffer { path_key_index, &buffer_snapshot, next_excerpt.clone(), - to_insert.peek().is_some() || cursor.item().is_some(), + to_insert.peek().is_some() + || cursor + .item() + .is_some_and(|item| item.path_key_index != path_key_index), ), (), ); @@ -481,7 +488,7 @@ impl MultiBuffer { let edits = Self::sync_diff_transforms( &mut snapshot, - dbg!(patch.into_inner()), + patch.into_inner(), DiffChangeKind::BufferEdited, ); if !edits.is_empty() { @@ -506,10 +513,10 @@ impl MultiBuffer { let Some(path) = snapshot.path_for_buffer(buffer).cloned() else { return; }; - self.remove_excerpts_for_path(path, cx); + self.remove_excerpts(path, cx); } - pub fn remove_excerpts_for_path(&mut self, path: PathKey, cx: &mut Context) { + pub fn remove_excerpts(&mut self, path: PathKey, cx: &mut Context) { assert_eq!(self.history.transaction_depth(), 0); self.sync_mut(cx); diff --git a/crates/text/src/anchor.rs b/crates/text/src/anchor.rs index 896fea24c9d9eac4f5623754e3167b9ab1b52d57..427ede3845a0b7e39b4519e5607af435f2eac924 100644 --- a/crates/text/src/anchor.rs +++ b/crates/text/src/anchor.rs @@ -216,6 +216,7 @@ where pub trait AnchorRangeExt { fn cmp(&self, b: &Range, buffer: &BufferSnapshot) -> Ordering; fn overlaps(&self, b: &Range, buffer: &BufferSnapshot) -> bool; + fn contains_anchor(&self, b: Anchor, buffer: &BufferSnapshot) -> bool; } impl AnchorRangeExt for Range { @@ -229,4 +230,8 @@ impl AnchorRangeExt for Range { fn overlaps(&self, other: &Range, buffer: &BufferSnapshot) -> bool { self.start.cmp(&other.end, buffer).is_lt() && other.start.cmp(&self.end, buffer).is_lt() } + + fn contains_anchor(&self, other: Anchor, buffer: &BufferSnapshot) -> bool { + self.start.cmp(&other, buffer).is_le() && self.end.cmp(&other, buffer).is_ge() + } }