Skip to content

Adapt all GUI Vim backends onto shared snapshot traits - #15664

Open
warp-agent-staging[bot] wants to merge 20 commits into
masterfrom
factory/code-1949-vim-traits-notebook
Open

Adapt all GUI Vim backends onto shared snapshot traits#15664
warp-agent-staging[bot] wants to merge 20 commits into
masterfrom
factory/code-1949-vim-traits-notebook

Conversation

@warp-agent-staging

@warp-agent-staging warp-agent-staging Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Description

Moves Vim navigation, jumps, visual ranges, and text objects into shared VimHandler defaults built on immutable buffer snapshots and selection writes. Terminal input, code editor, notebook, and TUI now reuse the same iterator-based motion logic instead of maintaining editor-specific navigate_* forwarding graphs.

The shared snapshot carries normalized carets and logical goal columns. Each adapter owns only its transaction/selection bridge and irreducible product hooks such as terminal autosuggestions/history, registers, search, LSP, scrolling, and syntax-aware edits. Notebook and code-editor adapters keep logical Vim columns separate from native pixel goals and invalidate them after ordinary edits or cursor changes.

This also enables Vim in editable personal notebooks behind FeatureFlag::VimNotebook and the existing Vim setting. Notebook edit entry resets the FSA to clean Normal, physical Escape wins over command-selection Escape, overlay-owned typing stays out of the document, and shared/read-only notebooks remain immutable. The feature flag is compiled into default/dev builds but is not enabled in dogfood, preview, or release feature sets until promotion.

Tracks CODE-1949.

Computer-use video recordings (1)

Creating a new personal notebook, then demonstrating Vim NORMAL/INSERT toggling with i/Escape, h/l caret movement, x delete, Visual mode via v+l, slash Find, and the long-short-long line j goal-column motion.
Vim keybindings smoke test in Warp notebook: Creating a new personal notebook, then demonstrating Vim NORMAL/INSERT toggling with i/Escape, h/l caret movement, x delete, Visual mode via v+l, slash Find, and the long-short-long line j goal-column motion.

Computer-use screenshots (5)

Editable personal notebook in Vim NORMAL mode after typing 'abcdefghijklmnop' and pressing Escape (status bar bottom-right shows NORMAL)
Editable personal notebook in Vim NORMAL mode after typing 'abcdefghijklmnop' and pressing Escape (status bar bottom-right shows NORMAL)

Visual mode selection via 'v' then 'l' three times (not Shift+Arrow) — status bar shows VISUAL and 'abcd' is highlighted in the notebook line 'abcdefghijklmnop'
Visual mode selection via 'v' then 'l' three times (not Shift+Arrow) — status bar shows VISUAL and 'abcd' is highlighted in the notebook line 'abcdefghijklmnop'

After pressing j from end of long line 'abcdefghijklmnop' (column 15), cursor clamps to 'y', the last character of the short line 'xy'
After pressing j from end of long line 'abcdefghijklmnop' (column 15), cursor clamps to 'y', the last character of the short line 'xy'

After second j onto the third long line 'ABCDEFGHIJKLMNOP', cursor restored to the original column 15 (character 'P'), demonstrating Vim's goal-column memory rather than staying clamped at the short line's last column
After second j onto the third long line 'ABCDEFGHIJKLMNOP', cursor restored to the original column 15 (character 'P'), demonstrating Vim's goal-column memory rather than staying clamped at the short line's last column

Shared read-only notebook 'vim-verify' remains unchanged after attempting i/TESTEDIT/hjkl/Escape — badge still shows 'Viewing', text still 'hello ll00xdwywpAxyz/world', mode remains NORMAL
Shared read-only notebook 'vim-verify' remains unchanged after attempting i/TESTEDIT/hjkl/Escape — badge still shows 'Viewing', text still 'hello ll00xdwywpAxyz/world', mode remains NORMAL

Review guide

Start with crates/vim/src/handler.rs and crates/vim/src/vim.rs for the snapshot/default-handler boundary. Then review the three GUI adapters and crates/warp_tui/src/input/vim.rs; their remaining methods should correspond to backend transactions or product-specific effects. Finally, review notebook lifecycle, keymap, and feature-flag wiring.

Remaining explicit VimHandler behavior

  • All views: snapshot/apply bridge plus backend-specific insert, replace, operation, paste, undo, mode, and search effects.
  • Terminal input: autosuggestion interception and boundary propagation into command history.
  • Code editor: LSP, viewport, formatting, and register effects.
  • Notebook: rich-text transaction behavior, native Find, edit/view lifecycle, and read-only policy.
  • TUI: cursor following, notification, and capability filtering while reusing the code-editor backend.

Linked Issue

  • The linked issue is labeled for the factory workflow.
  • Screenshots and a short video of the user-visible implementation are included above.

Linear: CODE-1949

Testing

  • Manually tested the exact final head in a built Warp GUI.

  • cargo test -p vim --lib: 91 passed.

  • cargo test -p warp --lib vim_handler: 221 passed.

  • Focused code-editor and TUI goal-column regressions passed after the CI correction; notebook, personal-notebook lifecycle, key-dispatch, and TUI range regressions passed during implementation.

  • ./script/format: passed.

  • cargo clippy --locked -p warp --all-targets --tests -- -D warnings: passed.

  • cargo check -p warp_tui --lib: passed before the final correction; the final shared TUI path is covered by the Vim crate and platform CI tests.

  • cargo build --bin warp at 3dac5bde: passed; GUI verified Normal/Insert/Escape, motions, operators, Visual, Find overlay ownership, long→short→long goal-column restoration, and read-only behavior.

  • Full cargo test -p warp_tui --lib remains blocked by unrelated missing get_cursor_shape implementations in WarpUI test delegates; CI platform test jobs cover the branch.

Agent Mode

  • Warp Agent Mode - This PR was created via Warp's AI Agent Mode

CHANGELOG-NONE

Extract the duplicated operator/visual/mode-change glue from the code editor
and TUI handlers into crates/vim::handler, implemented against a buffer-ops
trait. CodeEditorModel and NotebooksEditorModel provide the primitives;
views stay thin adapters for registers, search, and LSP.

Notebooks now enter Normal mode when the global Vim setting is on, reuse the
shared command glue where primitives exist, and no-op unsupported ops.
@warp-agent-staging

Copy link
Copy Markdown
Contributor Author

This PR was generated with Warp.

Comment @warp-factory on this PR to send it follow-up work.

View run View conversation View on Slack

…uction.

Operator support is now false by default so no-op primitives cannot still
mutate selection. Code editor allows all operators; notebooks whitelist
supported ones; TUI filters at the view because it shares CodeEditorModel.
Notebooks enter Normal when Vim is enabled after the view exists.
@warp-agent-staging
warp-agent-staging Bot marked this pull request as ready for review August 29, 2026 21:20

@warp-factories warp-factories Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overview

Solid refactor: operator/visual/mode glue is consolidated into a VimBufferOps trait shared by the code editor, the TUI prompt, and a genuinely new notebook implementation, backed by thorough characterization and new-feature tests. Net position: request changes, for two non-code-correctness gaps below.

Concerns

  • Notebook Vim has no feature flag. vim_mode_enabled (app/src/notebooks/editor/view.rs:1578) gates purely on the shared AppEditorSettings toggle, unlike the code editor's FeatureFlag::VimCodeEditor (app/src/code/editor/view.rs:357). Any user who already enabled Vim for the code editor gets notebook Vim immediately on merge, with no staged rollout or kill switch. Consider adding a dedicated flag mirroring VimCodeEditor.
  • This is a user-facing behavioral change (new Vim keybindings/cursor behavior in notebooks) with no screenshots or recording attached; the PR description states the authoring environment couldn't capture the UI. Per this repo's review policy, environment limitations don't exempt user-visible changes from visual proof — please attach a short recording (e.g. a cloud agent with computer use, or a local ./script/run) showing normal-mode navigation, an operator like dd/yy, insert mode, and visual mode in a notebook.

Verdict

Checks: build n/a (no local toolchain in this review environment), tests reported passing by author (81 vim + 209 warp lib), CI pending/incomplete at review time (one "Check CI results" failure the author attributes to a draft-only gather-job quirk — worth reconfirming now that the PR is ready for review), visual proof missing

Found: 0 critical, 1 important, 0 suggestions, 2 nits

Comment thread app/src/notebooks/editor/view.rs Outdated
Comment thread app/src/notebooks/editor/vim_handler.rs Outdated
Comment thread app/src/code/editor/view/vim_handler.rs Outdated
Char-cell vim_newline(false) leaves the cursor on the original line. The
pre-refactor TUI change_mode called move_right after o; keep that surface
quirk so typing after o goes on the new line.
The required backend is three primitives. Motions, operand ranges,
operators, visual behavior, and insert-position policy live as shared
algorithms over those primitives. Editors keep only irreducible
Buffer-aware hooks (syntax comment/indent, smart open-line, visual
tails, matching-bracket operand selection).
Toggling Vim off then on sent Escape while already in Normal, and the
notebook VimHandler re-dispatched it, looping. Match the code editor:
Normal-mode Escape is a no-op. Cover toggle and read-only notebooks.
Code editor, notebook, and TUI handlers now call vim::handler motion
helpers instead of backend-local vim_navigate_*/vim_move_* methods.
Operand ranges, visual expand, insert-position, and cursor-cap policy
live on the snapshot; backends keep snapshot/set_selections/replace_ranges
plus mechanical indent/comment/open-line hooks. Notebook vim_buffer.rs
drops the duplicated motion clone. Visual keep_selection is stamped on
VimEvent at emit time so queued visual motions still extend the origin.
@warp-agent-staging warp-agent-staging Bot changed the title Share VimHandler glue via VimBufferOps and enable notebook Vim Route Vim navigation through shared snapshot algorithms Aug 29, 2026
Wire EditorModel to VimBufferOps with 0-based/1-based caret conversion,
route motions/operators/mode changes through crates/vim, and delete the
now-unused per-editor vim_select_* helpers.
Keep visual origin on vim_visual_tails, skip newlines for space/backspace,
preserve goal column on j/k, and always store a trailing newline for
linewise yanks.
@warp-agent-staging warp-agent-staging Bot changed the title Route Vim navigation through shared snapshot algorithms Adapt all GUI Vim backends onto shared snapshot traits Aug 30, 2026
Restore linewise EOF register newlines, paragraph object ranges, visual
tails, charwise yank cursor policy, and G/wrapping quirks so EditorView
matches pre-refactor tests without per-command glue.
Navigation, jumps, and visual text objects now live in VimHandler
default methods via map_vim_snapshot. Views only implement snapshot
apply, view-only intercepts, and edit/undo backends. Also share
wrapping skip-vs-count and vertical goal-column, no-op counted
notebook r when it would overrun the line, and delete the unused
code-editor vim_select helper graph.
Notebook and code-editor snapshot adapters persist a logical Vim
character goal column, so j/k restore after a short line without
reading SelectionModel.goal_xs as character columns. Remove the unused
vim_visual_tails helper graph and selection stash.
TUI Visual used raw inclusive carets as exclusive ranges, so empty Visual
dropped the block-cursor character and Visual Line did not cover the line.
Share expand_visual_range with operator expansion and paint those ranges.

Independent selection/content changes now clear adapter goal columns, while
Vim's own selection write is recognized by the heads it just applied.
ViewHandle has no model() method; go through view.update like nearby TUI tests.
Platform typed-character events can arrive as UserTyped even after Escape
puts the FSA in Normal. That path inserted hjkl instead of running motions.
UserTyped and VimUserTyped now no-op when a child overlay owns input, matching the pre-unification should_handle_user_input gate.
Create-personal-notebook uses NotebookView, which now enters Normal when switching to edit. The editor paints NORMAL/INSERT/VISUAL so the pane matches other Vim surfaces. A lifecycle test constructs that pane, enables Vim, and checks off→on.
Escape now runs for Visual, Replace, pending Normal commands, and Normal itself. Server-confirmed baton grabs normalize only when the editor was not already editable, so optimistic Insert is kept.
Select-command-at-cursor was an editable Escape binding that outranked VimEscape, so Insert never left. Exclude Vim from those bindings and dispatch Escape through the keybinding matcher in a pane test.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants