Commit Graph
4 Commits
Author SHA1 Message Date
Thomas Heartman 0627a527bd 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`.
2025-11-04 08:24:17 -07:00
Thomas HeartmanandConrad Irwin e2882a7dc1 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>
2025-10-27 20:20:45 -06:00
Thomas Heartman 635fccbc3e vim: Fix clear exchange not working (#25804)
Fixes two issues with the Vim exchange implementation:

1. The clear exchange implementation **didn't** clear the exchange. This
was due to us asking the editor to clear normal highlights instead of
background highlights.
2. Calling clear exchange also wouldn't cause the operator to be
cleared, so you would be left in operator = "cx".

I've added tests for both of these cases.

Partially closes #25750. It doesn't address the problem with dot repeat
not working for my custom bindings, but I don't know what would cause
that. I'd love to hear some thoughts on why that is. That might be a
problem on my part or it might be something with the code. Input would
be appreciated.

Release Notes:

- Fixed: Vim exchange's "clear exchange" function didn't clear the
exchange and kept you in operator pending mode.
2025-03-04 19:34:52 -07:00
Thomas HeartmanandConrad Irwin 2f6424b0be vim: Add exchange (#24678)
Implements [vim-exchange](https://github.com/tommcdo/vim-exchange)
functionality.

Lets you swap the content of one selection/object/motion with another.

The default key bindings are the same as in exchange:
- `cx` to begin the exchange in normal mode. Visual mode does not have a
default binding due to conflicts.
- `cxx` selects the current line
- `cxc` clears the selection
- If the previous operation was an exchange, `.` will repeat that
operation.

Closes #22759

## Overlapping regions

According to the vim exchange readme:

> If one region is fully contained within the other, it will replace the
containing region.

Zed does the following:
- If one range is completely contained within another: the smaller
region replaces the larger region (as in exchange.vim)
- If the ranges only partially overlap, then we abort and cancel the
exchange. I don't think we can do anything sensible with that. Not sure
what the original does, evil-exchange aborts.

## Not implemented: cross-window exchange

Emacs's evil-exchange allows you to exchange across buffers. There is no
code to accommodate that in this PR. Personally, it'd never occurred to
me before working on this and I've never needed it. As such, I'll leave
that implementation for whomever needs it.

As an upside; this allows you to have concurrent exchange states per
buffer, which may come in handy.

## Bonus

Also adds "replace with register" for the full line with `grr` 🐕 This
was an oversight from a previous PR.

Release notes:

- Added an implementation of `vim-exchange`
- Fixed: Added missing default key binding for `Vim::CurrentLine` for
replace with register mode (`grr`)

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2025-02-22 20:36:21 +00:00