Support relative line number on wrapped lines (rework) (#41805)

## Add relative line numbers on wrapped lines, take 2

This is a rework of https://github.com/zed-industries/zed/pull/39268
that excludes
https://github.com/zed-industries/zed/pull/39268/commits/e7096d27a6463f6eb7c2a821637c5773b2460c10.
This commit introduced some line number rendering issues as described in
https://github.com/zed-industries/zed/issues/41422.

While @ConradIrwin suggested we try to pass in the buffer rows from the
calling method instead of the snapshot, that
appears to have had unintended consequences and I don't think the two
calculations were intended to do the same thing. Hence, this PR has
removed those changes.

This PR also includes the migration fix originally done by @MrSubidubi
in https://github.com/zed-industries/zed/pull/41351.

## Original PR description and release notes.

**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 and discussed in
https://github.com/zed-industries/zed/discussions/25733.

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`.
This commit is contained in:
Thomas Heartman
2025-11-04 08:24:17 -07:00
committed by GitHub
parent 1b93242351
commit fb410ab3ae
17 changed files with 335 additions and 114 deletions
+16 -6
View File
@@ -163,7 +163,10 @@ use rpc::{ErrorCode, ErrorExt, proto::PeerId};
use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager};
use selections_collection::{MutableSelectionsCollection, SelectionsCollection};
use serde::{Deserialize, Serialize};
use settings::{GitGutterSetting, Settings, SettingsLocation, SettingsStore, update_settings_file};
use settings::{
GitGutterSetting, RelativeLineNumbers, Settings, SettingsLocation, SettingsStore,
update_settings_file,
};
use smallvec::{SmallVec, smallvec};
use snippet::Snippet;
use std::{
@@ -19542,9 +19545,16 @@ impl Editor {
EditorSettings::get_global(cx).gutter.line_numbers
}
pub fn should_use_relative_line_numbers(&self, cx: &mut App) -> bool {
self.use_relative_line_numbers
.unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
pub fn relative_line_numbers(&self, cx: &mut App) -> RelativeLineNumbers {
match (
self.use_relative_line_numbers,
EditorSettings::get_global(cx).relative_line_numbers,
) {
(None, setting) => setting,
(Some(false), _) => RelativeLineNumbers::Disabled,
(Some(true), RelativeLineNumbers::Wrapped) => RelativeLineNumbers::Wrapped,
(Some(true), _) => RelativeLineNumbers::Enabled,
}
}
pub fn toggle_relative_line_numbers(
@@ -19553,8 +19563,8 @@ impl Editor {
_: &mut Window,
cx: &mut Context<Self>,
) {
let is_relative = self.should_use_relative_line_numbers(cx);
self.set_relative_line_number(Some(!is_relative), cx)
let is_relative = self.relative_line_numbers(cx);
self.set_relative_line_number(Some(!is_relative.enabled()), cx)
}
pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {