Skip to content

feat(input): add Visual Line mode (V)#90

Open
roramirez wants to merge 1 commit into
mainfrom
feat/visual-line-mode
Open

feat(input): add Visual Line mode (V)#90
roramirez wants to merge 1 commit into
mainfrom
feat/visual-line-mode

Conversation

@roramirez

Copy link
Copy Markdown
Owner

Summary

Adds a vim-like linewise Visual mode. V (from Normal or Visual) enters Visual LINE, where the selection always spans whole lines regardless of the cursor column: j/k extend it line by line, y copies every selected line, v returns to characterwise selection, Esc exits. The status bar badge reads V-LINE.

The mouse path is unified too: triple-click (select_line_at) now leaves the user in Visual LINE, so a mouse-picked line can keep being extended with j/k. Its immediate copy yields identical text to before.

Changes

  • InputMode::Visual gains a linewise: bool field, carried unchanged through every reconstruction of the variant (motions, scroll, boundary scroll, swap anchor).
  • New Action::VisualLineAnchor + AppState::set_visual_line_anchor(), routed from both dispatch_visual_action and dispatch_action. A dedicated action is required because do_set_mode discards keybinding-supplied fields on a Normal→Visual transition, which would silently drop the flag.
  • "V" bound in both the Normal-mode handler and visual_char_action; "v" now explicitly clears linewise, making it the way back to charwise.
  • Only two computations are linewise-aware:
    • Highlightselection_range in src/renderer/text.rs normalizes to (0, start_row, cols-1, cur_row), so nothing downstream in the render pipeline changes.
    • Copied textdo_visual_copy passes sc = 0, ec = cols-1; grid.selected_text's existing row_col_range already makes interior rows full-width, giving exact vim linewise semantics (trailing-space trim, \n join). grid.rs is untouched.
  • mode_style renders V-LINE when linewise, reusing the same theme.palette[5] color as VISUAL.
  • select_line_at builds its mode with linewise: true.
  • Docs: CHANGELOG.md, README.md, doc/SPEC.md, and the Visual Mode section of doc/LLMs.md.

How to test

cargo fmt --check
cargo clippy --locked -- -D warnings
cargo test

1245 tests pass. New coverage:

  • src/input/keybindings_test.rsV yields VisualLineAnchor from Normal and Visual; all motions (h/j/k/l/0/$/g/G, arrows, Home/End) preserve linewise; v returns to charwise. Modifier combos per CLAUDE.md: Ctrl+V and Ctrl+Shift+V do not produce VisualLineAnchor, Shift+V in Insert does not, and Alt+V is asserted to fall through like every other Visual char key (pre-existing behavior for the whole Visual keymap, documented in the test).
  • src/app_state_test.rsVisualLineAnchor yields anchored: true, linewise: true from both Normal and Visual; VisualAnchor clears the flag; a multi-row linewise Copy returns full rows ("hello\nworld") rather than the column-clipped span; boundary scroll, viewport scroll, and swap-anchor all preserve linewise; and V+y over one row produces byte-identical text to the triple-click path.
  • src/renderer/text_test.rs — a linewise selection highlights full rows without panicking; mode_style reports V-LINE with the same color as VISUAL.

Manually: run mmterm, Ctrl+. twice into Visual, press V, extend with j/k, y to copy, then paste to confirm whole lines. Triple-click a line and press j to confirm it extends.

Note: cargo clippy --all-targets reports pre-existing lints (useless_vec, dropping_copy_types) in test files untouched by this branch; the CLAUDE.md-mandated cargo clippy --locked -- -D warnings is clean.

`V` from Normal or Visual enters Visual LINE, where the selection always
spans whole rows: `j`/`k` extend it line by line, `y` copies every selected
line, `v` returns to characterwise, `Esc` exits. The status bar badge reads
`V-LINE`.

`InputMode::Visual` gains a `linewise` flag carried through every motion,
scroll, and boundary-scroll reconstruction. Only two computations are
linewise-aware: the renderer normalizes `selection_range` to full rows, and
`do_visual_copy` passes column 0..cols-1 to `grid.selected_text` — whose
`row_col_range` already yields vim's linewise yank semantics. `V` routes via
a dedicated `Action::VisualLineAnchor` because `do_set_mode` discards
keybinding-supplied fields on a Normal->Visual transition.

Triple-click (`select_line_at`) now also enters Visual LINE, so a
mouse-picked line can be extended with `j`/`k`.
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.

1 participant