Skip to content

feat(editor): add opt-in vi-style modal editing#118

Open
laurigates wants to merge 1 commit into
ynqa:mainfrom
laurigates:feat/vi-editing
Open

feat(editor): add opt-in vi-style modal editing#118
laurigates wants to merge 1 commit into
ynqa:mainfrom
laurigates:feat/vi-editing

Conversation

@laurigates

Copy link
Copy Markdown

Summary

This implements the vi-style editing requested in #80 as an opt-in feature
for the query editor. It is disabled by default, so the existing
emacs-style keybindings are completely unchanged for current users. Enable it
with:

[editor.vi]
enable = true

When enabled, the editor starts in NORMAL mode and the prefix (❮❮ by
default, configurable via normal_prefix) indicates the current mode. Press
i/a/I/A to enter INSERT mode — where typing, Tab completion, and the
configured keybinds all work exactly as before — and Esc to return to NORMAL
mode.

What's supported (NORMAL mode)

Category Keys
Motions h l 0 $ ^ w W b B e E f{char} F{char} t{char} T{char} gg G
Insert i I a A
Operators d / c / y + motion, and linewise dd cc yy
Edits x X D C s S r{char} ~
Paste p P (from the last delete/yank)
Counts prefix a motion or operator, e.g. 3w, d2w, 5x
Undo / redo u / Ctrl+R

The cw/cW quirk (acts like ce/cE on a non-blank) is implemented.

Design

  • The modal logic lives in a new self-contained src/vi.rs module, written as
    a pure transformation over (text, cursor) that returns an Outcome the
    query editor applies. This keeps it decoupled from the promkit text widget
    and fully unit-testable — 29 tests cover the motions, operators, counts,
    find, paste, and edge cases (empty buffer, bounds).
  • NORMAL-mode keys route through the engine; INSERT mode falls through to the
    existing text-editing path, so completion and the emacs keybinds are reused
    rather than reimplemented.
  • [editor.vi] is #[serde(default)], so configuration files written before
    this change continue to load (covered by a test).

Scope / limitations

The query buffer is single-line, so linewise commands (dd, cc, yy) act on
the whole line, and o/O/.-repeat/multi-line motions are intentionally out
of scope. Happy to adjust the keymap, defaults, or scope to fit your
preferences — this is meant as a starting point for #80.

Testing

Mirrors CI (.github/workflows/ci.yml):

  • cargo fmt --all -- --check — clean
  • cargo clippy — clean
  • cargo test — all green (29 new vi tests + 2 new config tests)
  • cargo build --bins / --examples — ok

Addresses #80.

Implements vi-style editing for the query editor (issue ynqa#80). Disabled by
default — set `enable = true` under `[editor.vi]` to turn it on — so existing
emacs-style keybindings are unchanged for everyone else.

The modal logic lives in a self-contained `vi` module as a pure transformation
over `(text, cursor)`, decoupled from the promkit text editor and fully unit
tested. The query editor applies the engine's outcomes via the widget's
existing primitives. NORMAL mode routes through the engine; INSERT mode falls
through to the original text-editing path, so typing, completion, and the
configured keybinds keep working unchanged. A configurable NORMAL-mode prefix
makes the current mode visible.

Supported in NORMAL mode:
- Motions: h l 0 $ ^ w W b B e E f F t T gg G
- Operators: d c y with those motions, plus linewise dd cc yy
- Edits: x X D C s S r ~ p P
- Counts (e.g. 3w, d2w) and undo (u) / redo (Ctrl+R)

The query buffer is single-line, so linewise commands act on the whole line and
o/O/./multi-line motions are intentionally unsupported.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@laurigates

Copy link
Copy Markdown
Author

Related PRs (no strict merge order)#118 overlaps two sibling PRs textually: #116 (completion after pipes) via src/query_editor.rs / src/main.rs, and #119 (editor keybinds) via default.toml. Independent in intent; whichever merges first, the others need a quick rebase — no required order.

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