Skip to content

Add Vim indent/dedent operators (< and >) to the code editor#14268

Merged
acarl005 merged 7 commits into
masterfrom
factory/vim-indent-operators
Jul 25, 2026
Merged

Add Vim indent/dedent operators (< and >) to the code editor#14268
acarl005 merged 7 commits into
masterfrom
factory/vim-indent-operators

Conversation

@warp-dev-github-integration

@warp-dev-github-integration warp-dev-github-integration Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Description

Adds Vim-style indent (>) and dedent (<) operators to the code editor only (not the terminal command input), per APP-4963. This update also addresses all review feedback from the first review cycle.

  • Shared parser (crates/vim): adds VimOperator::Indent/Dedent and recognizes </> as normal-mode operators — supporting the doubled line forms (>>/<<), operator + motion, text objects, and counts (e.g. 2>>, 3>j, >G, >w) — and as visual-mode operators (V>, v<). Indent/dedent are now included in the . (dot-repeat) machinery, so . replays the last indent/dedent like other operators.
  • Code editor (CodeEditorView): the operation and visual_operator handlers route Indent/Dedent to the existing CodeEditorModel::indent(shift, ctx) primitive (the same one Tab/Shift-Tab use; shift=true is dedent). Indent operators are linewise, so the linewise selection excludes the trailing newline (so the shift never advances onto an extra line) and only adjusts leading indentation; the cursor lands on the first non-whitespace of the affected line, matching existing linewise operators like gc.
  • Terminal command-input EditorView: </> are a true no-op here. The shared VimFSA gates the indent/dedent operators behind a per-VimModel flag (indent_operators_enabled, default false); only the code editor enables it, so the terminal's FSA never enters operator-pending mode for </> and never consumes the following keystroke. The terminal's operation/visual_operator Indent/Dedent arms are retained only for match exhaustiveness.

Review rework (cycle 1)

  • Scope violation (terminal command-input): </> previously entered operator-pending in the shared FSA for every consumer, swallowing the next keystroke in the terminal before its no-op arm ran. Fixed by gating the operators on the VimModel flag so the terminal's FSA treats </> as a true no-op (fall-through), restoring the terminal's pre-feature behavior.
  • Dot-repeat: for_dot_repeat now persists Indent/Dedent operations, so . repeats them.
  • Formatting/CI: ran cargo fmt --all (repo config); cargo fmt --all -- --check now passes on the changed app files.
  • Multi-line indent test expectations: rewrote the >j, 2>>, >G, visual V>, and Vj> app tests with clean, explicit input/expected strings. The prior .unindent()-based expectations were ambiguous/incorrect (unindent ignores the first line when computing common indentation); every affected line now clearly remains indented.

Linked Issue

Verification

Regression tests added:

  • Shared FSA (crates/vim/src/vim_tests.rs): >>, <<, >j, >w, >G, 2>>, 3>j, visual V>/v<; indent/dedent dot-repeat (>> then ., << then .); and disabled-operator (terminal) no-op behavior (> alone, >>, >j not consuming the motion, visual > staying visual).
  • Code editor (app/src/code/editor/view/vim_handler_tests.rs): >>, <<, dedent at column zero (no-op), one-unit dedent, non-leading text preserved, >j, 2>>, >G, visual V>/V</Vj>, u undo, and indent dot-repeat (>> then move + .).

Deterministic checks run in-sandbox (this rework):

  • cargo nextest run -p vim72 passed, 0 failed (includes the new dot-repeat and disabled-behavior regression tests).
  • cargo clippy -p vim --all-targets --tests -- -D warnings → clean.
  • cargo fmt --all -- --check (repo config: imports_granularity=Module, group_imports=StdExternalCrate) → clean.

Sandbox limitation (requires a higher-memory machine before merge)

This PR was prepared in a memory-constrained cloud sandbox (3.9 GB RAM, no swap). The full warp (app) crate could not be compiled here — cargo check -p warp --lib -j 1 (and the same with CARGO_PROFILE_DEV_DEBUG=0) is OOM-killed (SIGKILL) by the kernel during rustc on the app lib, identical to the prior cycle. As a result, the following were not run in-sandbox and are NOT claimed to pass:

  • The code-editor unit tests in app/src/code/editor/view/vim_handler_tests.rs (including the new dot-repeat test and the corrected multi-line indent expectations).
  • ./script/presubmit (which requires the app crate) and cargo clippy -p warp --all-targets.
  • Visual screenshot proof — the Warp GUI cannot be built/run here, so computer-use screenshots could not be captured. This is an environmental limitation, not a code defect; the change is user-facing (code editor Vim mode) and visual proof should be captured on a higher-memory machine.

Before merging, please run on a higher-memory machine:

  • cargo nextest run -p warp test_vim_ (the code-editor Vim tests, including test_vim_indent_dot_repeat_repeats_last_indent and the corrected >j/2>>/>G/V>/Vj> expectations).
  • cargo clippy -p warp --all-targets --tests -- -D warnings and ./script/presubmit.
  • Manually exercise >>, <<, >j, 2>>, >G, V>, V<, and . (dot-repeat) in the code editor with Vim mode on, and confirm the terminal command input is unaffected by </>.

The editor wiring mirrors the existing, proven gc/ToggleComment operator (same selection_change + primitive-call + cursor-placement shape), and the app diff was self-reviewed for type/exhaustiveness correctness.

Screenshots / Videos

Outstanding — see the sandbox limitation note above (the GUI cannot be built/run in this 3.9 GB sandbox).

Agent Mode

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

Originating thread: https://linear.app/warpdotdev/issue/APP-4963/implement-vim-indent-operators-and-in-the-code-editor

Conversation: https://staging.warp.dev/conversation/1ff09567-30d8-497a-b15c-ce10d3a93c4b
Run: https://oz.staging.warp.dev/runs/019f95dd-e7ec-7af8-a7a9-31b41b29ce67

This PR was generated with Oz.

Implements Vim-style `<` (dedent) and `>` (indent) operators in the code
editor only, reusing the existing `CodeEditorModel::indent(shift, ctx)`
primitive that Tab/Shift-Tab already use.

Shared parser (crates/vim):
- Add `VimOperator::Indent` and `VimOperator::Dedent`, mapping `>`/`<`.
- Recognize `<`/`>` as normal-mode operators (with motions, counts, and the
  doubled `>>`/`<<` line forms) and as visual-mode operators.

Code editor (CodeEditorView):
- `operation` and `visual_operator` route Indent/Dedent to `model.indent`,
  with `shift=true` for dedent. Indent operators are linewise, so the
  linewise selection excludes the trailing newline (so the shift primitive
  does not advance onto the next line) and the cursor lands on the first
  non-whitespace of the affected line, matching existing linewise operators.

Terminal command-input EditorView:
- Add explicit no-op arms for Indent/Dedent so the shared parser does not
  add terminal-input behavior; this feature stays code-editor-only.

Tests:
- Shared FSA: `>>`, `<<`, `>j`, `>w`, `>G`, `2>>`, `3>j`, visual `V>`/`v<`.
- Code editor: `>>`, `<<`, dedent at column zero, one-unit dedent,
  non-leading text preserved, `>j`, `2>>`, `>G`, visual `V>`/`V<`/`Vj>`,
  and `u` undo of an indent.

Co-Authored-By: Oz <oz-agent@warp.dev>
@cla-bot cla-bot Bot added the cla-signed label Jul 24, 2026
@warp-dev-github-integration
warp-dev-github-integration Bot marked this pull request as ready for review July 24, 2026 20:27
@oz-for-oss

oz-for-oss Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

@warp-dev-github-integration[bot]

I'm starting a first review of this pull request.

You can view the conversation on Warp.

I completed the review and no human review was requested for this pull request.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

@oz-for-oss oz-for-oss 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

This PR adds Vim </> indent and dedent operators to the shared Vim parser and wires them into the code editor, with terminal command-input handlers intended to remain no-op.

Concerns

  • The shared FSA now recognizes </> for every Vim consumer, so terminal command input can enter operator-pending mode and consume the next key even though the terminal handler later no-ops the operation. That changes the terminal surface despite the PR being code-editor-only.
  • Several added app-level assertions appear to expect only the first line to be indented because unindent ignores the first line when computing common indentation; the multi-line operator tests need to assert all affected lines are shifted.
  • This is a user-facing code-editor Vim behavior change, and the PR description does not include screenshots or a screen recording demonstrating the feature end to end. Per repo guidance, please attach visual evidence before merge.

Verdict

Found: 0 critical, 3 important, 0 suggestions

Request changes

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Comment thread crates/vim/src/vim.rs Outdated
Comment thread app/src/code/editor/view/vim_handler_tests.rs Outdated

@warp-dev-github-integration warp-dev-github-integration Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Overview

Reviewed PR #14268 for APP-4963 against the ticket criteria: Vim </> operators in the code editor only, doubled operators, motions/counts, visual selections, dot-repeat/undo behavior, and unchanged terminal command-input behavior. The parser-focused tests reported by the PR cover the new event shapes, but the acceptance gate is not complete and one required behavior is missing in the implementation.

Concerns

  • [BLOCKING] Dot-repeat is not implemented for the new indent/dedent operations. The for_dot_repeat match only stores Change and Delete operations; Indent and Dedent fall through to None. After >>, <<, or an indent motion, pressing . therefore does not replay that indent operation (and may replay an older operation instead). See the inline comment at the new operator mapping.
  • [BLOCKING] The repository formatting gate fails on this head. cargo fmt --all -- --check reproduces rustfmt diffs in the changed app/src/code/editor/model.rs and app/src/code/editor/view/vim_handler.rs lines. The PR's cargo fmt -p vim --check does not cover those app files, and all four Formatting + Clippy CI jobs are failing for this reason. Please run the repository formatter and commit the result.
  • [BLOCKING / ENVIRONMENTAL] Required user-facing visual proof is missing. The PR states that screenshots/video could not be captured in the sandbox. This is not treated as a code defect, but the code-editor behavior still needs computer-use evidence covering normal >>/<<, motion/count and visual selection behavior, plus confirmation that terminal command-input is unchanged.
  • [BLOCKING / ENVIRONMENTAL] The full code-editor test target and ./script/presubmit remain outstanding because the sandbox ran out of memory/disk. The focused vim parser tests are reported as passing, but the affected app tests and repository gate must pass on a higher-capacity runner before acceptance.

Verdict

Request changes (REJECT). Fix the dot-repeat behavior and formatting, then attach the outstanding higher-capacity test/presubmit results and visual proof. GitHub records this as a comment because the authenticated review identity is the PR author; the factory gate remains blocked.

Review run

https://oz.staging.warp.dev/runs/019f95d2-19e5-7ccb-9967-ab9b6a321f5b

Comment thread crates/vim/src/vim.rs
oz-agent added 2 commits July 24, 2026 21:27
…tting

Rework for APP-4963 review (PR #14268). Addresses all addressable review
comments in this cycle:

1. Scope violation (terminal command-input): the shared VimFSA now gates the
   `<`/`>` (indent/dedent) operators behind a per-VimModel flag
   (`indent_operators_enabled`, default false). Only the code editor's
   CodeEditorView enables it; the terminal command-input EditorView leaves it
   disabled, so `<`/`>` fall through to the FSA's catch-all no-op and never
   enter operator-pending mode or consume the following keystroke. This
   restores the terminal's pre-indent-operator behavior (a true no-op) while
   keeping the operators active in the code editor. The terminal's
   `operation`/`visual_operator` Indent/Dedent arms are kept only for match
   exhaustiveness.

2. Dot-repeat: `VimEventType::for_dot_repeat` now includes `Indent` and
   `Dedent` in the repeatable `Operation` branch, so `.` replays the last
   indent/dedent like other operators.

3. Formatting/CI: ran `cargo fmt --all` (repo config); `cargo fmt --all --
   --check` now passes on the changed app files.

4. Multi-line indent test expectations: rewrote the `>j`, `2>>`, `>G`, visual
   `V>`, and `Vj>` app tests to use clean, explicit input/expected strings
   (the prior `.unindent()`-based expectations were ambiguous/incorrect because
   `unindent` ignores the first line when computing common indentation). Every
   affected line now clearly remains indented.

Tests:
- crates/vim: added `enter_normal_mode_with_indent`/`enter_visual_mode_with_indent`
  helpers, updated the existing indent FSA tests to use them, and added
  regression tests for indent/dedent dot-repeat and for the disabled (terminal)
  no-op behavior (`>` alone, `>>`, `>j` not consuming the motion, visual `>`
  staying visual).
- app code editor: added `test_vim_indent_dot_repeat_repeats_last_indent`.

Co-Authored-By: Oz <oz-agent@warp.dev>

@warp-dev-github-integration warp-dev-github-integration Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Overview

Cumulative re-review (cycle 2) of Vim </> indent/dedent operators for the code editor (head d8b6913). All four cycle-1 findings are resolved and verified: the per-VimModel indent_operators_enabled flag (default false) gates </> so only CodeEditorView enables them and the terminal EditorView treats them as true no-ops (no operator-pending, no consumed keystroke); for_dot_repeat now persists Indent/Dedent so . repeats them; the ambiguous unindent-based multi-line test expectations were rewritten; and cargo fmt --all -- --check, cargo clippy -p vim --all-targets --tests -- -D warnings, and cargo nextest run -p vim (72 passed) are green in-sandbox. The indent(shift, …) mapping is correct (>indent(false), <indent(true), matching Tab/Shift-Tab in actions.rs), and no un-gated From<char> path can construct Indent/Dedent</> only reach the operator mappings through the two gated arms, falling through to the catch-all no-op when disabled. All five acceptance criteria are met by the implementation and its tests.

Concerns

No code-level concerns remain. The only outstanding items are environmental, not code defects: this 3.9 GB / no-swap sandbox is OOM-killed compiling the full warp app crate, so the app-level code-editor unit tests (app/src/code/editor/view/vim_handler_tests.rs), ./script/presubmit, cargo clippy -p warp --all-targets, and the required visual proof for this user-facing change could not be captured here. This is an infrastructure gap that needs a higher-memory machine — another automated rework cycle would hit the same OOM and make no progress. Before merging, on a higher-memory machine run: cargo nextest run -p warp test_vim_, cargo clippy -p warp --all-targets --tests -- -D warnings, ./script/presubmit, and manually exercise >>, <<, >j, 2>>, >G, V>, V<, and . in the code editor with Vim mode on, confirming the terminal command input is unaffected by </>. Also confirm CI completes green on the current head — several checks (Linux/Windows/MacOS test runs, Linux/Windows Formatting+Clippy) were still pending at review time; the release-compilation (Linux/Windows/wasm), MacOS/wasm Formatting+Clippy, CodeQL, and Miscellaneous checks already passed.

Verdict

Found: 0 critical, 0 important, 0 suggestions. Prior concerns: none remain.

Approve — the code is sound and every cycle-1 finding is resolved. This approval is for code quality; the app-level tests and visual proof remain unverified pending a higher-memory machine (an environmental gap, not a code fault), and should be confirmed before merge as listed above.

Review run

https://oz.staging.warp.dev/runs/019f960c-a6c6-719d-b321-5c54e4c81ac0

@acarl005 acarl005 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.

None of the comments you added are useful. Delete them

Comment thread app/src/code/editor/view/vim_handler.rs
Comment thread crates/vim/src/vim.rs Outdated
Co-Authored-By: Oz <oz-agent@warp.dev>
Comment thread app/src/code/editor/view/vim_handler.rs Outdated
Comment thread crates/vim/src/vim.rs Outdated
Comment thread app/src/code/editor/view/vim_handler.rs Outdated
Restore the existing register comment and use VimOperator equality for the ToggleComment check.\n\nCo-Authored-By: Oz <oz-agent@warp.dev>
Use the centralized trailing-newline decision for jump-to-line selections.\n\nCo-Authored-By: Oz <oz-agent@warp.dev>
Use VimOperator::includes_trailing_newline() for all code-editor linewise motion selection paths.\n\nCo-Authored-By: Oz <oz-agent@warp.dev>
@acarl005
acarl005 enabled auto-merge (squash) July 25, 2026 01:11
@acarl005 acarl005 self-assigned this Jul 25, 2026
@acarl005
acarl005 merged commit c0ad836 into master Jul 25, 2026
27 checks passed
@acarl005
acarl005 deleted the factory/vim-indent-operators branch July 25, 2026 01:26
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.

3 participants