Skip to content

Keep gutters aligned with wrapped lines in diff and merge panes - #27

Merged
CoreyKaylor merged 1 commit into
mainfrom
fix/wrap-line-numbers
Nov 5, 2026
Merged

CoreyKaylor merged 1 commit into
mainfrom
fix/wrap-line-numbers

Conversation

@CoreyKaylor

Copy link
Copy Markdown
Owner

Fixes #26.

Problem

With :set wrap in a diff pane, a line longer than the window takes several screen rows, but the line-number panes and the connector still moved one row per buffer line. Numbers, change bands and routes fell further out of step with each wrapped line (cursor on 376, gutter showing 378).

Approach

Gutters now work in screen-row space instead of buffer-line space.

  • util/rowmap (new): per-pane map from buffer line to screen row. It only stores lines taller than one row, so it stays small, and it is the identity when nothing wraps, which leaves unwrapped rendering exactly as before. Maps are cached per window and rebuilt when the buffer text, the text-area width, or a wrap-related option changes. Each paint re-checks the visible lines so inlay hints and virtual lines are picked up. Virtual lines (e.g. diagnostic virtual_lines) count as rows even with wrap off.
  • Number panes put a line's number on its first screen row and carry the band through its continuation rows.
  • Route geometry: base paths come from the existing compute_paths, run over a row-space expansion of the aligned view. The connector engine is unchanged. An add/delete origin on a wrapped line attaches to its bottom row.
  • Scrolling: gutters scroll to the pane's screen-row topline, including a 'smoothscroll' skipcol. Viewport repaints now restore full window views (previously topline + cursor only, which dropped the skipcol).
  • WinResized/VimResized and OptionSet for wrap-related options rebuild the gutters. Merge also re-renders on resize now.
  • The 3-way merge view uses the same maps for local, result and remote.
  • Sync on cursor/scroll reuses the last map (stale_ok), so typing in a large wrapped file doesn't re-measure on every keystroke. Paints re-measure.

Cost

  • Wrap off: a few µs per scroll (cached map, cheap key).
  • Wrap on, 200k-line file: one re-measure costs about 30–170 ms, depending on how many lines wrap. It runs only on paint after a text/width/option change.

Known limits

  • Folds are still forced off in diff panes, as before.
  • On wrapped continuation rows, the right pane's sign-column band fill and the origin-line underline padding only cover the first row.
  • goto_document_edge and snap-to-cursor still compute offsets in lines rather than rows. With wrap on they work, but may land a few rows off.

Tests

  • tests/integration/test_wrap.lua: on every visible row, each tagged fixture line's number must sit on the row where the line starts, and continuation rows must be blank. Covered through scrolling either pane, smoothscroll partway into a wrapped line, toggling wrap off, and resizing. All four cases fail without the fix.
  • tests/specs/33_rowmap.lua: map maths, measuring a real window (long lines, tabs, linebreak rebuild, virtual lines), and row-space path expansion.
  • Full suite: nvim -l tests/run_all.lua → 19 jobs, 0 failed.

With 'wrap' on, a long line spans several screen rows but the number
panes and connector still advanced one row per buffer line, so numbers,
bands and routes drifted out of step with the text (#26).

Gutters now work in screen-row space. util/rowmap maps each source
pane's buffer lines to screen rows (sparse: only lines taller than one
row; the identity without wrap, so unwrapped rendering is unchanged).
Number panes put a line's number on its first row and carry its band
through continuation rows; base paths are built by running the existing
compute_paths over a row-space expansion of the aligned view, so the
connector engine is untouched. Gutters scroll to the pane's screen-row
topline, including a 'smoothscroll' skipcol, and viewport repaints now
restore full window views so that skipcol survives. Resizes and
wrap-related option changes rebuild the maps; virtual lines count as
rows too. The merge view uses the same maps for its three panes.

Fixes #26
@CoreyKaylor
CoreyKaylor merged commit fc4eafc into main Nov 5, 2026
1 check passed
@CoreyKaylor
CoreyKaylor deleted the fix/wrap-line-numbers branch November 5, 2026 22:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Setting Wrap to on breaks line number shown in diff view

1 participant