Skip to content

Latest commit

History

16,039 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

OpenCode Vim

npm versionCIUpstream syncWebsiteBun

Keyboard-first OpenCode with Vim controls across the TUI, kept in sync with upstream releases.

Installation

# curl
curl -fsSL https://raw.githubusercontent.com/leohenon/opencode-vim/ocv/install.sh | sh
# Package managers
npm i -g @leohenon/ocv
brew install leohenon/tap/ocv
# Arch Linux AUR (community-maintained)
yay -S opencode-vim-bin

Curl installs to ~/.ocv/bin. Set OCV_INSTALL_DIR to install elsewhere.

Usage

ocv

Update

# built-in updater
ocv update
# Package managers
npm i -g @leohenon/ocv@latest
brew upgrade ocv

Prefer a plugin?

The prompt Vim mode is also available as a plugin for the official OpenCode, prompt editing only (no copy mode or session navigation):

opencode plugin @leohenon/opencode-vim-plugin --global

See opencode-vim-plugin.

Features

Prompt controls

Toggle via command palette > Toggle vim mode.

Unicode word boundaries are not supported.

CategoryKeys
Character / wordh, j, k, l, w, b, e, W, B, E
Line / buffer0, ^, _, $, gg, G
Display linegj, gk, g<Down>, g<Up>, g0, g^, g$
Matching / paragraph%, {, }
Find / tillf, F, t, T, ;, ,
ScrollCtrl+e, Ctrl+y, Ctrl+d, Ctrl+u, Ctrl+f, Ctrl+b
Insert / replacei, I, a, A, o, O, R
Character / line editr, x, ~, s, S, J, C, D, dd, cc
Word changescw, cb, ce, cW, cE, ciw, caw, ciW, caW
Word deletesdw, db, de, dW, dE, diw, daw, diW, daW
Quote changesci", ca", ci', ca', ci` , ca`
Quote deletesdi", da", di', da', di` , da`
Bracket changesci(, ca(, ci[, ca[, ci{, ca{, ci<, ca<
Bracket deletesdi(, da(, di[, da[, di{, da{, di<, da<
Find / till operatorscf, cF, ct, cT, df, dF, dt, dT
Matching / paragraph operatorsc%, d%, c}, c{, d}, d{
Line boundary operatorsc0, c^, c$, d0, d^, d$, y0, y^, y$
Display line operatorscgj, cgk, cg0, cg^, cg$, dgj, dgk, dg0, dg^, dg$, ygj, ygk, yg0, yg^, yg$
Line / word yanksyy, yw, ye, yW, yE, yiw, yaw, yiW, yaW
Quote yanksyi", ya", yi', ya', yi` , ya`
Bracket yanksyi(, ya(, yi[, ya[, yi{, ya{, yi<, ya<
Matching / paragraph yanksy%, y}, y{
Put / undo / repeatp, P, u, Ctrl+r, .
Visual selectionv, V
Chat history search/, ?
Commands:, :q

Numeric count prefixes are supported for motions and common operators.

Note

<leader>y copies the prompt selection when present; configure it with keybinds.prompt_copy_selection. For clipboard sync, see System clipboard register.

Copy mode

Text selection from the chat session view.

Copy mode collapses code diffs into a single column for easy copying.

KeysAction
<leader>v, Ctrl+W kEnter copy mode
h, j, k, l, arrow keysNavigate
v, V, Ctrl+VStart character-wise, line-wise, or block selection
y, yyYank to the vim register
EnterCopy to the system clipboard, toggle expandable tool output, or open a selected subagent task
YYank to the vim register and scroll to the bottom
Shift+EnterCopy to the system clipboard and scroll to the bottom
EscapeExit visual mode
qExit copy mode and scroll to the bottom
Ctrl+W jExit copy mode without scrolling
Ctrl+W wToggle copy mode
iFocus the prompt input in insert mode without scrolling
z, zt, zz, zbAdjust copy-mode scroll positioning
H, M, LJump to the top, middle, or bottom of the viewport
/, ?Search forward or backward in chat history
n, NRepeat search in the same or opposite direction

When in search mode, Enter submits the search, and Escape clears search highlights before exiting copy mode.

Search uses smartcase, lowercase queries are case-insensitive, and queries containing uppercase letters are case-sensitive.

Tip

Configure the copy mode entry key with keybinds.copy_mode.

Dialog controls

Searchable dialogs and custom question answer inputs use modal controls.

AreaControls
Input modeEscape enters normal mode; i, a, /, I, A return to insert
Input editingh, l, w, b, e, 0, $ move within the input; dd clears it
Searchable dialogsj, k, gg, G move through dialog items
Question dialogsj, k move through answers; h, l move between questions

Tip

Disable modal dialog inputs with vim_modal_input: false.

Minimal UI

Hides extra UI hints and tips.

Toggle via command palette > Toggle minimal ui.

Configuration

Options

OptionPurpose
prompt_max_heightSet max prompt input height
prompt_scrollbarShow the prompt scrollbar
vim_initial_modeStart in insert (default) or normal
vim_enter_submitSubmit with Enter from insert mode
vim_insert_after_submitReturn to insert mode after submit
vim_system_clipboard_registerUse the system clipboard as Vim register
vim_modal_inputEnable Vim controls in dialogs
vim_langmapMap non-English keys or simple aliases
vim_line_motionsUse wrapped display-line motions
vim_showbreakMark wrapped prompt rows
vim_escape_sequenceUse a two-key escape sequence like jk

Initial mode

Vim mode starts in insert mode by default. To start in normal mode instead:

{
"vim_initial_mode": "normal"
}

Mode-scoped keybinds

Bind existing commands only in normal mode by nesting overrides under vim.normal.

For example, use <space> as the leader:

{
"keybinds": {
"vim.normal": {
"leader": "space",
"session_list": "<leader>s"
}
}
}

When vim.normal.leader is set without a top-level leader, it replaces the implicit default global leader (Ctrl+X). Configure both to keep both leaders.

Prompt input height

Configure prompt input height in tui.json:

{
"prompt_max_height": 35,
"prompt_scrollbar": true
}

prompt_max_height above 40 is not recommended.

Submit behavior

By default, insert mode uses Enter for newlines and normal mode uses Enter to submit.

To submit from insert mode too:

{
"vim_enter_submit": true
}

To always default to insert mode after a prompt submission:

{
"vim_insert_after_submit": true
}

To keep newline available:

{
"keybinds": {
"input_newline": "alt+return"
}
}

Or configure a separate submit key:

{
"keybinds": {
"input_force_submit": "alt+return"
}
}

input_force_submit is unbound by default.

System clipboard register

Use the system clipboard as Vim's register:

{
"vim_system_clipboard_register": true
}

Yank and delete operations sync to the system clipboard, p / P paste from it.

Modal dialog inputs

Disable modal controls in searchable dialogs and custom question answer inputs:

{
"vim_modal_input": false
}

Note

Terminal/OS clipboard shortcuts don’t preserve Vim linewise register state. External clipboard text is pasted as characterwise text.

Vim langmap

Map non-English keyboard layout characters to Vim command keys.

Keys and values should be single characters.

{
"vim_langmap": {
"р": "h",
"о": "j",
"л": "k",
"д": "l"
}
}

Or or for simple aliases:

{
"vim_langmap": {
"H": "^",
"L": "$"
}
}

Vim line motions

Choose how motions handle wrapped lines in the prompt:

{
"vim_line_motions": "logical"
}

Values:

ValueBehavior
logicalVertical and boundary motions use logical lines
display_verticalVertical motions use display lines; boundaries stay logical
displayVertical and boundary motions use display lines

Vim showbreak

Set a marker for wrapped rows in the prompt:

{
"vim_showbreak": true
}

Shows marker in the prompt's left padding.

Vim escape sequence

Set a two-character sequence to leave insert mode without pressing Escape:

{
"vim_escape_sequence": "jk"
}

Neovim integration

Compatible with opencode.nvim. Use the following server config:

localocv_cmd="bash -c 'exec -a opencode ocv --port'"vim.g.opencode_opts= {
server= {
start=function()
require("opencode.terminal").open(ocv_cmd)
end,
toggle=function()
require("opencode.terminal").toggle(ocv_cmd)
end,
},
}

Thanks to all contributors!

@reobin@BrettKulp@lamiphil@XPhyro@shaheislam@semi710@bjschafer

About

OpenCode for people who don’t want to touch the mouse.

Topics

Resources

Contributing

Security policy

Stars

94 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages