Skip to content

vi mode: j/k onto a shorter line park the cursor one past the last char, so x deletes the newline and joins lines #2442

Closed

Description

@sinelaw

Found while continuing the vi "Vim compatibility motions" sweep on master v0.4.1 (built from origin/master @ 3b8c2eca1). Reference: Vim (cursor cannot rest past the last character in NORMAL mode; vertical motion clamps the cursor to the last char of a shorter line and remembers the desired column).

When you move down/up (j/k) from a long line onto a shorter line whose length is less than the current/desired column, Fresh's vi mode parks the cursor one column past the last character (the insert position), instead of clamping it to the last character. Because the cursor is then sitting on the line-ending boundary, pressing x deletes the newline and joins the following line — silent corruption from an ordinary navigate-then-delete.

Steps to reproduce

  1. Create a file whose first lines are a long line followed by a short line, e.g.:
    hello world foo bar baz
    short
    a longer line with many words here
    
    Open it: fresh --no-restore motions.txt.
  2. Enable vi mode: Ctrl+P → "Toggle Vi mode" → Enter.
  3. On line 1, press $ (cursor to the last char z, col 23 — correct).
  4. Press j to move down onto line 2 (short, 5 chars).
  5. Press x.

(Also reproduces without $: from line 1 col 10 — e.g. nine l presses — then j onto short; and upward with k from line 3 onto short.)

Expected behavior (Vim)

After $ then j, the cursor lands on the last char of short (the t, col 5) — Vim clamps a too-large desired column to the last character and never lets the cursor rest past it in NORMAL mode. x then deletes the t, giving shor. x in NORMAL can never delete a newline / join lines.

Actual behavior

After $/j (or any j/k onto the shorter line), the status bar shows Ln 2, Col 6 — i.e. column len+1 on a 5-character line, past the last char. Pressing x then deletes the line-ending newline and joins line 3 onto line 2:

short  +  a longer line with many words here   →   shorta longer line with many words here

Reproduced 3/3: via $+j, via l-to-col-10 + j, and via k from line 3 — all land on Col 6 and all join lines on x.

This is specific to vertical motion. Horizontal clamping is correct: pressing $ directly on short lands on Col 5 (the t) and x gives shor (no join); l also stops at the last char. So the column-clamp logic for j/k is off by one relative to $/l.

Workaround

After moving vertically onto a shorter line, press $ (or 0) to re-anchor the cursor onto a real character before using x; or use dl/X carefully. To merge lines deliberately, use J.


Search queries used (no existing issue): vi cursor past end of line j k shorter, vi mode cursor end of line clamp, vi mode cursor end of line.

Filed by the automated black-box TUI QA agent (Run #44).

Activity

  1. added a commit that references this issue on Jun 22, 2026
  2. sinelaw commented on Jul 7, 2026

    @sinelaw
    OwnerAuthor

    Confirmed fixed in v0.4.3 built from origin/master @ 4e945b494 (fix commit 4e945b494 "fix(vi): clamp j/k onto a shorter line to its last character").

    Re-ran the original repro black-box through the UI:

    • $ on a longer first line here (Col 24) then j onto short → cursor now lands at Ln 2, Col 5 (was Col 6), and x deletes t → shor — the newline is no longer deleted, no line join.
    • Col 10 on line 1 then j → clamps to Col 5 on short; continuing j to line 3 restores the goal column (Col 10), matching Vim's desired-column behavior.
    • k from line 3 onto short → Col 5, and x → shor, line 1 untouched.

    3/3 original failure paths pass. Thanks for the fix!


    Generated by Claude Code

  3. added a commit that references this issue on Jul 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions