Add ergonomic Vim window navigation

This commit is contained in:
m1ngsama 2026-05-24 09:45:32 +08:00
parent d3a137a36e
commit 16c1974e1a
11 changed files with 82 additions and 24 deletions

View file

@ -65,6 +65,7 @@ s + 2ch jump on screen gd / gr definition / references
SPC rr run current file SPC gs git status
SPC cf format SPC ca code action
SPC fc local config SPC ? active cheat sheet
Ctrl-hjkl windows SPC e sidebar
:ChopsticksStatus health :ChopsticksConfig preferences
```
@ -76,6 +77,7 @@ file, edit Markdown, and use a machine with missing optional tools.
- `s` as the default visible jump still feels worth the native override after
real editing.
- No high-frequency action requires remembering an undocumented key.
- Window/sidebar navigation feels faster than native `<C-w>` only.
- README, QUICKSTART, `:help chopsticks`, `SPC ?`, and `:ChopsticksTutor`
teach the same layout.
- No private wiki or external note is needed to remember the daily loop.

View file

@ -86,7 +86,8 @@ SPC SPC fuzzy find file (git-aware)
SPC / ripgrep project
SPC , search buffers
SPC fr recent files
SPC e file browser
SPC e sidebar at project cwd
SPC E sidebar at current file dir
SPC Tab last file
```
@ -134,7 +135,9 @@ SPC y clipboard yank
## Navigate
```
<C-w>h/j/k/l splits
Ctrl-h/j/k/l splits
<C-w>h/j/k/l native Vim fallback
SPC e, Ctrl-h/l open sidebar, enter/leave it
SPC bp / SPC bn prev / next buffer
SPC z maximize window
SPC tt / SPC th terminal

View file

@ -49,7 +49,7 @@ That assembly work is the pain chopsticks removes:
diagnostics live behind unrelated commands unless you design a system.
- **Plugin defaults fight muscle memory.** chopsticks gives QWERTY users one
canonical Space layout and keeps native Vim/LSP habits where they matter:
`gd`, `gr`, `K`, `<C-w>hjkl`, `cl`, `cc`.
`gd`, `gr`, `K`, `Ctrl-h/j/k/l`, `<C-w>hjkl`, `cl`, `cc`.
- **Remote editing is fragile.** It is built to degrade on TTY, slow SSH, and
headless machines instead of assuming a GUI desktop.
- **Custom configs are hard to onboard.** `:ChopsticksHelp`, `SPC ?`,
@ -62,7 +62,7 @@ integration, format-on-save — not a 20-minute setup.
chopsticks gives you a production-ready Vim config in one command. Pure VimScript — no Node.js for the core. Degrades gracefully on TTY. Works the same on your MacBook and your headless Arch box.
**2325 plugins** (tmux-navigator loads only inside tmux; auto-pairs is opt-in), LSP, linting, and a hand-built statusline. No bloat, no decorations, just tools.
**23+ plugins** depending on profile and opt-ins, LSP, linting, and a hand-built statusline. No bloat, no decorations, just tools.
## What's in the box
@ -124,6 +124,7 @@ let g:chopsticks_enable_sudo_save_bang = 1 " optional: :w!! sudo save
let g:chopsticks_enable_completion_keymaps = 1 " optional: Tab/Enter completion
let g:chopsticks_enable_auto_pairs = 1 " optional: automatic pair insertion
let g:chopsticks_enable_terminal_keymaps = 1 " optional: terminal Esc/Ctrl navigation
let g:chopsticks_enable_tmux_navigator = 1 " optional: vim-tmux-navigator integration
let g:chopsticks_enable_exrc = 1 " optional: source project-local .vimrc/.exrc from CWD
let g:chopsticks_enable_reindent_file = 1 " optional: full-file reindent map
```
@ -144,7 +145,8 @@ Default layout: `space`, leader `SPC`, localleader `,`.
This is the canonical layout for QWERTY keyboards with CapsLock mapped to
tap-Esc / hold-Ctrl. Escape and Ctrl stay at the system layer; Vim keeps the
native `<C-w>` window model and standard LSP motions (`gd`, `gr`, `K`).
native `<C-w>` window model as a fallback and standard LSP motions (`gd`,
`gr`, `K`).
Git push/pull are intentionally not bound to default hotkeys. Normal-mode `s`
is a screen-local EasyMotion jump; use `cl` for native `s` substitute and `cc`
for native `S`.
@ -162,6 +164,7 @@ appends a timestamped session block.
SPC SPC fuzzy find file gd go to definition
SPC / ripgrep project K hover docs
SPC e toggle file sidebar SPC rr run current file
Ctrl-h/l enter/leave sidebar Ctrl-hjkl windows
SPC gs git status SPC cf format
SPC w save SPC qq quit
Esc exit insert mode SPC ? cheat sheet
@ -197,7 +200,7 @@ Esc exit insert mode SPC ? cheat sheet
### Windows
`<C-w>hjkl` navigate | `SPC z` maximize | `SPC bp` `SPC bn` buffers | `SPC bd` close buffer | `SPC bo` close other buffers | `SPC tt` `SPC th` terminal | `]q` `[q` quickfix | `SPC xq` `SPC xQ` open/close quickfix | `SPC xl` `SPC xL` open/close loclist
`Ctrl-h/j/k/l` windows | `<C-w>h/j/k/l` native fallback | `SPC z` maximize | `SPC bp` `SPC bn` buffers | `SPC bd` close buffer | `SPC bo` close other buffers | `SPC tt` `SPC th` terminal | `]q` `[q` quickfix | `SPC xq` `SPC xQ` open/close quickfix | `SPC xl` `SPC xL` open/close loclist
### Markdown
@ -234,7 +237,7 @@ Esc exit insert mode SPC ? cheat sheet
### Classic Windows
`<C-w>hjkl` navigate | `,z` maximize | `,h` `,l` buffers | `,bd` close buffer | `,=` `,-` resize | `,tv` `,th` terminal
`Ctrl-h/j/k/l` windows | `<C-w>h/j/k/l` native fallback | `,z` maximize | `,h` `,l` buffers | `,bd` close buffer | `,=` `,-` resize | `,tv` `,th` terminal
### Classic Markdown
@ -297,7 +300,7 @@ For Markdown LSP, install or select `marksman` first.
│ └── chopsticks.txt :help chopsticks
└── modules/
├── env.vim TTY detection, truecolor, skip built-in plugins
├── plugins.vim vim-plug + 2325 plugins
├── plugins.vim vim-plug + profile/option-driven plugins
├── core.vim settings, keymaps, performance
├── ui.vim solarized, statusline, startify
├── editing.vim easymotion, yank highlight, blank lines

View file

@ -21,7 +21,7 @@ self-documenting keys for people who edit locally and over SSH.
It is meant to supplement stock Vim, not replace Vim muscle memory. Native
motions and well-known conventions stay where they are useful: gd, gr, K,
<C-w>hjkl, cl, cc, quickfix, and normal Vim commands.
Ctrl-h/j/k/l, <C-w>hjkl, cl, cc, quickfix, and normal Vim commands.
==============================================================================
FIRST FIVE MINUTES *chopsticks-start*
@ -50,8 +50,8 @@ CANONICAL SPACE LAYOUT *chopsticks-v3-space
Default layout: Space leader, comma localleader.
This layout assumes a QWERTY keyboard and CapsLock mapped at the system layer
to tap-Esc / hold-Ctrl. Vim keeps <C-w>hjkl for windows and standard LSP
motions for code.
to tap-Esc / hold-Ctrl. Ctrl-h/j/k/l is the fast path for Vim windows;
<C-w>hjkl remains the native fallback. Standard LSP motions stay on code.
Normal-mode s is a visible EasyMotion jump. This is intentionally different
from stock Vim because screen-local jumping is higher value in project editing.
@ -62,6 +62,7 @@ High-frequency keys:
SPC SPC files SPC , buffers
SPC / grep project SPC Tab alternate file
SPC e/E file sidebar SPC rr run file
Ctrl-hjkl windows Ctrl-h/l enter/leave sidebar
SPC gs git status SPC gl git log graph
SPC ca code action SPC cr rename
SPC cf format SPC fc edit local config

View file

@ -43,6 +43,7 @@ function! s:OpenBetaGuide() abort
\ ' exit criteria',
\ ' s as jump still feels worth the native override',
\ ' no high-frequency action needs an undocumented key',
\ ' window/sidebar navigation beats native <C-w> only',
\ ' README, QUICKSTART, SPC ?, and tutor teach the same layout',
\ ' no private wiki is needed to remember the daily loop',
\ ' quick/vim tests pass locally and over SSH',

View file

@ -126,7 +126,8 @@ function! s:CheatSheet() abort
\ ' [x ]x conflict markers',
\ '',
\ ' ── windows ───────────────',
\ ' <C-w>hjkl navigate splits',
\ ' Ctrl-hjkl windows',
\ ' <C-w>hjkl native fallback',
\ ' SPC bp/bn prev / next buf',
\ ' SPC bd close buffer',
\ ' SPC bo close other buffers',
@ -249,7 +250,8 @@ function! s:CheatSheet() abort
\ ' [x ]x conflict markers',
\ '',
\ ' ── windows ───────────────',
\ ' <C-w>hjkl navigate splits',
\ ' Ctrl-hjkl windows',
\ ' <C-w>hjkl native fallback',
\ ' ,h ,l prev / next buf',
\ ' ,bd close buffer',
\ ' ,z maximize toggle',

View file

@ -34,6 +34,8 @@ let g:chopsticks_enable_auto_pairs = get(g:,
\ 'chopsticks_enable_auto_pairs', 0)
let g:chopsticks_enable_terminal_keymaps = get(g:,
\ 'chopsticks_enable_terminal_keymaps', 0)
let g:chopsticks_enable_tmux_navigator = get(g:,
\ 'chopsticks_enable_tmux_navigator', 0)
let g:chopsticks_markdown_lint = get(g:, 'chopsticks_markdown_lint',
\ s:profile_full)

View file

@ -28,12 +28,29 @@ function! s:ToggleSidebar(...) abort
wincmd p
endfunction
function! s:NavigateWindow(direction) abort
execute 'wincmd ' . a:direction
endfunction
nnoremap <silent> <C-h> :<C-U>call <SID>NavigateWindow('h')<CR>
nnoremap <silent> <C-j> :<C-U>call <SID>NavigateWindow('j')<CR>
nnoremap <silent> <C-k> :<C-U>call <SID>NavigateWindow('k')<CR>
nnoremap <silent> <C-l> :<C-U>call <SID>NavigateWindow('l')<CR>
nnoremap <silent> <leader>e :call <SID>ToggleSidebar()<CR>
nnoremap <silent> <leader>E :call <SID>ToggleSidebar(expand('%:p:h'))<CR>
function! s:NetrwKeymaps() abort
setlocal bufhidden=wipe
nnoremap <buffer> <silent> <C-h> :<C-U>call <SID>NavigateWindow('h')<CR>
nnoremap <buffer> <silent> <C-j> :<C-U>call <SID>NavigateWindow('j')<CR>
nnoremap <buffer> <silent> <C-k> :<C-U>call <SID>NavigateWindow('k')<CR>
nnoremap <buffer> <silent> <C-l> :<C-U>call <SID>NavigateWindow('l')<CR>
endfunction
augroup ChopstickNetrw
autocmd!
autocmd FileType netrw setlocal bufhidden=wipe
autocmd FileType netrw call s:NetrwKeymaps()
augroup END
" ── FZF ─────────────────────────────────────────────────────────────────────
@ -138,9 +155,9 @@ if has('terminal')
endif
if g:chopsticks_enable_terminal_keymaps
tnoremap <Esc><Esc> <C-\><C-n>
tnoremap <C-h> <C-\><C-n><C-w>h
tnoremap <C-j> <C-\><C-n><C-w>j
tnoremap <C-k> <C-\><C-n><C-w>k
tnoremap <C-l> <C-\><C-n><C-w>l
tnoremap <C-h> <C-\><C-n>:<C-U>call <SID>NavigateWindow('h')<CR>
tnoremap <C-j> <C-\><C-n>:<C-U>call <SID>NavigateWindow('j')<CR>
tnoremap <C-k> <C-\><C-n>:<C-U>call <SID>NavigateWindow('k')<CR>
tnoremap <C-l> <C-\><C-n>:<C-U>call <SID>NavigateWindow('l')<CR>
endif
endif

View file

@ -62,7 +62,7 @@ if g:chopsticks_enable_ui_extras
Plug 'mhinz/vim-startify'
endif
Plug 'lifepillar/vim-solarized8'
if !empty($TMUX)
if g:chopsticks_enable_tmux_navigator && !empty($TMUX)
Plug 'christoomey/vim-tmux-navigator'
endif

View file

@ -73,7 +73,9 @@ function! s:ChopsticksTutor() abort
\ ' 6. git and windows',
\ ' SPC gs/gd/gb status / diff / blame',
\ ' SPC gl log graph',
\ ' <C-w>hjkl split navigation',
\ ' Ctrl-h/j/k/l split navigation',
\ ' <C-w>hjkl native fallback',
\ ' SPC e, Ctrl-h/l enter/leave sidebar',
\ ' SPC z maximize split',
\ '',
\ ' daily drill',
@ -106,7 +108,8 @@ function! s:ChopsticksTutor() abort
\ ' gc comment',
\ ' ,u undo tree',
\ ' ,gs/,gd/,gb status / diff / blame',
\ ' <C-w>hjkl split navigation',
\ ' Ctrl-h/j/k/l split navigation',
\ ' <C-w>hjkl native fallback',
\ '',
\ ' support',
\ ' ,ec edit local config',

View file

@ -112,7 +112,8 @@ check_vim() {
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
-c 'let last_change_map = nr2char(96) . "[v" . nr2char(96) . "]"' \
-c 'if maparg("0", "n") !=# "" || maparg("0", "v") !=# "" || maparg("Y", "n") !=# "" || maparg("Q", "n") !=# "" || maparg("<Space>", "n") !=# "" || maparg("//", "v") !=# "" || maparg("gV", "n") !=# "" || maparg("jk", "i") !=# "" || maparg("<C-s>", "n") !=# "" || maparg("<C-s>", "i") !=# "" || maparg("<C-h>", "n") !=# "" || maparg("<C-j>", "n") !=# "" || maparg("<C-k>", "n") !=# "" || maparg("<C-l>", "n") !=# "" || maparg("<C-p>", "n") !=# "" || maparg("<C-p>", "c") !=# "" || maparg("<C-n>", "c") !=# "" || maparg("w!!", "c") !=# "" | cquit | endif' \
-c 'if maparg("0", "n") !=# "" || maparg("0", "v") !=# "" || maparg("Y", "n") !=# "" || maparg("Q", "n") !=# "" || maparg("<Space>", "n") !=# "" || maparg("//", "v") !=# "" || maparg("gV", "n") !=# "" || maparg("jk", "i") !=# "" || maparg("<C-s>", "n") !=# "" || maparg("<C-s>", "i") !=# "" || maparg("<C-p>", "n") !=# "" || maparg("<C-p>", "c") !=# "" || maparg("<C-n>", "c") !=# "" || maparg("w!!", "c") !=# "" | cquit | endif' \
-c 'if maparg("<C-h>", "n") !~# "NavigateWindow" || maparg("<C-j>", "n") !~# "NavigateWindow" || maparg("<C-k>", "n") !~# "NavigateWindow" || maparg("<C-l>", "n") !~# "NavigateWindow" | cquit | endif' \
-c 'if has_key(g:plugs, "auto-pairs") || maparg("<Tab>", "i") =~# "pumvisible" || maparg("<S-Tab>", "i") =~# "pumvisible" || maparg("<CR>", "i") =~# "asyncomplete#close_popup" || maparg("<CR>", "i") =~# "AutoPairs" | cquit | endif' \
-c 'if maparg("<Esc><Esc>", "t") !=# "" || maparg("<C-h>", "t") !=# "" || maparg("<C-j>", "t") !=# "" || maparg("<C-k>", "t") !=# "" || maparg("<C-l>", "t") !=# "" | cquit | endif' \
-c 'if maparg("s", "n") !~# "easymotion-overwin-f2" | cquit | endif' \
@ -125,6 +126,7 @@ check_vim() {
-c 'source .vimrc' \
-c 'let last_change_map = nr2char(96) . "[v" . nr2char(96) . "]"' \
-c 'if mapleader !=# "," || maparg("s", "n") !=# "" || maparg(",/", "v") !~# "escape" || maparg(",v", "n") !=# last_change_map || maparg(",ff", "n") !~# "SmartFiles" | cquit | endif' \
-c 'if maparg("<C-h>", "n") !~# "NavigateWindow" || maparg("<C-j>", "n") !~# "NavigateWindow" || maparg("<C-k>", "n") !~# "NavigateWindow" || maparg("<C-l>", "n") !~# "NavigateWindow" | cquit | endif' \
-c 'if maparg(",ec", "n") !~# "ChopsticksConfig" || maparg(",sv", "n") !~# "ChopsticksReload" | cquit | endif' \
-c 'if maparg(",gp", "n") !=# "" || maparg(",gl", "n") !=# "" | cquit | endif' \
-c 'qa!' 2>&1
@ -153,7 +155,24 @@ check_vim() {
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u NONE -i NONE -es -N \
-c 'let g:chopsticks_enable_terminal_keymaps = 1' \
-c 'source .vimrc' \
-c 'if has("terminal") && (maparg("<Esc><Esc>", "t") !~# "<C-\\\\><C-N>" || maparg("<C-h>", "t") !~# "<C-W>h" || maparg("<C-j>", "t") !~# "<C-W>j" || maparg("<C-k>", "t") !~# "<C-W>k" || maparg("<C-l>", "t") !~# "<C-W>l") | cquit | endif' \
-c 'if has("terminal") && (maparg("<Esc><Esc>", "t") !~# "<C-\\\\><C-N>" || maparg("<C-h>", "t") !~# "NavigateWindow" || maparg("<C-j>", "t") !~# "NavigateWindow" || maparg("<C-k>", "t") !~# "NavigateWindow" || maparg("<C-l>", "t") !~# "NavigateWindow") | cquit | endif' \
-c 'qa!' 2>&1
TMUX=/tmp/chopsticks-test XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
-c 'if has_key(g:plugs, "vim-tmux-navigator") | cquit | endif' \
-c 'qa!' 2>&1
TMUX=/tmp/chopsticks-test XDG_CONFIG_HOME="$EMPTY_XDG" vim -u NONE -i NONE -es -N \
-c 'let g:chopsticks_enable_tmux_navigator = 1' \
-c 'source .vimrc' \
-c 'if !has_key(g:plugs, "vim-tmux-navigator") | cquit | endif' \
-c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
-c 'setfiletype netrw' \
-c 'if &filetype !=# "netrw" | cquit | endif' \
-c 'if !maparg("<C-l>", "n", 0, 1).buffer | cquit | endif' \
-c 'if maparg("<C-h>", "n") !~# "NavigateWindow" || maparg("<C-l>", "n") !~# "NavigateWindow" | cquit | endif' \
-c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
@ -281,7 +300,8 @@ check_vim() {
grep -Fq 'gd definition' "$TMP_ROOT/cheat-default.txt"
grep -Fq 'K hover docs' "$TMP_ROOT/cheat-default.txt"
grep -Fq '[d ]d LSP diagnostics' "$TMP_ROOT/cheat-default.txt"
grep -Fq '<C-w>hjkl navigate splits' "$TMP_ROOT/cheat-default.txt"
grep -Fq 'Ctrl-hjkl windows' "$TMP_ROOT/cheat-default.txt"
grep -Fq '<C-w>hjkl native fallback' "$TMP_ROOT/cheat-default.txt"
grep -Fq 'SPC w save' "$TMP_ROOT/cheat-default.txt"
grep -Fq 'SPC fc edit local config' "$TMP_ROOT/cheat-default.txt"
grep -Fq 's+2ch easymotion jump' "$TMP_ROOT/cheat-default.txt"
@ -379,6 +399,8 @@ check_vim() {
grep -Fq 'SPC fc edit local config' "$TMP_ROOT/tutor-default.txt"
grep -Fq ':ChopsticksHelp full help' "$TMP_ROOT/tutor-default.txt"
grep -Fq ':ChopsticksConfig local config' "$TMP_ROOT/tutor-default.txt"
grep -Fq 'Ctrl-h/j/k/l split navigation' "$TMP_ROOT/tutor-default.txt"
grep -Fq 'SPC e, Ctrl-h/l enter/leave sidebar' "$TMP_ROOT/tutor-default.txt"
grep -Fq 's + 2 chars visible jump' "$TMP_ROOT/tutor-default.txt"
grep -Fq 'cl / cc native s / S substitute' "$TMP_ROOT/tutor-default.txt"
grep -Fq 'gd / gr / K definition / refs / docs' "$TMP_ROOT/tutor-default.txt"
@ -396,6 +418,7 @@ check_vim() {
grep -Fq 'Goal: train one long-term project loop around Vim.' "$TMP_ROOT/tutor-classic.txt"
grep -Fq ',? active cheat sheet' "$TMP_ROOT/tutor-classic.txt"
grep -Fq ',ec edit local config' "$TMP_ROOT/tutor-classic.txt"
grep -Fq 'Ctrl-h/j/k/l split navigation' "$TMP_ROOT/tutor-classic.txt"
grep -Fq ',S + 2 chars EasyMotion jump' "$TMP_ROOT/tutor-classic.txt"
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
@ -409,6 +432,7 @@ check_vim() {
grep -Fq 'Prove this can be a long-term project loop.' "$TMP_ROOT/beta-guide.txt"
grep -Fq 'Record real editing friction, not abstract taste.' "$TMP_ROOT/beta-guide.txt"
grep -Fq 'no private wiki is needed to remember the daily loop' "$TMP_ROOT/beta-guide.txt"
grep -Fq 'window/sidebar navigation beats native <C-w> only' "$TMP_ROOT/beta-guide.txt"
grep -Fq 'SPC ? active cheat sheet' "$TMP_ROOT/beta-guide.txt"
grep -Fq 'BETA.md full beta checklist and rollback' "$TMP_ROOT/beta-guide.txt"
grep -Fq ':ChopsticksBetaLog editable local beta notes' "$TMP_ROOT/beta-guide.txt"