Skip to content

kkkksu/vimcode

 
 

Repository files navigation

vimcode

Vim keybindings for the OpenCode prompt.

Experimental. Things will break.

vimcodefull.mp4

Install · Configuration · What it does · Keybindings · Known gaps · How it works · Contributing


Install

Add to your tui.json (or .opencode/tui.json):

{
  "plugin": ["vimcode@git+https://github.com/oribarilan/vimcode.git#v0.9.0"]
}

To upgrade, change the version tag and restart OpenCode. You'll see a toast when a newer version is available.

Configuration

To pass options, use the tuple form in tui.json:

{
  "plugin": [["vimcode@git+https://github.com/oribarilan/vimcode.git#v0.9.0", { "updateCheck": false }]]
}
Option Type Default Description
updateCheck boolean true On startup, check GitHub for new versions (at most once per day). This is the only network request vimcode makes. Set to false to disable.
modeToast boolean true Show a brief toast ("NORMAL" / "INSERT" / "VISUAL") on mode switches. Set to false to rely on cursor shape alone.
startMode "insert" | "normal" "insert" Which mode to start in when OpenCode launches.

What it does

Adds normal/insert mode to OpenCode's prompt input. Escape enters normal mode, i goes back to insert. A brief toast shows the current mode on each switch (configurable).

In insert mode, typing works normally. Enter adds a newline, Ctrl+Enter submits. The file picker and autocomplete keep working: Enter picks the selected item, Escape closes the picker without leaving insert.

In normal mode, keys are vim commands. Unrecognized keys get swallowed so you don't accidentally type into the prompt. : opens the command palette.

Overlay passthrough

When OpenCode shows its own UI (command palette, /sessions, the @ file picker, question prompts, permission prompts) vimcode steps aside. All keys pass through to the overlay until it closes.

Escape behavior

First Escape in insert mode switches to normal - it won't trigger OpenCode's double-escape interrupt. So canceling a running response from insert mode takes 3 escapes: one for normal, two more for the interrupt.

Platform notes

Clipboard (y, yy, p) uses the system clipboard: pbcopy on macOS, clip.exe on Windows, xclip on Linux. Linux users need xclip installed (apt install xclip or equivalent). If the clipboard tool is missing, yank/paste still works within the session via an internal register.

Cursor shape (block in normal, bar in insert) needs a terminal that supports DECSCUSR escape sequences. Most modern terminals do: iTerm2, Ghostty, Alacritty, Windows Terminal, Kitty. Older macOS Terminal.app may not respond.

The plugin checks GitHub for new versions once per day on startup. No other network requests, no telemetry.

Keybindings

Motions

Key Action
h j k l Left, down, up, right
w b e Word forward, backward, forward
0 ^ Line start
$ Line end
G Buffer end

All motions take counts: 3j moves down 3 lines.

When the input is empty, j/k scroll through prompt history instead of moving the cursor.

Operators

d (delete), c (change), and y (yank) combine with motions:

Combo Action
dd cc yy Operate on whole line
D C Delete/change to end of line
dw cw yw To next word
db cb yb To previous word
de ce ye To end of word
d$ c$ y$ To end of line
d0 c0 y0 To start of line
d^ c^ y^ To start of line
dh ch yh Character left
dl cl yl Character right
dj cj yj Current + line below
dk ck yk Current + line above
yG To end of buffer (yank only)

Counts work on both operator and motion: 2dd deletes 2 lines, d3w deletes 3 words.

Insert entries

Key Action
i Insert at cursor
a Insert after cursor
A Insert at end of line
o Open line below
O Open line above

Visual mode

Press v in normal mode to enter character-wise visual mode. Motions extend the selection, operators act on it:

Key Action
d x Delete selection
c Delete selection, enter insert mode
y Yank (copy) selection
Escape v Exit visual mode

All normal-mode motions work for extending the selection: h j k l w b e 0 $ G, with counts.

Other

Key Action
r{char} Replace character under cursor with {char}
x Delete character
u Undo
Ctrl+r Redo
p Paste from yank register
: Command palette
/ Jump to message (session timeline)
[ ] Scroll conversation half-page up/down
{ } Jump to previous/next message
X Backspace
J Join current line with next
j k Cycle prompt history (when input is empty)
Enter Submit prompt
Escape Pass through for double-escape interrupt

Known gaps

  • V, Ctrl+v - only character-wise visual mode (v) is supported, no line-wise or block
  • ciw, di", etc. (text objects) - not yet implemented
  • gg - single g goes to buffer start immediately, doesn't wait for a second keypress
  • dG, cG - delete/change to buffer end not yet implemented (yG works)
  • e behaves the same as w - the host doesn't expose a separate "end of word" command
  • yy accuracy - line position is tracked with a counter that drifts on clicks and arrow keys
  • No persistent mode indicator - the toast fades after about a second. Cursor shape is the persistent signal, but a status bar indicator would need the host's SolidJS runtime, which external plugins can't access.

Configurable key bindings are next once the core vim coverage stabilizes.

How it works

vimcode registers a key intercept on every prompt keypress. A pure handler in src/vim.ts takes the current mode and key, returns a list of actions (move cursor, delete word, switch mode, etc.) without touching the plugin API. src/index.ts applies those actions through @opentui/keymap commands.

Contributing

  1. Try it
  2. If it's useful, a star helps others find it
  3. Open issues for bugs or missing keybindings
  4. PRs welcome. See CONTRIBUTING.md for dev setup and the release process.

License

MIT

About

Vim for OpenCode.

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • TypeScript 99.1%
  • Just 0.9%