From d73ef2451816908fba17603f64fbb7e8c4bc7717 Mon Sep 17 00:00:00 2001 From: "zed-zippy[bot]" <234243425+zed-zippy[bot]@users.noreply.github.com> Date: Fri, 13 Feb 2026 19:41:58 +0000 Subject: [PATCH] git: Fix misalignment in the split diff when inlays fall at the end of an excerpt (#49078) (cherry-pick to preview) (#49124) Cherry-pick of #49078 to preview ---- - [x] Tests or screenshots needed? - [ ] Code Reviewed - [x] Manual QA Release Notes: - Fixed misalignment of lines in the split diff when using inlay hints. Co-authored-by: Cole Miller --- crates/editor/src/display_map/block_map.rs | 39 +++++---- crates/editor/src/display_map/inlay_map.rs | 8 +- crates/editor/src/split.rs | 93 ++++++++++++++++++++++ 3 files changed, 121 insertions(+), 19 deletions(-) diff --git a/crates/editor/src/display_map/block_map.rs b/crates/editor/src/display_map/block_map.rs index a43c61c8617f34a8d396503e7392a306edf27929..b1abe6b39b2a4701bdbd99d51ad208b184b57ca3 100644 --- a/crates/editor/src/display_map/block_map.rs +++ b/crates/editor/src/display_map/block_map.rs @@ -1017,8 +1017,9 @@ impl BlockMap { |point, bias| { wrap_point_cursor .map( - tab_point_cursor - .map(fold_point_cursor.map(inlay_point_cursor.map(point), bias)), + tab_point_cursor.map( + fold_point_cursor.map(inlay_point_cursor.map(point, bias), bias), + ), ) .row() }, @@ -1232,32 +1233,33 @@ impl BlockMap { let mut companion_tab_point_cursor = companion_snapshot.tab_point_cursor(); let mut companion_wrap_point_cursor = companion_snapshot.wrap_point_cursor(); - let mut our_wrapper = |our_point: Point| { + let mut our_wrapper = |our_point: Point, bias: Bias| { our_wrap_point_cursor .map(our_tab_point_cursor.map( - our_fold_point_cursor.map(our_inlay_point_cursor.map(our_point), Bias::Left), + our_fold_point_cursor.map(our_inlay_point_cursor.map(our_point, bias), bias), )) .row() }; - let mut companion_wrapper = |their_point: Point| { + let mut companion_wrapper = |their_point: Point, bias: Bias| { companion_wrap_point_cursor .map( companion_tab_point_cursor.map( companion_fold_point_cursor - .map(companion_inlay_point_cursor.map(their_point), Bias::Left), + .map(companion_inlay_point_cursor.map(their_point, bias), bias), ), ) .row() }; fn determine_spacer( - our_wrapper: &mut impl FnMut(Point) -> WrapRow, - companion_wrapper: &mut impl FnMut(Point) -> WrapRow, + our_wrapper: &mut impl FnMut(Point, Bias) -> WrapRow, + companion_wrapper: &mut impl FnMut(Point, Bias) -> WrapRow, our_point: Point, their_point: Point, delta: i32, + bias: Bias, ) -> (i32, Option<(WrapRow, u32)>) { - let our_wrap = our_wrapper(our_point); - let companion_wrap = companion_wrapper(their_point); + let our_wrap = our_wrapper(our_point, bias); + let companion_wrap = companion_wrapper(their_point, bias); let new_delta = companion_wrap.0 as i32 - our_wrap.0 as i32; let spacer = if new_delta > delta { @@ -1313,9 +1315,11 @@ impl BlockMap { // Case 3: We are at the start of the excerpt--no previous row to use as the baseline. (first_point, edit_for_first_point.new.start) }; - let our_baseline = our_wrapper(our_baseline); - let their_baseline = - companion_wrapper(their_baseline.min(excerpt.target_excerpt_range.end)); + let our_baseline = our_wrapper(our_baseline, Bias::Left); + let their_baseline = companion_wrapper( + their_baseline.min(excerpt.target_excerpt_range.end), + Bias::Left, + ); let mut delta = their_baseline.0 as i32 - our_baseline.0 as i32; @@ -1338,6 +1342,7 @@ impl BlockMap { current_boundary, current_range.end.min(excerpt.target_excerpt_range.end), delta, + Bias::Left, ); delta = new_delta; @@ -1371,6 +1376,7 @@ impl BlockMap { current_boundary, current_range.start.min(excerpt.target_excerpt_range.end), delta, + Bias::Left, ); delta = delta_at_start; @@ -1411,6 +1417,7 @@ impl BlockMap { current_boundary, current_range.end.min(excerpt.target_excerpt_range.end), delta, + Bias::Left, ); delta = delta_at_end; spacer_at_end @@ -1449,6 +1456,7 @@ impl BlockMap { last_source_point, excerpt.target_excerpt_range.end, delta, + Bias::Right, ); if let Some((wrap_row, height)) = spacer { result.push(( @@ -4066,8 +4074,9 @@ mod tests { |point, bias| { wrap_point_cursor .map( - tab_point_cursor - .map(fold_point_cursor.map(inlay_point_cursor.map(point), bias)), + tab_point_cursor.map( + fold_point_cursor.map(inlay_point_cursor.map(point, bias), bias), + ), ) .row() }, diff --git a/crates/editor/src/display_map/inlay_map.rs b/crates/editor/src/display_map/inlay_map.rs index 731133d98e26632dc40c12de7e52469951f9a935..444c7bfb839505ac0d63291caaf3401d880d2827 100644 --- a/crates/editor/src/display_map/inlay_map.rs +++ b/crates/editor/src/display_map/inlay_map.rs @@ -935,7 +935,7 @@ impl InlaySnapshot { #[ztracing::instrument(skip_all)] pub fn to_inlay_point(&self, point: Point) -> InlayPoint { - self.inlay_point_cursor().map(point) + self.inlay_point_cursor().map(point, Bias::Left) } #[ztracing::instrument(skip_all)] @@ -1212,7 +1212,7 @@ pub struct InlayPointCursor<'transforms> { impl InlayPointCursor<'_> { #[ztracing::instrument(skip_all)] - pub fn map(&mut self, point: Point) -> InlayPoint { + pub fn map(&mut self, point: Point, bias: Bias) -> InlayPoint { let cursor = &mut self.cursor; if cursor.did_seek() { cursor.seek_forward(&point, Bias::Left); @@ -1224,7 +1224,7 @@ impl InlayPointCursor<'_> { Some(Transform::Isomorphic(_)) => { if point == cursor.end().0 { while let Some(Transform::Inlay(inlay)) = cursor.next_item() { - if inlay.position.bias() == Bias::Right { + if bias == Bias::Left && inlay.position.bias() == Bias::Right { break; } else { cursor.next(); @@ -1237,7 +1237,7 @@ impl InlayPointCursor<'_> { } } Some(Transform::Inlay(inlay)) => { - if inlay.position.bias() == Bias::Left { + if inlay.position.bias() == Bias::Left || bias == Bias::Right { cursor.next(); } else { return cursor.start().1; diff --git a/crates/editor/src/split.rs b/crates/editor/src/split.rs index 73a7f1dc4e5334cd3f681b84a3f90b1b02dca919..f44fb4ae9a31ebac882b6dc1d818a03a92f3d8b5 100644 --- a/crates/editor/src/split.rs +++ b/crates/editor/src/split.rs @@ -2091,6 +2091,7 @@ mod tests { use crate::SplittableEditor; use crate::display_map::{BlockPlacement, BlockProperties, BlockStyle}; + use crate::inlays::Inlay; use crate::test::{editor_content_with_blocks_and_width, set_block_content_for_tests}; async fn init_test( @@ -5450,4 +5451,96 @@ mod tests { Some(5) ); } + + #[gpui::test] + async fn test_multiline_inlays_create_spacers(cx: &mut gpui::TestAppContext) { + use rope::Point; + use unindent::Unindent as _; + + let (editor, mut cx) = init_test(cx, SoftWrap::None, DiffViewStyle::Split).await; + + let base_text = " + aaa + bbb + ccc + ddd + " + .unindent(); + let current_text = base_text.clone(); + + let (buffer, diff) = buffer_with_diff(&base_text, ¤t_text, &mut cx); + + editor.update(cx, |editor, cx| { + let path = PathKey::for_buffer(&buffer, cx); + editor.set_excerpts_for_path( + path, + buffer.clone(), + vec![Point::new(0, 0)..Point::new(3, 3)], + 0, + diff.clone(), + cx, + ); + }); + + cx.run_until_parked(); + + let rhs_editor = editor.read_with(cx, |e, _| e.rhs_editor.clone()); + rhs_editor.update(cx, |rhs_editor, cx| { + let snapshot = rhs_editor.buffer().read(cx).snapshot(cx); + rhs_editor.splice_inlays( + &[], + vec![ + Inlay::edit_prediction( + 0, + snapshot.anchor_after(Point::new(0, 3)), + "\nINLAY_WITHIN", + ), + Inlay::edit_prediction( + 1, + snapshot.anchor_after(Point::new(1, 3)), + "\nINLAY_MID_1\nINLAY_MID_2", + ), + Inlay::edit_prediction( + 2, + snapshot.anchor_after(Point::new(3, 3)), + "\nINLAY_END_1\nINLAY_END_2", + ), + ], + cx, + ); + }); + + cx.run_until_parked(); + + assert_split_content( + &editor, + " + § + § ----- + aaa + INLAY_WITHIN + bbb + INLAY_MID_1 + INLAY_MID_2 + ccc + ddd + INLAY_END_1 + INLAY_END_2" + .unindent(), + " + § + § ----- + aaa + § spacer + bbb + § spacer + § spacer + ccc + ddd + § spacer + § spacer" + .unindent(), + &mut cx, + ); + } }