Support relative line number on wrapped lines (#39268)

**Problem:** Current relative line numbering creates a mismatch with
vim-style navigation when soft wrap is enabled. Users must mentally
calculate whether target lines are wrapped segments or logical lines,
making `<n>j/k` navigation unreliable and cognitively demanding.

**How things work today:**
- Real line navigation (`j/k` moves by logical lines): Requires
determining if visible lines are wrapped segments before jumping. Can't
jump to wrapped lines directly.
- Display line navigation (`j/k` moves by display rows): Line numbers
don't correspond to actual row distances for multi-line jumps.

**Proposed solution:** Count and number each display line (including
wrapped segments) for relative numbering. This creates direct
visual-to-navigational correspondence where the relative number shown
always matches the `<n>j/k` distance needed.

**Benefits:**
- Eliminates mental overhead of distinguishing wrapped vs. logical lines
- Makes relative line numbers consistently actionable regardless of wrap
state
- Preserves intuitive "what you see is what you navigate" principle
- Maintains vim workflow efficiency in narrow window scenarios

Also explained an discussed in
https://github.com/zed-industries/zed/discussions/25733.

Release Notes:

Release Notes:

- Added support for counting wrapped lines as relative lines and for
displaying line numbers for wrapped segments. Changes
`relative_line_numbers` from a boolean to an enum: `enabled`,
`disabled`, or `wrapped`.

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
This commit is contained in:
Thomas Heartman
2025-10-27 20:20:45 -06:00
committed by GitHub
co-authored by Conrad Irwin
parent 1c4923e1c8
commit d5e297147f
16 changed files with 332 additions and 122 deletions
+4
View File
@@ -340,6 +340,7 @@ pub struct RowInfo {
pub multibuffer_row: Option<MultiBufferRow>,
pub diff_status: Option<buffer_diff::DiffHunkStatus>,
pub expand_info: Option<ExpandInfo>,
pub wrapped_buffer_row: Option<u32>,
}
/// A slice into a [`Buffer`] that is being edited in a [`MultiBuffer`].
@@ -6632,6 +6633,7 @@ impl Iterator for MultiBufferRows<'_> {
multibuffer_row: Some(MultiBufferRow(0)),
diff_status: None,
expand_info: None,
wrapped_buffer_row: None,
});
}
@@ -6689,6 +6691,7 @@ impl Iterator for MultiBufferRows<'_> {
buffer_row: Some(last_row),
multibuffer_row: Some(multibuffer_row),
diff_status: None,
wrapped_buffer_row: None,
expand_info,
});
} else {
@@ -6733,6 +6736,7 @@ impl Iterator for MultiBufferRows<'_> {
.diff_hunk_status
.filter(|_| self.point < region.range.end),
expand_info,
wrapped_buffer_row: None,
});
self.point += Point::new(1, 0);
result