Skip to content

Add vim visual modes (v, V) to query editor#172

Open
chasleslr wants to merge 8 commits intoMaxteabag:mainfrom
chasleslr:feat/visual-line-mode
Open

Add vim visual modes (v, V) to query editor#172
chasleslr wants to merge 8 commits intoMaxteabag:mainfrom
chasleslr:feat/visual-line-mode

Conversation

@chasleslr
Copy link
Copy Markdown

@chasleslr chasleslr commented Apr 2, 2026

Motivation

The query editor supports extensive vim motions and operators (d{motion}, y{motion}, c{motion}), but lacks visual mode — one of vim's most fundamental features for interactive text selection. Without it, users must rely on operator + motion combinations for every edit, which is less intuitive for selecting arbitrary ranges of SQL.

Changes

This PR adds both charwise visual mode (v) and visual line mode (V) to the query editor.

Visual mode (v)

Enters character-wise selection at the cursor. All standard motions extend the selection: h/j/k/l, w/b, 0/$/^, f/t/F/T, %, G/gg, and arrow keys.

Visual line mode (V)

Enters line-wise selection on the current line. Vertical motions (j/k/G/gg, arrow keys) extend the selection by full lines.

Common behavior

  • Operators act directly on the selection: y (yank), d (delete), c (change), Enter (execute selected SQL)
  • vV toggles between charwise and linewise selection
  • Esc exits back to NORMAL mode
  • Status bar displays VISUAL or V-LINE mode indicator
  • Footer bindings update to show available actions

Implementation notes

  • Follows the existing TreeVisualModeState pattern for state classes
  • Reuses operator_delete/operator_yank with appropriate MotionType for operators
  • Sets TextArea.selection directly rather than cursor_location to prevent Textual from clearing the highlight on cursor moves
  • Overrides action_cursor_up/down/left/right on QueryTextArea to intercept TextArea's built-in arrow key bindings in visual modes

Demo

Screen.Recording.2026-04-01.at.10.01.03.PM.mov

Test plan

  • v enters charwise visual mode; V enters visual line mode
  • All motions extend selection appropriately in both modes
  • Toggle between modes with v/V
  • y, d, c, Enter operate on selection correctly
  • Esc exits cleanly back to NORMAL mode
  • Existing unit tests pass (130/130)

chasleslr and others added 8 commits April 1, 2026 20:46
Adds Shift+V visual line mode for selecting entire lines in the query
editor, matching vim's native behavior. Selected lines can be yanked (y),
deleted (d), changed (c), or executed (Enter).

- New VimMode.VISUAL_LINE enum value and query_visual_line binding context
- QueryVisualLineModeState for action validation and footer bindings
- QueryEditingVisualLineMixin with enter/exit, selection tracking, and
  line-wise yank/delete/change/execute operators
- Selection updates via TextArea.selection directly to avoid cursor_location
  clearing the highlight
- Arrow key support via QueryTextArea action overrides
- V-LINE mode indicator in status bar and help text

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds both charwise visual mode (v) and visual line mode (V) to the query
editor, matching vim's native behavior.

Visual mode (v): character-wise selection using all standard motions
(h/j/k/l, w/b, 0/$, f/t, G/gg, %, etc). Operators y/d/c act on the
exact selection, Enter executes selected text.

Visual line mode (V): selects entire lines, j/k/G/gg extend by full
lines. Operators act linewise.

Both modes support toggling between each other (v↔V), arrow key
navigation, and display a VISUAL/V-LINE indicator in the status bar.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The tree explorer's ConnectionMixin already defines _update_visual_selection
for tree visual mode. Rename the query editor's method to
_update_query_visual_selection to avoid the name conflict.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Unit tests (22): binding context resolution, state machine action
validation, key routing, and footer bindings for both VISUAL and
VISUAL LINE modes.

UI integration tests (19): enter/exit, mode toggling (v↔V), motion-based
selection extension, and operators (yank, delete, change) using Textual
pilot.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove duplicate 0.15s clear timers in visual_yank and
  visual_line_yank — _flash_yank_range already schedules its own.
  Two competing callbacks could clobber a new selection if the user
  re-enters visual mode within 150ms.
- Clear _visual_anchor before delegating to _change_selection() in
  action_visual_change, so the anchor can't leak if the delegate raises.
- Fix notification color for VISUAL/VISUAL_LINE modes: use insert_color
  only for INSERT, not as the fallback for all non-NORMAL modes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Vim visual mode includes both the anchor and cursor characters in the
selection. Textual's Selection is half-open (end exclusive), so extend
the far end by one character to match vim behavior. Also highlight the
character under the cursor immediately on entering visual mode.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
In vim, x in visual mode is equivalent to d — both delete the selection.
Add x as a secondary binding for visual_delete and visual_line_delete.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The inclusive selection extends the far end by +1, but cursor_location
returns that extended position. Motions reading cursor_location would
operate from the wrong position, making h/left appear stuck when the
cursor was on the same line as the anchor.

Track the logical cursor position in _visual_cursor separately from
the TextArea selection endpoint. Motion functions now read from
_visual_cursor in charwise visual mode instead of cursor_location.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@chasleslr chasleslr marked this pull request as ready for review April 3, 2026 01:05
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