From 87d0401e647e1cf9a93d57ae5a993d3b1accb5c6 Mon Sep 17 00:00:00 2001 From: Finn Evers Date: Mon, 10 Nov 2025 21:00:50 +0100 Subject: [PATCH] editor: Show relative line numbers for deleted rows (#42378) Closes #42191 This PR adds support for relative line numbers in deleted hunks. Note that this only applies in cases where there is a form of relative numbering. It also adds some tests for this functionality as well as missing tests for other cases in line layouting that was previously untested. Release Notes: - Line numbers will now be shown in deleted git hunks if relative line numbering is enabled --- crates/editor/src/element.rs | 119 +++++++++++++++++++++++++++++++---- 1 file changed, 107 insertions(+), 12 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 67f6350ce625e96fcbe8734bf690fb557b86046c..7442ccc7442a11ab2f845cc637e5ad416085af02 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -3255,11 +3255,9 @@ impl EditorElement { (newest_selection_head, relative) }); - let relative_to = if relative.enabled() { - Some(newest_selection_head.row()) - } else { - None - }; + let relative_line_numbers_enabled = relative.enabled(); + let relative_to = relative_line_numbers_enabled.then(|| newest_selection_head.row()); + let relative_rows = self.calculate_relative_line_numbers(snapshot, &rows, relative_to, relative.wrapped()); let mut line_number = String::new(); @@ -3271,17 +3269,18 @@ impl EditorElement { } else { row_info.buffer_row? + 1 }; - let number = relative_rows - .get(&display_row) - .unwrap_or(&non_relative_number); - write!(&mut line_number, "{number}").unwrap(); - if row_info - .diff_status - .is_some_and(|status| status.is_deleted()) + let relative_number = relative_rows.get(&display_row); + if !(relative_line_numbers_enabled && relative_number.is_some()) + && row_info + .diff_status + .is_some_and(|status| status.is_deleted()) { return None; } + let number = relative_number.unwrap_or(&non_relative_number); + write!(&mut line_number, "{number}").unwrap(); + let color = active_rows .get(&display_row) .map(|spec| { @@ -11455,6 +11454,46 @@ mod tests { assert_eq!(relative_rows[&DisplayRow(0)], 5); assert_eq!(relative_rows[&DisplayRow(1)], 4); assert_eq!(relative_rows[&DisplayRow(2)], 3); + + const DELETED_LINE: u32 = 3; + let layouts = cx + .update_window(*window, |_, window, cx| { + element.layout_line_numbers( + None, + GutterDimensions { + left_padding: Pixels::ZERO, + right_padding: Pixels::ZERO, + width: px(30.0), + margin: Pixels::ZERO, + git_blame_entries_width: None, + }, + line_height, + gpui::Point::default(), + DisplayRow(0)..DisplayRow(6), + &(0..6) + .map(|row| RowInfo { + buffer_row: Some(row), + diff_status: (row == DELETED_LINE).then(|| { + DiffHunkStatus::deleted( + buffer_diff::DiffHunkSecondaryStatus::NoSecondaryHunk, + ) + }), + ..Default::default() + }) + .collect::>(), + &BTreeMap::default(), + Some(DisplayPoint::new(DisplayRow(0), 0)), + &snapshot, + window, + cx, + ) + }) + .unwrap(); + assert_eq!(layouts.len(), 5,); + assert!( + layouts.get(&MultiBufferRow(DELETED_LINE)).is_none(), + "Deleted line should not have a line number" + ); } #[gpui::test] @@ -11530,6 +11569,62 @@ mod tests { // current line has no relative number assert_eq!(relative_rows[&DisplayRow(4)], 1); assert_eq!(relative_rows[&DisplayRow(5)], 2); + + let layouts = cx + .update_window(*window, |_, window, cx| { + element.layout_line_numbers( + None, + GutterDimensions { + left_padding: Pixels::ZERO, + right_padding: Pixels::ZERO, + width: px(30.0), + margin: Pixels::ZERO, + git_blame_entries_width: None, + }, + line_height, + gpui::Point::default(), + DisplayRow(0)..DisplayRow(6), + &(0..6) + .map(|row| RowInfo { + buffer_row: Some(row), + diff_status: Some(DiffHunkStatus::deleted( + buffer_diff::DiffHunkSecondaryStatus::NoSecondaryHunk, + )), + ..Default::default() + }) + .collect::>(), + &BTreeMap::from_iter([(DisplayRow(0), LineHighlightSpec::default())]), + Some(DisplayPoint::new(DisplayRow(0), 0)), + &snapshot, + window, + cx, + ) + }) + .unwrap(); + assert!( + layouts.is_empty(), + "Deleted lines should have no line number" + ); + + let relative_rows = window + .update(cx, |editor, window, cx| { + let snapshot = editor.snapshot(window, cx); + element.calculate_relative_line_numbers( + &snapshot, + &(DisplayRow(0)..DisplayRow(6)), + Some(DisplayRow(3)), + true, + ) + }) + .unwrap(); + + // Deleted lines should still have relative numbers + assert_eq!(relative_rows[&DisplayRow(0)], 3); + assert_eq!(relative_rows[&DisplayRow(1)], 2); + assert_eq!(relative_rows[&DisplayRow(2)], 1); + // current line, even if deleted, has no relative number + assert_eq!(relative_rows[&DisplayRow(4)], 1); + assert_eq!(relative_rows[&DisplayRow(5)], 2); } #[gpui::test]