Revert "Release v3.0.0 ergonomic keymap"

This reverts commit d56ca80da7.
This commit is contained in:
m1ngsama 2026-05-21 12:02:55 +08:00
parent d56ca80da7
commit f38bdf6e57
28 changed files with 958 additions and 1665 deletions

View file

@ -10,4 +10,4 @@
- [ ] `vim --startuptime` shows no regression
- [ ] Tested on macOS / Linux
- [ ] `SPC ?` cheat sheet still accurate
- [ ] `,?` cheat sheet still accurate

12
.github/demo.tape vendored
View file

@ -24,16 +24,16 @@ Type "vim server.py"
Enter
Sleep 3.5s
# ── 2. Fuzzy find files (SPC SPC → type → select) ────────────────────────
Type " "
# ── 2. Fuzzy find files (,ff → type → select) ────────────────────────────
Type ",ff"
Sleep 1.5s
Type "route"
Sleep 2.5s
Enter
Sleep 3s
# ── 3. Ripgrep project search (SPC / → query → select) ───────────────────
Type " /"
# ── 3. Ripgrep project search (,rg → query → select) ──────────────────────
Type ",rg"
Sleep 1.5s
Type "def "
Sleep 3s
@ -47,12 +47,12 @@ Sleep 4.5s
Enter
Sleep 1.5s
# ── 5. Cheat sheet (SPC ?) ────────────────────────────────────────────────
# ── 5. Cheat sheet (,?) ───────────────────────────────────────────────────
# Reset to server.py so cheat sheet shows code on left, keys on right.
Type ":edit server.py"
Enter
Sleep 1s
Type " ?"
Type ",?"
Sleep 5.5s
Type "q"
Sleep 0.5s

8
.vimrc
View file

@ -27,12 +27,4 @@ call s:load('lsp')
call s:load('lint')
call s:load('git')
call s:load('languages')
call s:load('buffers')
call s:load('utilities')
call s:load('files')
call s:load('runner')
call s:load('quickfix')
call s:load('status')
call s:load('cheatsheet')
call s:load('tutor')
call s:load('tools')

View file

@ -2,57 +2,6 @@
## Unreleased
## 3.0.0 — 2026-05-21
### Breaking
- Default keymap style is now `space`: `SPC` is the command leader and `,`
is reserved for filetype-local actions. Set
`let g:chopsticks_keymap_style = 'classic'` in
`${XDG_CONFIG_HOME:-~/.config}/chopsticks.vim` to keep the old comma layout
- In the default Space layout, Normal-mode `s` is now the fastest
EasyMotion over-window two-character jump. Use `cl` for native `s`
substitute and `cc` for native `S` substitute
- Git push and pull hotkeys are removed from both Space and classic layouts;
use `:Git push` / `:Git pull` explicitly for irreversible remote operations
### Added
- Canonical QWERTY/CapsLock-friendly Space leader layout:
`SPC SPC`, `SPC /`, `SPC ,`, `SPC w`, `SPC qx`, `SPC rr`,
`SPC gs`, `SPC gl`, `SPC ca`, `SPC cr`, `SPC cf`, and `SPC ?`
- Native LSP motions in the default layout: `gd`, `gr`, `gI`, `gy`, `K`,
`[d`, and `]d`
- `:ChopsticksTutor` guided practice buffer for learning the final keymap
- `:ChopsticksCheatSheet` command, with `SPC ?` as the discoverable default
- Dedicated modules for buffers, utilities, files, runner, quickfix, status,
cheat sheet, and tutor
- Split test runner: `scripts/test.sh` now dispatches to
`scripts/test-quick.sh` and `scripts/test-vim.sh`
### Changed
- README, QUICKSTART, installer onboarding text, PR template, and demo tape now
teach the Space layout first while keeping the legacy classic layout documented
- `SPC ?` / `,?` cheat sheet output is generated from the active keymap style
and profile, so minimal installs no longer display disabled features
- Markdown actions now use localleader maps in the Space layout:
`,mp` preview and `,mt` table of contents
- `SPC U` opens UndoTree, `SPC z` toggles maximize, `SPC bp` / `SPC bn`
move between buffers, and quickfix/location-list actions live under `SPC x`
- `tools.vim` is now a compatibility placeholder; runtime behavior lives in
smaller focused modules
- CI/local smoke coverage now asserts Space defaults, classic compatibility,
missing push/pull hotkeys, tutor and cheat-sheet content, runner behavior,
large-file protection, and startup budget
### Fixed
- `diffopt` enhancements now degrade safely on macOS system Vim builds that
report the required patch level but reject individual diff options
- Installer system-tool reporting now still detects already-installed tools on
Linux hosts where sudo is unavailable in non-interactive mode
## 2.2.0 — 2026-05-17
### Added

View file

@ -18,14 +18,6 @@ without prompting. You can later put `let g:chopsticks_profile = 'minimal'` in
`${XDG_CONFIG_HOME:-~/.config}/chopsticks.vim` for a smaller core-only setup,
or use `full` for the heavier Markdown/LSP feedback.
The default keymap style is `space`: `SPC` is the command leader and `,` is
reserved for filetype-local actions. To use the legacy comma layout instead,
add this to `${XDG_CONFIG_HOME:-~/.config}/chopsticks.vim`:
```vim
let g:chopsticks_keymap_style = 'classic'
```
To switch later without reinstalling anything:
```bash
@ -42,16 +34,6 @@ cd ~/.vim && ./install.sh --configure-only --profile=full
## Survival
```
Esc back to Normal
SPC w save
SPC qx save + quit
:q! force quit
SPC ? cheat sheet (toggle sidebar)
```
Classic layout equivalents:
```
Esc back to Normal
,w save
@ -63,23 +45,23 @@ Esc back to Normal
## Find things
```
SPC SPC fuzzy find file (git-aware)
SPC / ripgrep project
SPC , search buffers
SPC fr recent files
SPC e file browser
SPC Tab last file
,ff fuzzy find file (git-aware)
,rg ripgrep project
,b search buffers
,fh recent files
,e file browser
,, last file
```
## Write code
```
gd go to definition
K hover docs
SPC cr rename symbol
SPC ca code action
SPC cf format
SPC rr run current file
,dd go to definition
,dk hover docs
,rn rename symbol
,ca code action
,f format
,cr run current file
Tab / S-Tab cycle completions
```
@ -88,37 +70,31 @@ Tab / S-Tab cycle completions
## Git
```
SPC gs status (s=stage, cc=commit)
SPC gd diff
SPC gb blame
SPC gl log graph
,gs status (s=stage, cc=commit)
,gd diff
,gb blame
,gp push
]x / [x conflict markers
```
## Edit
In the default Space layout, Normal-mode `s` is a fast visible-text jump.
Use `cl` when you want Vim's original single-character substitute behavior,
and `cc` when you want Vim's original line substitute behavior.
```
s + 2 chars EasyMotion jump
SPC S + 2 chars same jump, discoverable fallback
cl / cc native s / S substitute replacements
,S + 2 chars EasyMotion jump
gc toggle comment
cs"' change surrounding " to '
Alt+j / Alt+k move line
SPC U undo tree
SPC y clipboard yank
,u undo tree
,y clipboard yank
```
## Navigate
```
<C-w>h/j/k/l splits
SPC bp / SPC bn prev / next buffer
SPC z maximize window
SPC tt / SPC th terminal
,h / ,l prev / next buffer
,z maximize window
,tv / ,th terminal
```
## Markdown
@ -134,11 +110,10 @@ syntax. Enable the heavier Markdown tools only when you want them.
## Health check
```
:ChopsticksTutor guided practice for the final keymap
:ChopsticksStatus see what's installed and what's missing
```
The `SPC ?` cheat sheet follows your active profile, so `minimal` users only see
The `,?` cheat sheet follows your active profile, so `minimal` users only see
keys for features that are actually loaded.
See [README](README.md) for the full reference. See the [wiki](https://github.com/m1ngsama/chopsticks/wiki) for deep dives.

106
README.md
View file

@ -30,7 +30,7 @@ You SSH into a server. You need to edit code. You want LSP, fuzzy find, git inte
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.
**2425 plugins** (tmux-navigator loads only inside tmux), LSP, linting, and a hand-built statusline. No bloat, no decorations, just tools.
## What's in the box
@ -39,8 +39,8 @@ chopsticks gives you a production-ready Vim config in one command. Pure VimScrip
| **LSP** | completion, go-to-def, hover, rename, code actions — pure VimScript ([vim-lsp](https://github.com/prabirshrestha/vim-lsp)) |
| **Lint + format** | [ALE](https://github.com/dense-analysis/ale) runs black, prettier, goimports, rustfmt on save |
| **Fuzzy find** | files, buffers, grep, tags, marks, commands — [FZF](https://github.com/junegunn/fzf.vim) |
| **Git** | status, diff, blame, commit, log, conflict markers — [fugitive](https://github.com/tpope/vim-fugitive) + [gitgutter](https://github.com/airblade/vim-gitgutter) |
| **Run file** | `SPC rr` — auto-detects Python, Go, Rust, JS, C, Shell, and more |
| **Git** | status, diff, blame, push, pull, conflict markers — [fugitive](https://github.com/tpope/vim-fugitive) + [gitgutter](https://github.com/airblade/vim-gitgutter) |
| **Run file** | `,cr` — auto-detects Python, Go, Rust, JS, C, Shell, and more |
| **Markdown** | quiet writing defaults, browser preview (`,mp`), table of contents (`,mt`) |
| **Diagnostics** | `:ChopsticksStatus` — see what's installed, what's missing, how to fix it |
| **TTY-aware** | degrades gracefully on SSH, console, slow links — never breaks |
@ -81,8 +81,6 @@ profile or uses `engineer`.
let g:chopsticks_profile = 'minimal' " core navigation/editing/git/markdown
let g:chopsticks_profile = 'engineer' " default: LSP, ALE, syntax extras
let g:chopsticks_profile = 'full' " engineer + heavier Markdown feedback
let g:chopsticks_keymap_style = 'space' " default: Space leader grouped layout
let g:chopsticks_keymap_style = 'classic' " optional: legacy comma leader layout
let g:chopsticks_enable_jk_escape = 1 " optional: insert-mode jk exits insert
let g:chopsticks_enable_ctrl_s_save = 1 " optional: Ctrl-S saves
let g:chopsticks_enable_sudo_save_bang = 1 " optional: :w!! sudo save
@ -98,63 +96,44 @@ Startify, UndoTree, and browser Markdown preview. `full` keeps those and opts
into Markdown lint, format, spell, conceal, Marksman, and LSP virtual text.
Project updates leave `~/.config/chopsticks.vim` alone, so put local choices
there instead of editing the managed `.vimrc`. The `SPC ?` cheat sheet follows
the active profile and only shows keys for enabled features.
there instead of editing the managed `.vimrc`. The `,?` cheat sheet follows the
active profile and only shows keys for enabled features.
## Keys
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`).
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`.
For onboarding, use `:ChopsticksTutor` for a guided practice page, `SPC ?` for
the active keymap, and `:ChopsticksStatus` for tool/LSP health.
`QUICKSTART.md` is the 5-minute path; this README is the full reference.
Leader: `,`
```
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
SPC gs git status SPC cf format
SPC w save SPC qq quit
Esc exit insert mode SPC ? cheat sheet
,ff fuzzy find file ,dd go to definition
,rg ripgrep project ,dk hover docs
,e toggle file sidebar ,cr run current file
,gs git status ,f format
,w save ,q quit
Esc exit insert mode ,? cheat sheet
```
<details>
<summary><strong>Canonical Space keybindings</strong></summary>
### Fast Path
`SPC SPC` files | `SPC ,` buffers | `SPC /` grep | `SPC Tab` alternate buffer | `SPC e` browser | `SPC E` browser (file dir)
<summary><strong>All keybindings</strong></summary>
### Files
`SPC ff` files | `SPC fb` buffers | `SPC fg` git files | `SPC fr` recent | `SPC fl` buffer lines | `SPC fL` all lines | `SPC fv` edit vimrc | `SPC fV` reload vimrc
### Search
`SPC sg` grep | `SPC sw` grep word | `SPC s/` search history | `SPC s:` command history | `SPC sm` marks | `SPC st` tags | `SPC sr` replace word
`,ff` find | `,b` buffers | `,rg` grep | `,rG` grep word | `,fh` recent | `,fl` lines | `,e` browser | `,E` browser (file dir) | `,,` last file
### Code
`gd` def | `gr` refs | `gI` impl | `gy` type | `K` docs | `[d` `]d` LSP diagnostics | `[e` `]e` ALE errors | `SPC ca` action | `SPC cr` rename | `SPC cf` format | `SPC co` outline | `SPC ci` LSP status | `SPC rr` run
`,dd` def | `,dt` type | `,di` impl | `,dr` refs | `,dk` docs | `,dp` `,dn` diagnostics | `[e` `]e` ALE errors | `,rn` rename | `,ca` action | `,o` outline | `,cr` run
### Edit
`s`+2ch jump | `SPC S` jump fallback | `cl` native `s` substitute | `cc` native `S` substitute | `gc` comment | `cs"'` surround | `Alt+j/k` move line | `SPC U` undo tree | `SPC y` clipboard | `SPC =` re-indent visual | `SPC cW` strip whitespace | `[<Space>` `]<Space>` blank lines
`,S`+2ch jump | `gc` comment | `cs"'` surround | `Alt+j/k` move line | `,u` undo tree | `,y` clipboard | `,*` replace word | `,F` re-indent (v) | `,W` strip whitespace | `[<Space>` `]<Space>` blank lines
### Git
`SPC gs` status | `SPC gd` diff | `SPC gb` blame | `SPC gc` commit | `SPC gl` log graph | `SPC gC` FZF commits | `SPC gB` buffer commits | `]x` `[x` conflict
`,gs` status | `,gd` diff | `,gb` blame | `,gc` commit | `,gp` push | `,gl` pull | `,gL` log graph | `,gC` FZF commits | `,gB` buffer commits | `]x` `[x` conflict
### 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
`<C-w>hjkl` navigate | `,z` maximize | `,h` `,l` buffers | `,bd` close buffer | `,=` `,` resize | `,tv` `,th` terminal
### Markdown
@ -162,43 +141,6 @@ Esc exit insert mode SPC ? cheat sheet
### Toggle
`F2` paste | `F3` line numbers | `F4` relative numbers | `F6` invisible chars | `SPC us` spell check | `SPC uf` format on save
### Survival
`SPC w` save | `SPC W` save all | `SPC qq` quit | `SPC qx` save and quit | `SPC ?` cheat sheet | `:ChopsticksTutor` practice | `:ChopsticksStatus` diagnostics
</details>
<details>
<summary><strong>Legacy classic keybindings</strong></summary>
### Classic Files
`,ff` find | `,b` buffers | `,rg` grep | `,rG` grep word | `,fh` recent | `,fl` lines | `,e` browser | `,E` browser (file dir) | `,,` last file
### Classic Code
`,dd` def | `,dt` type | `,di` impl | `,dr` refs | `,dk` docs | `,dp` `,dn` diagnostics | `[e` `]e` ALE errors | `,rn` rename | `,ca` action | `,o` outline | `,cr` run
### Classic Edit
`,S`+2ch jump | `gc` comment | `cs"'` surround | `Alt+j/k` move line | `,u` undo tree | `,y` clipboard | `,*` replace word | `,F` re-indent (v) | `,W` strip whitespace | `[<Space>` `]<Space>` blank lines
### Classic Git
`,gs` status | `,gd` diff | `,gb` blame | `,gc` commit | `,gL` log graph | `,gC` FZF commits | `,gB` buffer commits | `]x` `[x` conflict
### Classic Windows
`<C-w>hjkl` navigate | `,z` maximize | `,h` `,l` buffers | `,bd` close buffer | `,=` `,` resize | `,tv` `,th` terminal
### Classic Markdown
`,mp` preview in browser | `,mt` table of contents
### Classic Toggle
`F2` paste | `F3` line numbers | `F4` relative numbers | `F6` invisible chars | `,ss` spell check | `,af` format on save
### Utilities
@ -252,7 +194,7 @@ For Markdown LSP, install or select `marksman` first.
├── .vimrc thin loader
├── modules/
│ ├── env.vim TTY detection, truecolor, skip built-in plugins
│ ├── plugins.vim vim-plug + 2325 plugins
│ ├── plugins.vim vim-plug + 2425 plugins
│ ├── core.vim settings, keymaps, performance
│ ├── ui.vim solarized, statusline, startify
│ ├── editing.vim easymotion, yank highlight, blank lines
@ -261,15 +203,7 @@ For Markdown LSP, install or select `marksman` first.
│ ├── lint.vim ale, format-on-save
│ ├── git.vim fugitive, gitgutter, conflict nav
│ ├── languages.vim vim-go, markdown, filetype settings
│ ├── buffers.vim buffer commands
│ ├── utilities.vim reindent, trim, clipboard, vimrc helpers
│ ├── files.vim auto mkdir, large-file protection
│ ├── runner.vim run current file
│ ├── quickfix.vim quickfix and location-list helpers
│ ├── status.vim :ChopsticksStatus diagnostics
│ ├── cheatsheet.vim SPC ? and :ChopsticksCheatSheet
│ ├── tutor.vim :ChopsticksTutor guided practice
│ └── tools.vim compatibility placeholder
│ └── tools.vim run file, quickfix, cheat sheet, diagnostics
```
Each module is self-contained. Comment out one line in `.vimrc` to disable it. Add your own with `call s:load('mine')`.

View file

@ -741,22 +741,22 @@ _I_NPM=-1; _I_PYTHON=-1; _I_GO=-1; _I_TMUX=-1
# Is any package manager available?
HAS_PKG_MGR=0
if [[ $HAS_BREW -eq 1 ]] || \
[[ $HAS_APT -eq 1 || $HAS_PACMAN -eq 1 || $HAS_DNF -eq 1 ]]; then
{ [[ $HAS_APT -eq 1 || $HAS_PACMAN -eq 1 || $HAS_DNF -eq 1 ]] && [[ $HAS_SUDO -eq 1 ]]; }; then
HAS_PKG_MGR=1
fi
# ── System tools ─────────────────────────────────────────────────────────────
if [[ $HAS_PKG_MGR -eq 1 ]]; then
_I_RIPGREP=$_idx
_ITEMS+=("ripgrep|SPC / project search · SPC sw word search · powers FZF preview|1")
_ITEMS+=("ripgrep|,rg / ,rG project-wide search · powers FZF preview|1")
: $(( _idx++ ))
_I_FZF=$_idx
_ITEMS+=("fzf|SPC SPC files · SPC , buffers · SPC st tag search|1")
_ITEMS+=("fzf|,ff fuzzy file search · ,b buffers · ,rt tag search|1")
: $(( _idx++ ))
_I_CTAGS=$_idx
_ITEMS+=("universal-ctags|Optional symbol index for SPC st tag jumps|0")
_ITEMS+=("universal-ctags|Optional symbol index for ,rt tag jumps|0")
: $(( _idx++ ))
if [[ $_PROFILE_TOOLING -eq 1 ]]; then
@ -848,11 +848,6 @@ _do_sys() {
if command -v "$check" >/dev/null 2>&1; then
ok "$name (already installed)"; return
fi
if [[ $OS != "macos" && $HAS_SUDO -ne 1 ]]; then
skip "$name — sudo not available, install manually"
SKIPPED+=("$name")
return
fi
if pkg_install "$brew_p" "$apt_p" "$pac_p" "$dnf_p"; then
ok "$name"; INSTALLED+=("$name")
else
@ -1206,9 +1201,9 @@ echo -e " ${CYAN}vim .${NC} Open dashboard in current directory"
echo -e " ${CYAN}vim myfile${NC} Edit a specific file"
echo ""
echo -e "${BOLD} First steps inside Vim${NC}"
echo -e " ${CYAN}SPC ?${NC} Open cheat sheet — your map of every keybinding"
echo -e " ${CYAN},?${NC} Open cheat sheet — your map of every keybinding"
echo -e " ${CYAN}Esc${NC} Exit insert mode → back to Normal"
echo -e " ${CYAN}SPC qx${NC} Save and quit"
echo -e " ${CYAN},x${NC} Save and quit"
echo -e " ${CYAN}:q!${NC} + Enter Emergency quit without saving"
if [[ $CONFIG_PROFILE != "minimal" ]]; then
echo -e " ${CYAN}:LspInstallServer${NC} Install LSP for current filetype"

View file

@ -1,18 +0,0 @@
" buffers.vim — buffer commands
command! Bclose call <SID>BufcloseCloseIt()
function! <SID>BufcloseCloseIt()
let l:currentBufNum = bufnr("%")
let l:alternateBufNum = bufnr("#")
if buflisted(l:alternateBufNum)
buffer #
else
bnext
endif
if bufnr("%") == l:currentBufNum
new
endif
if buflisted(l:currentBufNum)
execute("bdelete! " . l:currentBufNum)
endif
endfunction

View file

@ -1,267 +0,0 @@
" cheatsheet.vim — active keymap reference
function! s:OpenCheatSheet(lines) abort
let l:name = '__ChopsticksCheatSheet__'
if bufwinnr(l:name) > 0
execute bufwinnr(l:name) . 'wincmd w | bd'
return
endif
execute 'vertical botright new ' . l:name
vertical resize 42
setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile
setlocal nowrap nonumber norelativenumber signcolumn=no
setlocal winfixwidth
call setline(1, a:lines)
setlocal nomodifiable readonly
nnoremap <buffer> <silent> q :bd<CR>
nnoremap <buffer> <silent> <leader>? :bd<CR>
endfunction
function! s:CheatSheet() abort
let l:has_lsp = get(g:, 'chopsticks_enable_lsp', 1)
let l:has_lint = get(g:, 'chopsticks_enable_lint', 1)
let l:has_undotree = exists('g:plugs["undotree"]')
let l:has_previm = exists('g:plugs["previm"]')
if g:chopsticks_space_keymaps
let l:lines = [
\ ' chopsticks <Space>? close',
\ ' ─────────────────────────────────',
\ '',
\ ' ── fast path ─────────────',
\ ' SPC SPC files',
\ ' SPC , buffers',
\ ' SPC / grep project',
\ ' SPC Tab last file',
\ ' SPC e sidebar (cwd)',
\ ' SPC E sidebar (file dir)',
\ '',
\ ' ── files/find ────────────',
\ ' SPC ff files',
\ ' SPC fb buffers',
\ ' SPC fg git files',
\ ' SPC fr recent files',
\ ' SPC fl lines in buffer',
\ ' SPC sc commands',
\ ' SPC sm marks',
\ ' SPC s/ search history',
\ ' SPC s: command history',
\ ' SPC sg grep project',
\ ' SPC sw grep word',
\ ' SPC st tags',
\ '',
\ ' ── code ──────────────────',
\ ]
if l:has_lsp
call extend(l:lines, [
\ ' gd definition',
\ ' gr references',
\ ' gI implementation',
\ ' gy type definition',
\ ' K hover docs',
\ ' [d ]d LSP diagnostics',
\ ' SPC ca code action',
\ ' SPC cr rename',
\ ' SPC cf format',
\ ' SPC ci LSP status',
\ ' SPC co outline',
\ ' SPC cS workspace symbols',
\ ' :LspInstallServer setup LSP',
\ ' :ChopsticksStatus check LSP setup',
\ ])
endif
call extend(l:lines, [
\ ' SPC rr run file',
\ ' SPC cW strip trailing (v)',
\ ' SPC c= re-indent file (opt-in)',
\ ' SPC = re-indent (v)',
\ ])
if l:has_previm
call add(l:lines, ' ,mp markdown preview')
endif
call add(l:lines, ' ,mt table of contents')
if l:has_lint
call extend(l:lines, [
\ ' [e ]e ALE errors',
\ ' SPC xd ALE detail',
\ ' SPC uf format on save',
\ ])
endif
call extend(l:lines, [
\ '',
\ ' ── edit ──────────────────',
\ ' s+2ch easymotion jump',
\ ' gc comment',
\ ' cl / cc native s / S substitute',
\ ' SPC S+2ch jump fallback',
\ ' cs"'' surround',
\ ])
if l:has_undotree
call add(l:lines, ' SPC U undo tree')
endif
call extend(l:lines, [
\ ' SPC y/p clipboard y/p (v)',
\ ' Alt+j/k move line (v)',
\ ' SPC sr replace word (v)',
\ '',
\ ' ── git ───────────────────',
\ ' SPC gs status',
\ ' SPC gd diff',
\ ' SPC gb blame',
\ ' SPC gc commit',
\ ' SPC gl log graph',
\ ' SPC gC FZF commits',
\ ' SPC gB FZF buffer commits',
\ ' [x ]x conflict markers',
\ '',
\ ' ── windows ───────────────',
\ ' <C-w>hjkl navigate splits',
\ ' SPC bp/bn prev / next buf',
\ ' SPC bd close buffer',
\ ' SPC bo close other buffers',
\ ' SPC z maximize toggle',
\ ' SPC tt/th terminal / split',
\ ' ]q [q next / prev qf',
\ ' SPC xq/xQ open / close qf',
\ ' SPC xl/xL open / close loclist',
\ '',
\ ' ── toggle ────────────────',
\ ' F2 paste mode',
\ ' F3 line numbers',
\ ' F4 relative numbers',
\ ' F6 invisible chars',
\ ' SPC us spell check',
\ '',
\ ' ── survival ──────────────',
\ ' SPC w save',
\ ' SPC W save all',
\ ' SPC qq quit',
\ ' SPC qx save + quit',
\ ' Esc exit insert',
\ ' SPC fv edit vimrc',
\ ' SPC fV reload vimrc',
\ ' :ChopsticksTutor practice',
\ ' :ChopsticksStatus health',
\ ])
call s:OpenCheatSheet(l:lines)
return
endif
let l:lines = [
\ ' chopsticks ,? close',
\ ' ─────────────────────────────',
\ '',
\ ' ── files ──────────────────',
\ ' ,ff files',
\ ' ,b buffers',
\ ' ,rg grep project',
\ ' ,rG grep word',
\ ' ,e sidebar (cwd)',
\ ' ,E sidebar (file dir)',
\ ' ,, last file',
\ ' ,fh recent files',
\ ' ,fl lines in buffer',
\ ' ,fc commands',
\ ' ,fm marks',
\ '',
\ ' ── code ──────────────────',
\ ]
if l:has_lsp
call extend(l:lines, [
\ ' ,dd definition',
\ ' ,dt type definition',
\ ' ,di implementation',
\ ' ,dr references',
\ ' ,dk hover docs',
\ ' ,rn rename',
\ ' ,ca code action',
\ ' ,f format',
\ ' ,o outline',
\ ' ,dp ,dn LSP diagnostics',
\ ' :LspInstallServer setup LSP',
\ ' :ChopsticksStatus check LSP setup',
\ ])
endif
call add(l:lines, ' ,cr run file')
if l:has_previm
call add(l:lines, ' ,mp markdown preview')
endif
call add(l:lines, ' ,mt table of contents')
if l:has_lint
call extend(l:lines, [
\ ' [e ]e ALE errors',
\ ' ,af format on save',
\ ])
endif
call extend(l:lines, [
\ '',
\ ' ── edit ──────────────────',
\ ' gc comment',
\ ' ,S+2ch easymotion jump',
\ ' cs"'' surround',
\ ])
if l:has_undotree
call add(l:lines, ' ,u undo tree')
endif
call extend(l:lines, [
\ ' ,y ,p clipboard y/p (v)',
\ ' Alt+j/k move line (v)',
\ ' ,* replace word (v)',
\ ' ,F re-indent (v)',
\ ' ,W strip trailing (v)',
\ '',
\ ' ── git ───────────────────',
\ ' ,gs status',
\ ' ,gd diff',
\ ' ,gb blame',
\ ' ,gc commit',
\ ' ,gL log graph',
\ ' ,gC FZF commits',
\ ' [x ]x conflict markers',
\ '',
\ ' ── windows ───────────────',
\ ' <C-w>hjkl navigate splits',
\ ' ,h ,l prev / next buf',
\ ' ,bd close buffer',
\ ' ,z maximize toggle',
\ ' ,= ,- resize height',
\ ' ,tv ,th terminal v / h',
\ ' ]q [q next / prev qf',
\ ' ,qo ,qc open / close qf',
\ '',
\ ' ── toggle ────────────────',
\ ' F2 paste mode',
\ ' F3 line numbers',
\ ' F4 relative numbers',
\ ' F6 invisible chars',
\ ' ,ss spell check',
\ '',
\ ' ── survival ──────────────',
\ ' ,w save',
\ ' ,q quit',
\ ' ,x save + quit',
\ ' Esc exit insert',
\ ' ,ev edit vimrc',
\ ' ,sv reload vimrc',
\ ' :ChopsticksTutor practice',
\ ' :ChopsticksStatus health',
\ ])
call s:OpenCheatSheet(l:lines)
endfunction
command! ChopsticksCheatSheet call s:CheatSheet()
nnoremap <silent> <leader>? :ChopsticksCheatSheet<CR>

View file

@ -94,44 +94,22 @@ set smartindent
" ── Leader ──────────────────────────────────────────────────────────────────
if g:chopsticks_space_keymaps
let mapleader = "\<Space>"
let maplocalleader = ","
else
let mapleader = ","
endif
let mapleader = ","
" ── Basic Keymaps ───────────────────────────────────────────────────────────
if g:chopsticks_space_keymaps
nnoremap <leader>w :w<cr>
nnoremap <leader>W :wa<cr>
nnoremap <leader>qq :q<cr>
nnoremap <leader>qx :x<cr>
nnoremap <leader>w :w<cr>
nnoremap <leader>q :q<cr>
nnoremap <leader>x :x<cr>
nnoremap <silent> <leader>uh :noh<cr>
nnoremap <silent> <leader><cr> :noh<cr>
nnoremap <leader>bd :Bclose<cr>
nnoremap <leader>ba :bufdo bd<cr>
nnoremap <leader>bo :%bd<bar>e#<bar>bd#<cr>
nnoremap <leader>bn :bnext<cr>
nnoremap <leader>bp :bprevious<cr>
nnoremap <leader>bd :Bclose<cr>
nnoremap <leader>ba :bufdo bd<cr>
nnoremap <leader>l :bnext<cr>
nnoremap <leader>h :bprevious<cr>
nnoremap <leader>fd :lcd %:p:h<cr>:pwd<cr>
else
nnoremap <leader>w :w<cr>
nnoremap <leader>q :q<cr>
nnoremap <leader>x :x<cr>
nnoremap <silent> <leader><cr> :noh<cr>
nnoremap <leader>bd :Bclose<cr>
nnoremap <leader>ba :bufdo bd<cr>
nnoremap <leader>l :bnext<cr>
nnoremap <leader>h :bprevious<cr>
nnoremap <leader>cd :lcd %:p:h<cr>:pwd<cr>
endif
nnoremap <leader>cd :lcd %:p:h<cr>:pwd<cr>
nnoremap <leader>v `[v`]
@ -140,11 +118,7 @@ nnoremap <M-k> :m .-2<CR>==
vnoremap <M-j> :m '>+1<CR>gv=gv
vnoremap <M-k> :m '<-2<CR>gv=gv
if g:chopsticks_space_keymaps
nnoremap <silent> <leader>us :setlocal spell!<CR>:echo 'Spell: ' . (&spell ? 'ON' : 'OFF')<CR>
else
nnoremap <silent> <leader>ss :setlocal spell!<CR>:echo 'Spell: ' . (&spell ? 'ON' : 'OFF')<CR>
endif
nnoremap <silent> <leader>ss :setlocal spell!<CR>:echo 'Spell: ' . (&spell ? 'ON' : 'OFF')<CR>
nnoremap <silent> <F2> :set paste!<CR>:echo 'Paste: ' . (&paste ? 'ON' : 'OFF')<CR>
nnoremap <silent> <F3> :set invnumber<CR>:echo 'Line numbers: ' . (&number ? 'ON' : 'OFF')<CR>
@ -183,15 +157,8 @@ if has('clipboard')
vnoremap <leader>P "+P
endif
if g:chopsticks_space_keymaps
nnoremap <leader>xq :copen<CR>
nnoremap <leader>xQ :cclose<CR>
nnoremap <leader>xl :lopen<CR>
nnoremap <leader>xL :lclose<CR>
else
nnoremap <leader>qo :copen<CR>
nnoremap <leader>qc :cclose<CR>
endif
nnoremap <leader>qo :copen<CR>
nnoremap <leader>qc :cclose<CR>
augroup ChopstickResize
autocmd!
@ -226,10 +193,7 @@ endif
set sessionoptions=blank,buffers,curdir,folds,help,tabpages,winsize,winpos,terminal
if has("patch-8.1.0360")
silent! set diffopt+=internal
silent! set diffopt+=context:3
silent! set diffopt+=algorithm:histogram
silent! set diffopt+=indent-heuristic
set diffopt=filler,internal,context:3,algorithm:histogram,indent-heuristic
endif
" ── Format Options ──────────────────────────────────────────────────────────

View file

@ -6,27 +6,16 @@ let g:EasyMotion_do_mapping = 0
let g:EasyMotion_smartcase = 1
if exists('g:plugs["vim-easymotion"]')
if g:chopsticks_space_keymaps
" In the canonical layout, cl/cc cover native s/S substitute behavior;
" s becomes the fastest screen-local jump entry.
nmap s <Plug>(easymotion-overwin-f2)
nmap <Leader>S <Plug>(easymotion-overwin-f2)
else
nmap <Leader>S <Plug>(easymotion-overwin-f2)
nmap <Leader>j <Plug>(easymotion-j)
nmap <Leader>k <Plug>(easymotion-k)
endif
endif
" ── UndoTree ────────────────────────────────────────────────────────────────
if exists('g:plugs["undotree"]')
nnoremap <F5> :UndotreeToggle<CR>
if g:chopsticks_space_keymaps
nnoremap <leader>U :UndotreeToggle<CR>
else
nnoremap <leader>u :UndotreeToggle<CR>
endif
endif
" ── Yank Highlight ──────────────────────────────────────────────────────────

View file

@ -11,12 +11,6 @@ if index(['minimal', 'engineer', 'full'], g:chopsticks_profile) < 0
let g:chopsticks_profile = 'engineer'
endif
let g:chopsticks_keymap_style = get(g:, 'chopsticks_keymap_style', 'space')
if index(['classic', 'space'], g:chopsticks_keymap_style) < 0
let g:chopsticks_keymap_style = 'space'
endif
let g:chopsticks_space_keymaps = g:chopsticks_keymap_style ==# 'space'
let s:profile_full = g:chopsticks_profile ==# 'full'
let s:profile_minimal = g:chopsticks_profile ==# 'minimal'

View file

@ -1,54 +0,0 @@
" files.vim — file safety and large-file handling
function! s:MkNonExDir(file, buf)
if empty(getbufvar(a:buf, '&buftype')) && a:file !~# '\v^\w+\:\/'
let dir = fnamemodify(a:file, ':h')
if !isdirectory(dir)
call mkdir(dir, 'p')
endif
endif
endfunction
augroup BWCCreateDir
autocmd!
autocmd BufWritePre *
\ if !empty(expand('<afile>')) |
\ call s:MkNonExDir(expand('<afile>'), +expand('<abuf>')) |
\ endif
augroup END
let g:LargeFile = get(g:, 'LargeFile', 1024 * 1024 * 10)
let s:tty_large = g:is_tty ? 512000 : g:LargeFile
function! s:ApplyLargeFileSettings() abort
if get(b:, 'chopsticks_large_file', 0)
setlocal bufhidden=unload undolevels=-1 noswapfile
let b:ale_enabled = 0
if &l:syntax !=# ''
setlocal syntax=
endif
elseif get(b:, 'chopsticks_tty_large_file', 0)
if &l:syntax !=# ''
setlocal syntax=
endif
endif
endfunction
function! s:MarkLargeFile(file) abort
if empty(a:file)
return
endif
let l:fsize = getfsize(a:file)
if l:fsize > g:LargeFile || l:fsize == -2
let b:chopsticks_large_file = 1
elseif g:is_tty && l:fsize > s:tty_large
let b:chopsticks_tty_large_file = 1
endif
call s:ApplyLargeFileSettings()
endfunction
augroup ChopstickLargeFile
autocmd!
autocmd BufReadPre * call s:MarkLargeFile(expand('<afile>'))
autocmd BufReadPost,FileType,Syntax * call s:ApplyLargeFileSettings()
augroup END

View file

@ -14,13 +14,11 @@ let g:gitgutter_sign_modified_removed = '~'
if exists('g:plugs["vim-fugitive"]')
nnoremap <leader>gs :Git status<CR>
nnoremap <leader>gc :Git commit<CR>
nnoremap <leader>gp :Git push<CR>
nnoremap <leader>gl :Git pull<CR>
nnoremap <leader>gd :Gdiffsplit<CR>
nnoremap <leader>gb :Git blame<CR>
if g:chopsticks_space_keymaps
nnoremap <leader>gl :Git log --oneline --graph -20<CR>
else
nnoremap <leader>gL :Git log --oneline --graph -20<CR>
endif
endif
" ── Conflict Navigation ────────────────────────────────────────────────────

View file

@ -14,16 +14,7 @@ let g:vim_markdown_follow_anchor = 1
let g:vim_markdown_new_list_item_indent = 2
let g:vim_markdown_strikethrough = 1
function! s:MarkdownKeymaps() abort
if exists('g:plugs["vim-markdown"]')
nnoremap <buffer> <localleader>mt :Toc<CR>
endif
if exists('g:plugs["previm"]')
nnoremap <buffer> <localleader>mp :PrevimOpen<CR>
endif
endfunction
if exists('g:plugs["vim-markdown"]') && !g:chopsticks_space_keymaps
if exists('g:plugs["vim-markdown"]')
nnoremap <leader>mt :Toc<CR>
endif
@ -33,7 +24,7 @@ elseif executable('xdg-open')
let g:previm_open_cmd = 'xdg-open'
endif
let g:previm_enable_realtime = get(g:, 'previm_enable_realtime', 0)
if exists('g:plugs["previm"]') && !g:chopsticks_space_keymaps
if exists('g:plugs["previm"]')
nnoremap <leader>mp :PrevimOpen<CR>
endif
@ -87,7 +78,6 @@ augroup ChopstickFiletype
autocmd FileType yaml
\ setlocal expandtab shiftwidth=2 tabstop=2
autocmd FileType markdown call s:MarkdownDefaults()
autocmd FileType markdown if g:chopsticks_space_keymaps | call s:MarkdownKeymaps() | endif
autocmd FileType sh
\ setlocal expandtab shiftwidth=2 tabstop=2 textwidth=80
autocmd FileType make

View file

@ -64,13 +64,7 @@ let g:ale_virtualtext_cursor = get(g:, 'ale_virtualtext_cursor', 'disabled')
if exists('g:plugs["ale"]')
nnoremap <silent> [e :ALEPrevious<cr>
nnoremap <silent> ]e :ALENext<cr>
if g:chopsticks_space_keymaps
nnoremap <silent> <leader>xd :ALEDetail<cr>
nnoremap <silent> <leader>uf :let g:ale_fix_on_save = !g:ale_fix_on_save
\ <bar> echo 'Format on save: ' . (g:ale_fix_on_save ? 'ON' : 'OFF')<cr>
else
nnoremap <silent> <leader>aD :ALEDetail<cr>
nnoremap <silent> <leader>af :let g:ale_fix_on_save = !g:ale_fix_on_save
\ <bar> echo 'Format on save: ' . (g:ale_fix_on_save ? 'ON' : 'OFF')<cr>
endif
endif

View file

@ -65,24 +65,6 @@ function! s:on_lsp_buffer_enabled() abort
setlocal signcolumn=yes
endif
if g:chopsticks_space_keymaps
nmap <buffer> gd <plug>(lsp-definition)
nmap <buffer> gr <plug>(lsp-references)
nmap <buffer> gI <plug>(lsp-implementation)
nmap <buffer> gy <plug>(lsp-type-definition)
nmap <buffer> K <plug>(lsp-hover)
nmap <buffer> [d <plug>(lsp-previous-diagnostic)
nmap <buffer> ]d <plug>(lsp-next-diagnostic)
nmap <buffer> <leader>ca <plug>(lsp-code-action)
nmap <buffer> <leader>cr <plug>(lsp-rename)
nmap <buffer> <leader>cf <plug>(lsp-document-format)
xmap <buffer> <leader>cf <plug>(lsp-document-range-format)
nnoremap <buffer> <leader>ci :LspStatus<CR>
nmap <buffer> <leader>co <plug>(lsp-document-symbol-search)
nmap <buffer> <leader>cS <plug>(lsp-workspace-symbol-search)
else
nmap <buffer> <leader>dd <plug>(lsp-definition)
nmap <buffer> <leader>dt <plug>(lsp-type-definition)
nmap <buffer> <leader>di <plug>(lsp-implementation)
@ -100,7 +82,6 @@ function! s:on_lsp_buffer_enabled() abort
nmap <buffer> <leader>o <plug>(lsp-document-symbol-search)
nmap <buffer> <leader>ws <plug>(lsp-workspace-symbol-search)
nmap <buffer> <leader>cD <plug>(lsp-document-diagnostics)
endif
endfunction
augroup lsp_install

View file

@ -47,26 +47,6 @@ function! s:SmartFiles() abort
endfunction
if exists('g:plugs["fzf.vim"]')
if g:chopsticks_space_keymaps
nnoremap <leader><Space> :call <SID>SmartFiles()<CR>
nnoremap <leader>, :Buffers<CR>
nnoremap <leader>/ :Rg<CR>
nnoremap <leader>ff :call <SID>SmartFiles()<CR>
nnoremap <leader>fb :Buffers<CR>
nnoremap <leader>fg :GFiles<CR>
nnoremap <leader>fr :History<CR>
nnoremap <leader>fl :BLines<CR>
nnoremap <leader>fL :Lines<CR>
nnoremap <leader>s/ :History/<CR>
nnoremap <leader>s: :History:<CR>
nnoremap <leader>sc :Commands<CR>
nnoremap <leader>sm :Marks<CR>
nnoremap <leader>sg :Rg<CR>
nnoremap <leader>sw :RgWord<CR>
nnoremap <leader>st :Tags<CR>
nnoremap <leader>gC :Commits<CR>
nnoremap <leader>gB :BCommits<CR>
else
nnoremap <leader>ff :call <SID>SmartFiles()<CR>
nnoremap <leader>b :Buffers<CR>
nnoremap <leader>rg :Rg<CR>
@ -82,7 +62,6 @@ if exists('g:plugs["fzf.vim"]')
nnoremap <leader>f: :History:<CR>
nnoremap <leader>gC :Commits<CR>
nnoremap <leader>gB :BCommits<CR>
endif
endif
let g:fzf_layout = { 'down': '40%' }
@ -120,22 +99,13 @@ function! s:ToggleMaximize() abort
echo 'Window: MAXIMIZED'
endif
endfunction
if g:chopsticks_space_keymaps
nnoremap <silent> <leader>z :call <SID>ToggleMaximize()<CR>
else
nnoremap <silent> <leader>z :call <SID>ToggleMaximize()<CR>
endif
nnoremap <silent> <leader>z :call <SID>ToggleMaximize()<CR>
" ── Terminal ────────────────────────────────────────────────────────────────
if has('terminal')
if g:chopsticks_space_keymaps
nnoremap <leader>tt :terminal<CR>
nnoremap <leader>th :terminal ++rows=10<CR>
else
nnoremap <leader>tv :terminal<CR>
nnoremap <leader>th :terminal ++rows=10<CR>
endif
if g:chopsticks_enable_terminal_keymaps
tnoremap <Esc><Esc> <C-\><C-n>
tnoremap <C-h> <C-\><C-n><C-w>h

View file

@ -1,10 +0,0 @@
" quickfix.vim — quickfix and location-list helpers
augroup ChopstickQF
autocmd!
autocmd QuickFixCmdPost [^l]* cwindow
autocmd QuickFixCmdPost l* lwindow
augroup END
nnoremap <silent> ]q :cnext<CR>
nnoremap <silent> [q :cprev<CR>

View file

@ -1,28 +0,0 @@
" runner.vim — run the current file by filetype
function! s:RunFile() abort
write
let l:ft = &filetype
let l:file = shellescape(expand('%:p'))
if l:ft ==# 'python' | execute '!python3 ' . l:file
elseif l:ft ==# 'javascript' | execute '!node ' . l:file
elseif l:ft ==# 'typescript' | execute '!npx ts-node ' . l:file
elseif l:ft ==# 'go' | execute '!go run ' . l:file
elseif l:ft ==# 'rust' | execute '!cargo run'
elseif l:ft ==# 'sh' | execute '!bash ' . l:file
elseif l:ft ==# 'c'
let l:out_path = tempname()
let l:out = shellescape(l:out_path)
execute '!gcc -o ' . l:out . ' ' . l:file . ' && ' . l:out
call delete(l:out_path)
elseif l:ft ==# 'lua' | execute '!lua ' . l:file
elseif l:ft ==# 'ruby' | execute '!ruby ' . l:file
elseif l:ft ==# 'perl' | execute '!perl ' . l:file
else | echo 'No runner for filetype: ' . l:ft
endif
endfunction
if g:chopsticks_space_keymaps
nnoremap <leader>rr :call <SID>RunFile()<CR>
else
nnoremap <leader>cr :call <SID>RunFile()<CR>
endif

View file

@ -1,165 +0,0 @@
" status.vim — health diagnostics
function! s:Check(name, cmd) abort
return executable(a:cmd) ? ' OK ' . a:name : ' -- ' . a:name . ' (missing: ' . a:cmd . ')'
endfunction
function! s:Off(name, reason) abort
return ' off ' . a:name . ' (' . a:reason . ')'
endfunction
function! s:PlugDir(name) abort
if !exists('g:plugs') || !has_key(g:plugs, a:name)
return ''
endif
return fnamemodify(get(g:plugs[a:name], 'dir', ''), ':p')
endfunction
function! s:PlugInstalled(name) abort
let l:dir = s:PlugDir(a:name)
return !empty(l:dir) && isdirectory(l:dir)
endfunction
function! s:LspStackIssue() abort
if !get(g:, 'chopsticks_enable_lsp', 1)
return 'LSP disabled by profile'
endif
if empty(s:PlugDir('vim-lsp'))
return 'vim-lsp not declared by this profile'
endif
if !s:PlugInstalled('vim-lsp')
return 'vim-lsp not installed; run :PlugInstall'
endif
if empty(s:PlugDir('vim-lsp-settings'))
return 'vim-lsp-settings not declared by this profile'
endif
if !s:PlugInstalled('vim-lsp-settings')
return 'vim-lsp-settings not installed; run :PlugInstall'
endif
return ''
endfunction
function! s:LspStackCheck() abort
let l:issue = s:LspStackIssue()
if l:issue ==# 'LSP disabled by profile'
return s:Off('vim-lsp stack', l:issue)
endif
if !empty(l:issue)
return ' -- vim-lsp stack (' . l:issue . ')'
endif
if exists(':LspStatus') == 2 || exists(':LspInstallServer') == 2
return ' OK vim-lsp stack (installed)'
endif
return ' OK vim-lsp stack (installed; not loaded yet)'
endfunction
function! s:LspCheck(ft, server) abort
let l:issue = s:LspStackIssue()
if l:issue ==# 'LSP disabled by profile'
return s:Off(a:ft, l:issue)
endif
if !empty(l:issue)
return ' -- ' . a:ft . ' (' . l:issue . ')'
endif
let l:dir = expand('~/.local/share/vim-lsp-settings/servers/' . a:server)
if isdirectory(l:dir)
return ' OK ' . a:ft . ' (' . a:server . ')'
endif
return ' -- ' . a:ft . ' (:LspInstallServer in a ' . a:ft . ' file)'
endfunction
function! s:ChopsticksStatus() abort
let l:lines = []
call add(l:lines, 'chopsticks status')
call add(l:lines, repeat('─', 50))
call add(l:lines, '')
call add(l:lines, '── system tools ──')
call add(l:lines, s:Check('fzf', 'fzf'))
call add(l:lines, s:Check('ripgrep', 'rg'))
call add(l:lines, s:Check('git', 'git'))
call add(l:lines, s:Check('curl', 'curl'))
call add(l:lines, s:Check('node', 'node'))
call add(l:lines, s:Check('python3', 'python3'))
call add(l:lines, s:Check('go', 'go'))
call add(l:lines, '')
call add(l:lines, '── lsp servers ── (:LspInstallServer to install)')
call add(l:lines, s:LspStackCheck())
if get(g:, 'chopsticks_enable_lsp', 1)
call add(l:lines, ' LSP actions are buffer-local and start after a server attaches.')
call add(l:lines, ' Missing one? Open that filetype and run :LspInstallServer once.')
endif
call add(l:lines, s:LspCheck('python', 'pylsp'))
call add(l:lines, s:LspCheck('go', 'gopls'))
call add(l:lines, s:LspCheck('rust', 'rust-analyzer'))
call add(l:lines, s:LspCheck('typescript', 'typescript-language-server'))
call add(l:lines, s:LspCheck('c/c++', 'clangd'))
call add(l:lines, s:LspCheck('bash', 'bash-language-server'))
call add(l:lines, s:LspCheck('html', 'vscode-html-language-server'))
call add(l:lines, s:LspCheck('json', 'vscode-json-language-server'))
call add(l:lines, s:LspCheck('yaml', 'yaml-language-server'))
call add(l:lines, s:LspCheck('markdown', 'marksman'))
call add(l:lines, s:LspCheck('sql', 'sqls'))
call add(l:lines, '')
call add(l:lines, '── linters ──')
if get(g:, 'chopsticks_enable_lint', 1)
call add(l:lines, s:Check('flake8 (python)', 'flake8'))
call add(l:lines, s:Check('pylint (python)', 'pylint'))
call add(l:lines, s:Check('eslint (js/ts)', 'eslint'))
call add(l:lines, s:Check('staticcheck (go)', 'staticcheck'))
call add(l:lines, s:Check('shellcheck (sh)', 'shellcheck'))
call add(l:lines, s:Check('yamllint (yaml)', 'yamllint'))
call add(l:lines, s:Check('hadolint (docker)', 'hadolint'))
if get(g:, 'chopsticks_markdown_lint', 0)
call add(l:lines, s:Check('markdownlint (md)', 'markdownlint'))
else
call add(l:lines, s:Off('markdownlint (md)', 'disabled by default'))
endif
else
call add(l:lines, s:Off('ALE linters', 'lint disabled by profile'))
endif
call add(l:lines, '')
call add(l:lines, '── formatters ── (format-on-save is ' . (get(g:, 'ale_fix_on_save', 0) ? 'ON' : 'OFF') . ')')
if get(g:, 'chopsticks_enable_lint', 1)
call add(l:lines, s:Check('black (python)', 'black'))
call add(l:lines, s:Check('isort (python)', 'isort'))
call add(l:lines, s:Check('prettier (js/ts/json)', 'prettier'))
if get(g:, 'chopsticks_markdown_format_on_save', 0)
call add(l:lines, s:Check('prettier (md)', 'prettier'))
else
call add(l:lines, s:Off('prettier (md)', 'disabled by default'))
endif
call add(l:lines, s:Check('goimports (go)', 'goimports'))
call add(l:lines, s:Check('rustfmt (rust)', 'rustfmt'))
call add(l:lines, s:Check('clang-format (c)', 'clang-format'))
else
call add(l:lines, s:Off('ALE formatters', 'lint disabled by profile'))
endif
call add(l:lines, '')
let l:ok = len(filter(copy(l:lines), 'v:val =~# " OK "'))
let l:miss = len(filter(copy(l:lines), 'v:val =~# " -- "'))
call add(l:lines, repeat('─', 50))
call add(l:lines, ' ' . l:ok . ' ready, ' . l:miss . ' missing')
call add(l:lines, '')
call add(l:lines, ' Install missing tools with ./install.sh')
if get(g:, 'chopsticks_enable_lsp', 1)
call add(l:lines, ' Install LSP servers with :LspInstallServer')
endif
let l:name = '__ChopsticksStatus__'
if bufwinnr(l:name) > 0
execute bufwinnr(l:name) . 'wincmd w | bd'
endif
execute 'botright new ' . l:name
resize 45
setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile
setlocal nowrap nonumber norelativenumber signcolumn=no
call setline(1, l:lines)
setlocal nomodifiable readonly
nnoremap <buffer> <silent> q :bd<CR>
endfunction
command! ChopsticksStatus call s:ChopsticksStatus()

View file

@ -1,4 +1,445 @@
" tools.vim — compatibility placeholder
"
" Tooling was split into granular modules:
" buffers, utilities, files, runner, quickfix, status, cheatsheet, and tutor.
" tools.vim — run file, sudo save, quickfix, helpers
" ── Buffer Close ───────────────────────────────────────────────────────────
command! Bclose call <SID>BufcloseCloseIt()
function! <SID>BufcloseCloseIt()
let l:currentBufNum = bufnr("%")
let l:alternateBufNum = bufnr("#")
if buflisted(l:alternateBufNum)
buffer #
else
bnext
endif
if bufnr("%") == l:currentBufNum
new
endif
if buflisted(l:currentBufNum)
execute("bdelete! " . l:currentBufNum)
endif
endfunction
" ── Utilities ──────────────────────────────────────────────────────────────
if get(g:, 'chopsticks_enable_reindent_file', 0)
nnoremap <leader>F gg=G``
endif
vnoremap <leader>F =
nnoremap <leader>wa :wa<CR>
nnoremap <silent> <Leader>= :exe "resize " . (winheight(0) * 3/2)<CR>
nnoremap <silent> <Leader>- :exe "resize " . (winheight(0) * 2/3)<CR>
nnoremap <leader><leader> <c-^>
nnoremap <leader>W :%s/\s\+$//<CR>:let @/=''<CR>
vnoremap <leader>W :s/\s\+$//<CR>:let @/=''<CR>gv
nnoremap <leader>ev :edit $MYVIMRC<CR>
nnoremap <leader>sv :unlet! g:chopsticks_loaded<CR>:execute 'source ' . fnameescape($MYVIMRC)<CR>:echo "vimrc reloaded"<CR>
nnoremap <leader>* :%s/\<<C-r><C-w>\>//g<Left><Left>
vnoremap <leader>* :s///g<Left><Left><Left>
if has('clipboard')
nnoremap <leader>cp :let @+ = expand("%:p")<CR>:echo "Copied: " . expand("%:p")<CR>
nnoremap <leader>cf :let @+ = expand("%:t")<CR>:echo "Copied: " . expand("%:t")<CR>
endif
" ── Auto-Create Directories ─────────────────────────────────────────────────
function! s:MkNonExDir(file, buf)
if empty(getbufvar(a:buf, '&buftype')) && a:file !~# '\v^\w+\:\/'
let dir = fnamemodify(a:file, ':h')
if !isdirectory(dir)
call mkdir(dir, 'p')
endif
endif
endfunction
augroup BWCCreateDir
autocmd!
autocmd BufWritePre *
\ if !empty(expand('<afile>')) |
\ call s:MkNonExDir(expand('<afile>'), +expand('<abuf>')) |
\ endif
augroup END
" ── Large File Handling ──────────────────────────────────────────────────────
let g:LargeFile = get(g:, 'LargeFile', 1024 * 1024 * 10)
let s:tty_large = g:is_tty ? 512000 : g:LargeFile
function! s:ApplyLargeFileSettings() abort
if get(b:, 'chopsticks_large_file', 0)
setlocal bufhidden=unload undolevels=-1 noswapfile
let b:ale_enabled = 0
if &l:syntax !=# ''
setlocal syntax=
endif
elseif get(b:, 'chopsticks_tty_large_file', 0)
if &l:syntax !=# ''
setlocal syntax=
endif
endif
endfunction
function! s:MarkLargeFile(file) abort
if empty(a:file)
return
endif
let l:fsize = getfsize(a:file)
if l:fsize > g:LargeFile || l:fsize == -2
let b:chopsticks_large_file = 1
elseif g:is_tty && l:fsize > s:tty_large
let b:chopsticks_tty_large_file = 1
endif
call s:ApplyLargeFileSettings()
endfunction
augroup ChopstickLargeFile
autocmd!
autocmd BufReadPre * call s:MarkLargeFile(expand('<afile>'))
autocmd BufReadPost,FileType,Syntax * call s:ApplyLargeFileSettings()
augroup END
" ── Run Current File (,cr) ──────────────────────────────────────────────────
function! s:RunFile() abort
write
let l:ft = &filetype
let l:file = shellescape(expand('%:p'))
if l:ft ==# 'python' | execute '!python3 ' . l:file
elseif l:ft ==# 'javascript' | execute '!node ' . l:file
elseif l:ft ==# 'typescript' | execute '!npx ts-node ' . l:file
elseif l:ft ==# 'go' | execute '!go run ' . l:file
elseif l:ft ==# 'rust' | execute '!cargo run'
elseif l:ft ==# 'sh' | execute '!bash ' . l:file
elseif l:ft ==# 'c'
let l:out_path = tempname()
let l:out = shellescape(l:out_path)
execute '!gcc -o ' . l:out . ' ' . l:file . ' && ' . l:out
call delete(l:out_path)
elseif l:ft ==# 'lua' | execute '!lua ' . l:file
elseif l:ft ==# 'ruby' | execute '!ruby ' . l:file
elseif l:ft ==# 'perl' | execute '!perl ' . l:file
else | echo 'No runner for filetype: ' . l:ft
endif
endfunction
nnoremap <leader>cr :call <SID>RunFile()<CR>
" ── Sudo Save ───────────────────────────────────────────────────────────────
if get(g:, 'chopsticks_enable_sudo_save_bang', 0)
cnoremap w!! w !sudo tee > /dev/null %
endif
" ── QuickFix ────────────────────────────────────────────────────────────────
augroup ChopstickQF
autocmd!
autocmd QuickFixCmdPost [^l]* cwindow
autocmd QuickFixCmdPost l* lwindow
augroup END
nnoremap <silent> ]q :cnext<CR>
nnoremap <silent> [q :cprev<CR>
" ── Status Diagnostic (:ChopsticksStatus) ───────────────────────────────────
function! s:Check(name, cmd) abort
return executable(a:cmd) ? ' OK ' . a:name : ' -- ' . a:name . ' (missing: ' . a:cmd . ')'
endfunction
function! s:Off(name, reason) abort
return ' off ' . a:name . ' (' . a:reason . ')'
endfunction
function! s:PlugDir(name) abort
if !exists('g:plugs') || !has_key(g:plugs, a:name)
return ''
endif
return fnamemodify(get(g:plugs[a:name], 'dir', ''), ':p')
endfunction
function! s:PlugInstalled(name) abort
let l:dir = s:PlugDir(a:name)
return !empty(l:dir) && isdirectory(l:dir)
endfunction
function! s:LspStackIssue() abort
if !get(g:, 'chopsticks_enable_lsp', 1)
return 'LSP disabled by profile'
endif
if empty(s:PlugDir('vim-lsp'))
return 'vim-lsp not declared by this profile'
endif
if !s:PlugInstalled('vim-lsp')
return 'vim-lsp not installed; run :PlugInstall'
endif
if empty(s:PlugDir('vim-lsp-settings'))
return 'vim-lsp-settings not declared by this profile'
endif
if !s:PlugInstalled('vim-lsp-settings')
return 'vim-lsp-settings not installed; run :PlugInstall'
endif
return ''
endfunction
function! s:LspStackCheck() abort
let l:issue = s:LspStackIssue()
if l:issue ==# 'LSP disabled by profile'
return s:Off('vim-lsp stack', l:issue)
endif
if !empty(l:issue)
return ' -- vim-lsp stack (' . l:issue . ')'
endif
if exists(':LspStatus') == 2 || exists(':LspInstallServer') == 2
return ' OK vim-lsp stack (installed)'
endif
return ' OK vim-lsp stack (installed; not loaded yet)'
endfunction
function! s:LspCheck(ft, server) abort
let l:issue = s:LspStackIssue()
if l:issue ==# 'LSP disabled by profile'
return s:Off(a:ft, l:issue)
endif
if !empty(l:issue)
return ' -- ' . a:ft . ' (' . l:issue . ')'
endif
let l:dir = expand('~/.local/share/vim-lsp-settings/servers/' . a:server)
if isdirectory(l:dir)
return ' OK ' . a:ft . ' (' . a:server . ')'
endif
return ' -- ' . a:ft . ' (:LspInstallServer in a ' . a:ft . ' file)'
endfunction
function! s:ChopsticksStatus() abort
let l:lines = []
call add(l:lines, 'chopsticks status')
call add(l:lines, repeat('─', 50))
call add(l:lines, '')
call add(l:lines, '── system tools ──')
call add(l:lines, s:Check('fzf', 'fzf'))
call add(l:lines, s:Check('ripgrep', 'rg'))
call add(l:lines, s:Check('git', 'git'))
call add(l:lines, s:Check('curl', 'curl'))
call add(l:lines, s:Check('node', 'node'))
call add(l:lines, s:Check('python3', 'python3'))
call add(l:lines, s:Check('go', 'go'))
call add(l:lines, '')
call add(l:lines, '── lsp servers ── (:LspInstallServer to install)')
call add(l:lines, s:LspStackCheck())
if get(g:, 'chopsticks_enable_lsp', 1)
call add(l:lines, ' LSP actions are buffer-local and start after a server attaches.')
call add(l:lines, ' Missing one? Open that filetype and run :LspInstallServer once.')
endif
call add(l:lines, s:LspCheck('python', 'pylsp'))
call add(l:lines, s:LspCheck('go', 'gopls'))
call add(l:lines, s:LspCheck('rust', 'rust-analyzer'))
call add(l:lines, s:LspCheck('typescript', 'typescript-language-server'))
call add(l:lines, s:LspCheck('c/c++', 'clangd'))
call add(l:lines, s:LspCheck('bash', 'bash-language-server'))
call add(l:lines, s:LspCheck('html', 'vscode-html-language-server'))
call add(l:lines, s:LspCheck('json', 'vscode-json-language-server'))
call add(l:lines, s:LspCheck('yaml', 'yaml-language-server'))
call add(l:lines, s:LspCheck('markdown', 'marksman'))
call add(l:lines, s:LspCheck('sql', 'sqls'))
call add(l:lines, '')
call add(l:lines, '── linters ──')
if get(g:, 'chopsticks_enable_lint', 1)
call add(l:lines, s:Check('flake8 (python)', 'flake8'))
call add(l:lines, s:Check('pylint (python)', 'pylint'))
call add(l:lines, s:Check('eslint (js/ts)', 'eslint'))
call add(l:lines, s:Check('staticcheck (go)', 'staticcheck'))
call add(l:lines, s:Check('shellcheck (sh)', 'shellcheck'))
call add(l:lines, s:Check('yamllint (yaml)', 'yamllint'))
call add(l:lines, s:Check('hadolint (docker)', 'hadolint'))
if get(g:, 'chopsticks_markdown_lint', 0)
call add(l:lines, s:Check('markdownlint (md)', 'markdownlint'))
else
call add(l:lines, s:Off('markdownlint (md)', 'disabled by default'))
endif
else
call add(l:lines, s:Off('ALE linters', 'lint disabled by profile'))
endif
call add(l:lines, '')
call add(l:lines, '── formatters ── (format-on-save is ' . (get(g:, 'ale_fix_on_save', 0) ? 'ON' : 'OFF') . ')')
if get(g:, 'chopsticks_enable_lint', 1)
call add(l:lines, s:Check('black (python)', 'black'))
call add(l:lines, s:Check('isort (python)', 'isort'))
call add(l:lines, s:Check('prettier (js/ts/json)', 'prettier'))
if get(g:, 'chopsticks_markdown_format_on_save', 0)
call add(l:lines, s:Check('prettier (md)', 'prettier'))
else
call add(l:lines, s:Off('prettier (md)', 'disabled by default'))
endif
call add(l:lines, s:Check('goimports (go)', 'goimports'))
call add(l:lines, s:Check('rustfmt (rust)', 'rustfmt'))
call add(l:lines, s:Check('clang-format (c)', 'clang-format'))
else
call add(l:lines, s:Off('ALE formatters', 'lint disabled by profile'))
endif
call add(l:lines, '')
let l:ok = len(filter(copy(l:lines), 'v:val =~# " OK "'))
let l:miss = len(filter(copy(l:lines), 'v:val =~# " -- "'))
call add(l:lines, repeat('─', 50))
call add(l:lines, ' ' . l:ok . ' ready, ' . l:miss . ' missing')
call add(l:lines, '')
call add(l:lines, ' Install missing tools with ./install.sh')
if get(g:, 'chopsticks_enable_lsp', 1)
call add(l:lines, ' Install LSP servers with :LspInstallServer')
endif
let l:name = '__ChopsticksStatus__'
if bufwinnr(l:name) > 0
execute bufwinnr(l:name) . 'wincmd w | bd'
endif
execute 'botright new ' . l:name
resize 45
setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile
setlocal nowrap nonumber norelativenumber signcolumn=no
call setline(1, l:lines)
setlocal nomodifiable readonly
nnoremap <buffer> <silent> q :bd<CR>
endfunction
command! ChopsticksStatus call s:ChopsticksStatus()
" ── Cheat Sheet (,?) ────────────────────────────────────────────────────────
function! s:CheatSheet() abort
let l:name = '__ChopsticksCheatSheet__'
if bufwinnr(l:name) > 0
execute bufwinnr(l:name) . 'wincmd w | bd'
return
endif
let l:has_lsp = get(g:, 'chopsticks_enable_lsp', 1)
let l:has_lint = get(g:, 'chopsticks_enable_lint', 1)
let l:has_undotree = exists('g:plugs["undotree"]')
let l:has_previm = exists('g:plugs["previm"]')
let l:lines = [
\ ' chopsticks ,? close',
\ ' ─────────────────────────────',
\ '',
\ ' ── files ──────────────────',
\ ' ,ff files',
\ ' ,b buffers',
\ ' ,rg grep project',
\ ' ,rG grep word',
\ ' ,e sidebar (cwd)',
\ ' ,E sidebar (file dir)',
\ ' ,, last file',
\ ' ,fh recent files',
\ ' ,fl lines in buffer',
\ ' ,fc commands',
\ ' ,fm marks',
\ '',
\ ' ── code ──────────────────',
\ ]
if l:has_lsp
call extend(l:lines, [
\ ' ,dd definition',
\ ' ,dt type definition',
\ ' ,di implementation',
\ ' ,dr references',
\ ' ,dk hover docs',
\ ' ,rn rename',
\ ' ,ca code action',
\ ' ,f format',
\ ' ,o outline',
\ ' ,dp ,dn LSP diagnostics',
\ ' :LspInstallServer setup LSP',
\ ' :ChopsticksStatus check LSP setup',
\ ])
endif
call add(l:lines, ' ,cr run file')
if l:has_previm
call add(l:lines, ' ,mp markdown preview')
endif
call add(l:lines, ' ,mt table of contents')
if l:has_lint
call extend(l:lines, [
\ ' [e ]e ALE errors',
\ ' ,af format on save',
\ ])
endif
call extend(l:lines, [
\ '',
\ ' ── edit ──────────────────',
\ ' gc comment',
\ ' ,S+2ch easymotion jump',
\ ' cs"'' surround',
\ ])
if l:has_undotree
call add(l:lines, ' ,u undo tree')
endif
call extend(l:lines, [
\ ' ,y ,p clipboard y/p (v)',
\ ' Alt+j/k move line (v)',
\ ' ,* replace word (v)',
\ ' ,F re-indent (v)',
\ ' ,W strip trailing (v)',
\ '',
\ ' ── git ───────────────────',
\ ' ,gs status',
\ ' ,gd diff',
\ ' ,gb blame',
\ ' ,gc commit',
\ ' ,gp push',
\ ' ,gl pull',
\ ' ,gL log graph',
\ ' ,gC FZF commits',
\ ' [x ]x conflict markers',
\ '',
\ ' ── windows ───────────────',
\ ' <C-w>hjkl navigate splits',
\ ' ,h ,l prev / next buf',
\ ' ,bd close buffer',
\ ' ,z maximize toggle',
\ ' ,= ,- resize height',
\ ' ,tv ,th terminal v / h',
\ ' ]q [q next / prev qf',
\ ' ,qo ,qc open / close qf',
\ '',
\ ' ── toggle ────────────────',
\ ' F2 paste mode',
\ ' F3 line numbers',
\ ' F4 relative numbers',
\ ' F6 invisible chars',
\ ' ,ss spell check',
\ '',
\ ' ── survival ──────────────',
\ ' ,w save',
\ ' ,q quit',
\ ' ,x save + quit',
\ ' Esc exit insert',
\ ' ,ev edit vimrc',
\ ' ,sv reload vimrc',
\ ' :ChopsticksStatus health',
\ ])
execute 'vertical botright new ' . l:name
vertical resize 42
setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile
setlocal nowrap nonumber norelativenumber signcolumn=no
setlocal winfixwidth
call setline(1, l:lines)
setlocal nomodifiable readonly
nnoremap <buffer> <silent> q :bd<CR>
nnoremap <buffer> <silent> <leader>? :bd<CR>
endfunction
nnoremap <silent> <leader>? :call <SID>CheatSheet()<CR>

View file

@ -1,102 +0,0 @@
" tutor.vim — guided practice for chopsticks keymaps
function! s:OpenTutor(lines) abort
let l:name = '__ChopsticksTutor__'
if bufwinnr(l:name) > 0
execute bufwinnr(l:name) . 'wincmd w | bd'
return 0
endif
execute 'botright new ' . l:name
resize 38
setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile
setlocal nowrap nonumber norelativenumber signcolumn=no
call setline(1, a:lines)
setlocal nomodifiable readonly
nnoremap <buffer> <silent> q :bd<CR>
return 1
endfunction
function! s:ChopsticksTutor() abort
if g:chopsticks_space_keymaps
let l:lines = [
\ ' chopsticks tutor q close',
\ ' ───────────────────────────────',
\ '',
\ ' 1. survival',
\ ' Esc Normal mode',
\ ' SPC ? active cheat sheet',
\ ' SPC w save',
\ ' SPC qx save and quit',
\ ' :ChopsticksStatus health check',
\ '',
\ ' 2. find and switch',
\ ' SPC SPC find files',
\ ' SPC / grep project',
\ ' SPC , buffers',
\ ' SPC Tab alternate buffer',
\ ' SPC e/E sidebar cwd / file dir',
\ '',
\ ' 3. jump and edit',
\ ' s + 2 chars visible jump',
\ ' SPC S same jump fallback',
\ ' cl / cc native s / S substitute',
\ ' gc comment',
\ ' SPC U undo tree',
\ '',
\ ' 4. code loop',
\ ' gd / gr / K definition / refs / docs',
\ ' gI / gy implementation / type',
\ ' [d ]d LSP diagnostics',
\ ' SPC ca/cr/cf action / rename / format',
\ ' SPC rr run current file',
\ '',
\ ' 5. git and windows',
\ ' SPC gs/gd/gb status / diff / blame',
\ ' SPC gl log graph',
\ ' <C-w>hjkl split navigation',
\ ' SPC z maximize split',
\ '',
\ ' daily drill',
\ ' Open a project, run SPC SPC, jump with s, inspect with gd/K,',
\ ' edit with gc/SPC cf, check SPC gs, then save with SPC w.',
\ ]
else
let l:lines = [
\ ' chopsticks tutor q close',
\ ' ───────────────────────────────',
\ '',
\ ' classic layout',
\ ' ,? active cheat sheet',
\ ' ,w / ,x save / save and quit',
\ ' ,ff find files',
\ ' ,rg grep project',
\ ' ,b buffers',
\ ' ,, alternate buffer',
\ '',
\ ' code loop',
\ ' ,dd / ,dr definition / refs',
\ ' ,dk hover docs',
\ ' ,ca / ,rn action / rename',
\ ' ,f format',
\ ' ,cr run current file',
\ '',
\ ' edit and git',
\ ' ,S + 2 chars EasyMotion jump',
\ ' gc comment',
\ ' ,u undo tree',
\ ' ,gs/,gd/,gb status / diff / blame',
\ ' <C-w>hjkl split navigation',
\ '',
\ ' support',
\ ' :ChopsticksStatus health check',
\ ' README.md full reference',
\ ' QUICKSTART.md 5-minute path',
\ ]
endif
if s:OpenTutor(l:lines)
nnoremap <buffer> <silent> ? :ChopsticksCheatSheet<CR>
endif
endfunction
command! ChopsticksTutor call s:ChopsticksTutor()

View file

@ -1,64 +0,0 @@
" utilities.vim — small editing and config helpers
if get(g:, 'chopsticks_enable_reindent_file', 0)
if g:chopsticks_space_keymaps
nnoremap <leader>c= gg=G``
else
nnoremap <leader>F gg=G``
endif
endif
if g:chopsticks_space_keymaps
vnoremap <leader>= =
else
vnoremap <leader>F =
nnoremap <leader>wa :wa<CR>
endif
if !g:chopsticks_space_keymaps
nnoremap <silent> <Leader>= :exe "resize " . (winheight(0) * 3/2)<CR>
nnoremap <silent> <Leader>- :exe "resize " . (winheight(0) * 2/3)<CR>
endif
if g:chopsticks_space_keymaps
nnoremap <leader><Tab> <c-^>
else
nnoremap <leader><leader> <c-^>
endif
if g:chopsticks_space_keymaps
nnoremap <leader>cW :%s/\s\+$//<CR>:let @/=''<CR>
vnoremap <leader>cW :s/\s\+$//<CR>:let @/=''<CR>gv
else
nnoremap <leader>W :%s/\s\+$//<CR>:let @/=''<CR>
vnoremap <leader>W :s/\s\+$//<CR>:let @/=''<CR>gv
endif
if g:chopsticks_space_keymaps
nnoremap <leader>fv :edit $MYVIMRC<CR>
nnoremap <leader>fV :unlet! g:chopsticks_loaded<CR>:execute 'source ' . fnameescape($MYVIMRC)<CR>:echo "vimrc reloaded"<CR>
else
nnoremap <leader>ev :edit $MYVIMRC<CR>
nnoremap <leader>sv :unlet! g:chopsticks_loaded<CR>:execute 'source ' . fnameescape($MYVIMRC)<CR>:echo "vimrc reloaded"<CR>
endif
if g:chopsticks_space_keymaps
nnoremap <leader>sr :%s/\<<C-r><C-w>\>//g<Left><Left>
vnoremap <leader>sr :s///g<Left><Left><Left>
else
nnoremap <leader>* :%s/\<<C-r><C-w>\>//g<Left><Left>
vnoremap <leader>* :s///g<Left><Left><Left>
endif
if has('clipboard')
if g:chopsticks_space_keymaps
nnoremap <leader>fp :let @+ = expand("%:p")<CR>:echo "Copied: " . expand("%:p")<CR>
nnoremap <leader>fn :let @+ = expand("%:t")<CR>:echo "Copied: " . expand("%:t")<CR>
else
nnoremap <leader>cp :let @+ = expand("%:p")<CR>:echo "Copied: " . expand("%:p")<CR>
nnoremap <leader>cf :let @+ = expand("%:t")<CR>:echo "Copied: " . expand("%:t")<CR>
endif
endif
if get(g:, 'chopsticks_enable_sudo_save_bang', 0)
cnoremap w!! w !sudo tee > /dev/null %
endif

View file

@ -1,28 +0,0 @@
#!/usr/bin/env bash
# Shared setup for chopsticks test scripts.
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
TMP_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/chopsticks-test-XXXXXX")"
EMPTY_XDG="$TMP_ROOT/xdg-empty"
STARTUP_LIMIT_MS="${STARTUP_LIMIT_MS:-150}"
cleanup() {
rm -rf "$TMP_ROOT"
}
trap cleanup EXIT
cd "$ROOT"
mkdir -p "$EMPTY_XDG"
step() {
printf '\n==> %s\n' "$1"
}
need() {
command -v "$1" >/dev/null 2>&1 || {
echo "Missing required command: $1" >&2
exit 1
}
}

View file

@ -1,113 +0,0 @@
#!/usr/bin/env bash
# Shell, docs, installer, and bootstrap checks.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=scripts/test-common.sh
source "$SCRIPT_DIR/test-common.sh"
check_shell() {
step "Shell syntax and lint"
need bash
bash -n install.sh
bash -n get.sh
bash -n scripts/test.sh
bash -n scripts/test-common.sh
bash -n scripts/test-quick.sh
bash -n scripts/test-vim.sh
test -x install.sh
test -x get.sh
test -x scripts/test.sh
need shellcheck
shellcheck install.sh get.sh scripts/test.sh \
scripts/test-common.sh scripts/test-quick.sh scripts/test-vim.sh
}
check_docs() {
step "Markdown lint"
need markdownlint
markdownlint README.md QUICKSTART.md CONTRIBUTING.md CHANGELOG.md
}
check_installer_modes() {
step "Installer profile-only modes"
XDG_CONFIG_HOME="$TMP_ROOT/dry" ./install.sh --dry-run --profile=full \
| tee "$TMP_ROOT/install-dry-run.txt"
grep -q 'Profile: full' "$TMP_ROOT/install-dry-run.txt"
test ! -e "$TMP_ROOT/dry/chopsticks.vim"
XDG_CONFIG_HOME="$TMP_ROOT/config" ./install.sh --configure-only --profile=minimal
grep -q "let g:chopsticks_profile = 'minimal'" "$TMP_ROOT/config/chopsticks.vim"
XDG_CONFIG_HOME="$TMP_ROOT/config" ./install.sh --configure-only --profile=full
grep -q "let g:chopsticks_profile = 'full'" "$TMP_ROOT/config/chopsticks.vim"
XDG_CONFIG_HOME="$TMP_ROOT/default" ./install.sh --configure-only --yes
grep -q "let g:chopsticks_profile = 'engineer'" "$TMP_ROOT/default/chopsticks.vim"
}
check_bootstrap() {
step "Bootstrap dry-run safety"
CHOPSTICKS_DEST="$TMP_ROOT/bootstrap" ./get.sh --dry-run --profile=minimal \
| tee "$TMP_ROOT/get-dry-run.txt"
grep -q 'Would clone' "$TMP_ROOT/get-dry-run.txt"
test ! -e "$TMP_ROOT/bootstrap"
mkdir -p "$TMP_ROOT/no-git-bin"
printf '%s\n' \
'#!/usr/bin/env bash' \
"echo \"brew was called\" >> \"\$BREW_LOG\"" \
'exit 42' > "$TMP_ROOT/no-git-bin/brew"
chmod +x "$TMP_ROOT/no-git-bin/brew"
BREW_LOG="$TMP_ROOT/no-git-brew.log" \
PATH="$TMP_ROOT/no-git-bin" \
CHOPSTICKS_DEST="$TMP_ROOT/no-git-bootstrap" \
/bin/bash ./get.sh --dry-run --profile=full \
| tee "$TMP_ROOT/get-no-git-dry-run.txt"
grep -q 'Would require: git' "$TMP_ROOT/get-no-git-dry-run.txt"
grep -q 'Would clone' "$TMP_ROOT/get-no-git-dry-run.txt"
test ! -e "$TMP_ROOT/no-git-brew.log"
test ! -e "$TMP_ROOT/no-git-bootstrap"
mkdir -p "$TMP_ROOT/not-chopsticks"
git -c init.defaultBranch=main init "$TMP_ROOT/not-chopsticks" >/dev/null
git -C "$TMP_ROOT/not-chopsticks" remote add origin https://github.com/example/not-chopsticks.git
if CHOPSTICKS_DEST="$TMP_ROOT/not-chopsticks" ./get.sh --dry-run; then
echo "Expected get.sh to reject non-chopsticks repo" >&2
exit 1
fi
mkdir -p "$TMP_ROOT/chopsticks-existing"
git -c init.defaultBranch=main init "$TMP_ROOT/chopsticks-existing" >/dev/null
git -C "$TMP_ROOT/chopsticks-existing" remote add origin https://github.com/m1ngsama/chopsticks.git
touch "$TMP_ROOT/chopsticks-existing/install.sh" "$TMP_ROOT/chopsticks-existing/.vimrc"
CHOPSTICKS_DEST="$TMP_ROOT/chopsticks-existing" ./get.sh --dry-run --yes \
| tee "$TMP_ROOT/get-existing.txt"
grep -q 'Would update existing chopsticks repo' "$TMP_ROOT/get-existing.txt"
}
run_quick_group() {
case "$1" in
quick)
check_shell
check_docs
check_installer_modes
check_bootstrap
;;
shell) check_shell ;;
docs) check_docs ;;
installer) check_installer_modes ;;
bootstrap) check_bootstrap ;;
*)
echo "Unknown quick test group: $1" >&2
exit 1 ;;
esac
}
if [[ $# -eq 0 ]]; then
set -- quick
fi
for group in "$@"; do
run_quick_group "$group"
done

View file

@ -1,414 +0,0 @@
#!/usr/bin/env bash
# Vim smoke tests. Requires plugins in ~/.vim/plugged.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=scripts/test-common.sh
source "$SCRIPT_DIR/test-common.sh"
check_plugin_dirs() {
step "Plugin directories"
for plugin in \
fzf fzf.vim vim-fugitive vim-gitgutter ale vim-lsp vim-lsp-settings \
asyncomplete.vim asyncomplete-lsp.vim vim-markdown
do
test -d "$HOME/.vim/plugged/$plugin" || {
echo "Missing plugin directory: $plugin" >&2
exit 1
}
done
}
check_vim() {
step "Vim smoke tests"
need vim
check_plugin_dirs
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N -c 'qa!' 2>&1
if [ -x /usr/bin/vim ] && [ "$(command -v vim)" != "/usr/bin/vim" ]; then
XDG_CONFIG_HOME="$EMPTY_XDG" /usr/bin/vim -u .vimrc -i NONE -es -N -c 'qa!' 2>&1
fi
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
-c "redir! > $TMP_ROOT/plugs.txt" \
-c 'silent echo len(g:plugs)' \
-c 'redir END' \
-c 'qa!' 2>/dev/null
PLUGS="$(tr -d '[:space:]' < "$TMP_ROOT/plugs.txt")"
echo "Plugins registered: $PLUGS"
if [ "$PLUGS" -lt 20 ]; then
echo "Expected 20+ plugins, got $PLUGS" >&2
exit 1
fi
mkdir -p "$TMP_ROOT/chopsticks path/modules"
cp .vimrc "$TMP_ROOT/chopsticks path/.vimrc"
cp modules/*.vim "$TMP_ROOT/chopsticks path/modules/"
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u "$TMP_ROOT/chopsticks path/.vimrc" \
-i NONE -es -N -c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u NONE -i NONE -es -N \
-c 'let g:chopsticks_profile = "minimal"' \
-c 'source .vimrc' \
-c 'if has_key(g:plugs, "ale") || has_key(g:plugs, "vim-lsp") || has_key(g:plugs, "vim-lsp-settings") || has_key(g:plugs, "asyncomplete.vim") || has_key(g:plugs, "auto-pairs") | cquit | endif' \
-c 'qa!' 2>&1
mkdir -p "$TMP_ROOT/local"
printf "%s\n" "let g:chopsticks_profile = 'minimal'" > "$TMP_ROOT/local/config.vim"
vim -u NONE -i NONE -es -N \
-c "let g:chopsticks_local_config = '$TMP_ROOT/local/config.vim'" \
-c 'source .vimrc' \
-c 'if g:chopsticks_profile !=# "minimal" || has_key(g:plugs, "ale") || has_key(g:plugs, "vim-lsp") || has_key(g:plugs, "auto-pairs") | cquit | endif' \
-c 'qa!' 2>&1
mkdir -p "$TMP_ROOT/xdg"
printf "%s\n" "let g:chopsticks_profile = 'minimal'" > "$TMP_ROOT/xdg/chopsticks.vim"
XDG_CONFIG_HOME="$TMP_ROOT/xdg" vim -u NONE -i NONE -es -N \
-c 'source .vimrc' \
-c 'if g:chopsticks_profile !=# "minimal" || has_key(g:plugs, "ale") || has_key(g:plugs, "vim-lsp") || has_key(g:plugs, "auto-pairs") | cquit | endif' \
-c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
-c 'ChopsticksStatus' \
-c "redir! > $TMP_ROOT/status-default.txt" \
-c 'silent %print' \
-c 'redir END' \
-c 'qa!' 2>&1
if grep -Fq 'vim-lsp not loaded' "$TMP_ROOT/status-default.txt"; then
cat "$TMP_ROOT/status-default.txt"
exit 1
fi
grep -Fq 'OK vim-lsp stack (installed)' "$TMP_ROOT/status-default.txt"
grep -Fq 'python (:LspInstallServer in a python file)' "$TMP_ROOT/status-default.txt"
grep -Fq 'LSP actions are buffer-local and start after a server attaches.' "$TMP_ROOT/status-default.txt"
grep -Fq 'Open that filetype and run :LspInstallServer once.' "$TMP_ROOT/status-default.txt"
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 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' \
-c 'if maparg("<Space>/", "v") !~# "escape" || maparg("<Space>v", "n") !=# last_change_map || maparg("<Space><Space>", "n") !~# "SmartFiles" | cquit | endif' \
-c 'if maparg(",/", "v") !=# "" || maparg(",v", "n") !=# "" || maparg(",ff", "n") !=# "" | cquit | endif' \
-c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u NONE -i NONE -es -N \
-c 'let g:chopsticks_keymap_style = "classic"' \
-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(",gp", "n") !=# "" || maparg(",gl", "n") !=# "" | cquit | endif' \
-c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u NONE -i NONE -es -N \
-c 'let g:chopsticks_enable_jk_escape = 1' \
-c 'source .vimrc' \
-c 'if maparg("jk", "i") !~# "<Esc>" | cquit | endif' \
-c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u NONE -i NONE -es -N \
-c 'let g:chopsticks_enable_ctrl_s_save = 1' \
-c 'let g:chopsticks_enable_sudo_save_bang = 1' \
-c 'let g:chopsticks_enable_completion_keymaps = 1' \
-c 'source .vimrc' \
-c 'if maparg("<C-s>", "n") !~# ":w" || maparg("<C-s>", "i") !~# ":w" || maparg("w!!", "c") !~# "sudo tee" | cquit | endif' \
-c 'if maparg("<Tab>", "i") !~# "pumvisible" || maparg("<S-Tab>", "i") !~# "pumvisible" || maparg("<CR>", "i") !~# "asyncomplete#close_popup" | cquit | endif' \
-c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u NONE -i NONE -es -N \
-c 'let g:chopsticks_enable_auto_pairs = 1' \
-c 'source .vimrc' \
-c 'if !has_key(g:plugs, "auto-pairs") | cquit | endif' \
-c 'qa!' 2>&1
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 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
-c 'if &exrc || &secure | cquit | endif' \
-c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u NONE -i NONE -es -N \
-c 'let g:chopsticks_enable_exrc = 1' \
-c 'source .vimrc' \
-c 'if !&exrc || !&secure | cquit | endif' \
-c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
-c 'if maparg("<Space>c=", "n") !=# "" | cquit | endif' \
-c 'if maparg("<Space>=", "v") !~# "=" | cquit | endif' \
-c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u NONE -i NONE -es -N \
-c 'let g:chopsticks_enable_reindent_file = 1' \
-c 'source .vimrc' \
-c 'if maparg("<Space>c=", "n") !~# "gg=G" | cquit | endif' \
-c 'qa!' 2>&1
TERM=xterm-256color XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
-c 'if g:is_tty || &ttimeoutlen != 10 | cquit | endif' \
-c 'qa!' 2>&1
TERM=linux XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
-c 'if !g:is_tty || &ttimeoutlen != 50 | cquit | endif' \
-c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
-c 'if !exists("loaded_gzip") || !exists("loaded_logiPat") || !exists("loaded_rrhelper") || !exists("loaded_spellfile_plugin") | cquit | endif' \
-c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
-c 'silent! delcommand LspStatus' \
-c 'silent! delcommand LspInstallServer' \
-c 'ChopsticksStatus' \
-c "redir! > $TMP_ROOT/status-lsp-not-loaded.txt" \
-c 'silent %print' \
-c 'redir END' \
-c 'qa!' 2>&1
grep -Fq 'OK vim-lsp stack (installed; not loaded yet)' "$TMP_ROOT/status-lsp-not-loaded.txt"
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u NONE -i NONE -es -N \
-c 'let g:chopsticks_profile = "minimal"' \
-c 'source .vimrc' \
-c 'ChopsticksStatus' \
-c "redir! > $TMP_ROOT/status-minimal.txt" \
-c 'silent %print' \
-c 'redir END' \
-c 'qa!' 2>&1
grep -Fq 'off vim-lsp stack (LSP disabled by profile)' "$TMP_ROOT/status-minimal.txt"
grep -Fq 'off python (LSP disabled by profile)' "$TMP_ROOT/status-minimal.txt"
if grep -Fq 'LSP actions are buffer-local' "$TMP_ROOT/status-minimal.txt"; then
cat "$TMP_ROOT/status-minimal.txt"
exit 1
fi
mkdir -p "$TMP_ROOT/missing-home/.vim/autoload"
cp "$HOME/.vim/autoload/plug.vim" "$TMP_ROOT/missing-home/.vim/autoload/plug.vim"
HOME="$TMP_ROOT/missing-home" XDG_CONFIG_HOME="$EMPTY_XDG" \
vim -u .vimrc -i NONE -es -N \
-c 'ChopsticksStatus' \
-c "redir! > $TMP_ROOT/status-missing-plugin.txt" \
-c 'silent %print' \
-c 'redir END' \
-c 'qa!' 2>&1
grep -Fq 'vim-lsp not installed; run :PlugInstall' "$TMP_ROOT/status-missing-plugin.txt"
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
-c 'doautocmd User lsp_buffer_enabled' \
-c 'if maparg("gd", "n") !~# "lsp-definition" || maparg("gr", "n") !~# "lsp-references" || maparg("gI", "n") !~# "lsp-implementation" || maparg("gy", "n") !~# "lsp-type-definition" || maparg("K", "n") !~# "lsp-hover" | cquit | endif' \
-c 'if maparg("[d", "n") !~# "lsp-previous-diagnostic" || maparg("]d", "n") !~# "lsp-next-diagnostic" | cquit | endif' \
-c 'if maparg("<Space>ca", "n") !~# "lsp-code-action" || maparg("<Space>cr", "n") !~# "lsp-rename" || maparg("<Space>cf", "n") !~# "lsp-document-format" | cquit | endif' \
-c 'if maparg("<Space>ci", "n") !~# "LspStatus" || maparg("<Space>co", "n") !~# "lsp-document-symbol-search" | cquit | endif' \
-c 'if maparg("<Space>cd", "n") !=# "" || maparg("<Space>ck", "n") !=# "" || maparg("<Space>cp", "n") !=# "" || maparg("<Space>cn", "n") !=# "" | cquit | endif' \
-c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u NONE -i NONE -es -N \
-c 'let g:chopsticks_keymap_style = "classic"' \
-c 'source .vimrc' \
-c 'doautocmd User lsp_buffer_enabled' \
-c 'if maparg("gd", "n") !=# "" || maparg("K", "n") !=# "" || maparg("gI", "n") !=# "" || maparg("gr", "n") !=# "" | cquit | endif' \
-c 'if maparg(",dd", "n") !~# "lsp-definition" || maparg(",dt", "n") !~# "lsp-type-definition" || maparg(",di", "n") !~# "lsp-implementation" || maparg(",dr", "n") !~# "lsp-references" || maparg(",dk", "n") !~# "lsp-hover" | cquit | endif' \
-c 'if maparg(",dp", "n") !~# "lsp-previous-diagnostic" | cquit | endif' \
-c 'if maparg(",dn", "n") !~# "lsp-next-diagnostic" | cquit | endif' \
-c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u NONE -i NONE -es -N \
-c 'let g:chopsticks_keymap_style = "space"' \
-c 'source .vimrc' \
-c 'if mapleader !=# "\<Space>" || maplocalleader !=# "," | cquit | endif' \
-c 'if maparg(",ff", "n") !=# "" || maparg(",w", "n") !=# "" || maparg(",mt", "n") !=# "" || maparg(",gp", "n") !=# "" || maparg("<Space>gp", "n") !=# "" | cquit | endif' \
-c 'if maparg("<Space>f", "n") !=# "" || maparg("<Space>q", "n") !=# "" || maparg("<Space>u", "n") !=# "" || maparg("<Space>c", "n") !=# "" || maparg("<Space>x", "n") !=# "" || maparg("<Space>wm", "n") !=# "" || maparg("<Space>w+", "n") !=# "" || maparg("<Space>w-", "n") !=# "" | cquit | endif' \
-c 'if maparg("<Space><Space>", "n") !~# "SmartFiles" || maparg("<Space>ff", "n") !~# "SmartFiles" || maparg("<Space>,", "n") !~# "Buffers" || maparg("<Space>bd", "n") !~# "Bclose" | cquit | endif' \
-c 'if maparg("<Space>w", "n") !~# ":w" || maparg("<Space>W", "n") !~# ":wa" || maparg("<Space>qq", "n") !~# ":q" || maparg("<Space>qx", "n") !~# ":x" || maparg("<Space>U", "n") !~# "UndotreeToggle" || maparg("<Space>fs", "n") !=# "" || maparg("<Space>bu", "n") !=# "" | cquit | endif' \
-c 'if maparg("<Space>gl", "n") !~# "Git log" || maparg("<Space>gC", "n") !~# "Commits" | cquit | endif' \
-c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u NONE -i NONE -es -N \
-c 'let g:chopsticks_keymap_style = "space"' \
-c 'source .vimrc' \
-c 'doautocmd User lsp_buffer_enabled' \
-c 'if maparg("<Space>cf", "n") !~# "lsp-document-format" | cquit | endif' \
-c 'if maparg("gd", "n") !~# "lsp-definition" || maparg("gr", "n") !~# "lsp-references" || maparg("K", "n") !~# "lsp-hover" | cquit | endif' \
-c 'if maparg("<Space>cd", "n") !=# "" || maparg("<Space>ck", "n") !=# "" | cquit | endif' \
-c 'if maparg("<Space>f", "n") !=# "" || maparg("<Space>c", "n") !=# "" | cquit | endif' \
-c 'if maparg(",f", "n") !=# "" || maparg(",dd", "n") !=# "" | cquit | endif' \
-c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
-c 'call feedkeys("\<Space>?", "xt")' \
-c "redir! > $TMP_ROOT/cheat-default.txt" \
-c 'silent %print' \
-c 'redir END' \
-c 'qa!' 2>&1
grep -Fq ':ChopsticksStatus check LSP setup' "$TMP_ROOT/cheat-default.txt"
grep -Fq 'SPC SPC files' "$TMP_ROOT/cheat-default.txt"
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 'SPC w save' "$TMP_ROOT/cheat-default.txt"
grep -Fq 's+2ch easymotion jump' "$TMP_ROOT/cheat-default.txt"
grep -Fq 'cl / cc native s / S substitute' "$TMP_ROOT/cheat-default.txt"
grep -Fq ':ChopsticksTutor practice' "$TMP_ROOT/cheat-default.txt"
if grep -Eq 'Ctrl\\+p find file|Ctrl\\+hjkl navigate splits|Ctrl\\+s save|jk exit insert|SPC fs save|SPC cd definition|SPC ck hover|SPC wm|SPC w\\+/-|\\[g \\]g LSP diagnostics' "$TMP_ROOT/cheat-default.txt"; then
cat "$TMP_ROOT/cheat-default.txt"
exit 1
fi
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
-c 'ChopsticksCheatSheet' \
-c "redir! > $TMP_ROOT/cheat-command.txt" \
-c 'silent %print' \
-c 'redir END' \
-c 'qa!' 2>&1
grep -Fq 'SPC SPC files' "$TMP_ROOT/cheat-command.txt"
grep -Fq ':ChopsticksTutor practice' "$TMP_ROOT/cheat-command.txt"
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u NONE -i NONE -es -N \
-c 'let g:chopsticks_keymap_style = "classic"' \
-c 'source .vimrc' \
-c 'normal ,?' \
-c "redir! > $TMP_ROOT/cheat-classic.txt" \
-c 'silent %print' \
-c 'redir END' \
-c 'qa!' 2>&1
grep -Fq ',ff files' "$TMP_ROOT/cheat-classic.txt"
grep -Fq ',dd definition' "$TMP_ROOT/cheat-classic.txt"
grep -Fq ',dk hover docs' "$TMP_ROOT/cheat-classic.txt"
grep -Fq ',dp ,dn LSP diagnostics' "$TMP_ROOT/cheat-classic.txt"
if grep -Eq ',gp push|,gl pull' "$TMP_ROOT/cheat-classic.txt"; then
cat "$TMP_ROOT/cheat-classic.txt"
exit 1
fi
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u NONE -i NONE -es -N \
-c 'let g:chopsticks_profile = "minimal"' \
-c 'source .vimrc' \
-c 'call feedkeys("\<Space>?", "xt")' \
-c "redir! > $TMP_ROOT/cheat.txt" \
-c 'silent %print' \
-c 'redir END' \
-c 'qa!' 2>&1
if grep -Eq 'definition|LspInstallServer|ALE errors|undo tree|markdown preview' "$TMP_ROOT/cheat.txt"; then
cat "$TMP_ROOT/cheat.txt"
exit 1
fi
grep -q 'SPC rr run file' "$TMP_ROOT/cheat.txt"
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u NONE -i NONE -es -N \
-c 'let g:chopsticks_keymap_style = "space"' \
-c 'source .vimrc' \
-c 'call feedkeys("\<Space>?", "xt")' \
-c "redir! > $TMP_ROOT/cheat-space.txt" \
-c 'silent %print' \
-c 'redir END' \
-c 'qa!' 2>&1
grep -Fq 'SPC w save' "$TMP_ROOT/cheat-space.txt"
grep -Fq 'gd definition' "$TMP_ROOT/cheat-space.txt"
grep -Fq 'SPC gl log graph' "$TMP_ROOT/cheat-space.txt"
grep -Fq 's+2ch easymotion jump' "$TMP_ROOT/cheat-space.txt"
if grep -Eq ',w save|,gp push|SPC gp push|SPC gl pull|SPC fs save|SPC cd definition|SPC f format' "$TMP_ROOT/cheat-space.txt"; then
cat "$TMP_ROOT/cheat-space.txt"
exit 1
fi
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
-c 'ChopsticksTutor' \
-c "redir! > $TMP_ROOT/tutor-default.txt" \
-c 'silent %print' \
-c 'redir END' \
-c 'qa!' 2>&1
grep -Fq 'chopsticks tutor' "$TMP_ROOT/tutor-default.txt"
grep -Fq 'SPC ? active cheat sheet' "$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"
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u NONE -i NONE -es -N \
-c 'let g:chopsticks_keymap_style = "classic"' \
-c 'source .vimrc' \
-c 'ChopsticksTutor' \
-c "redir! > $TMP_ROOT/tutor-classic.txt" \
-c 'silent %print' \
-c 'redir END' \
-c 'qa!' 2>&1
grep -Fq 'classic layout' "$TMP_ROOT/tutor-classic.txt"
grep -Fq ',? active cheat sheet' "$TMP_ROOT/tutor-classic.txt"
grep -Fq ',S + 2 chars EasyMotion jump' "$TMP_ROOT/tutor-classic.txt"
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u NONE -i NONE -es -N README.md \
-c 'let g:chopsticks_keymap_style = "space"' \
-c 'source .vimrc' \
-c 'set filetype=markdown' \
-c 'if maparg(",mt", "n") !~# "Toc" || maparg(",mp", "n") !~# "PrevimOpen" | cquit | endif' \
-c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N README.md \
-c 'set filetype=markdown' \
-c 'if &l:spell || &l:conceallevel != 0 || &l:signcolumn !=# "no" || exists("g:lsp_settings_filetype_markdown") | cquit | endif' \
-c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
-c 'if maparg("s", "n") !~# "easymotion-overwin-f2" | cquit | endif' \
-c 'if maparg("<Space>w", "n") =~# "!" | cquit | endif' \
-c 'if !&swapfile || !&writebackup || &directory !~# "\.vim/.swap" | cquit | endif' \
-c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u NONE -i NONE -es -N \
-c 'let g:ale_fix_on_save = 0' \
-c 'source .vimrc' \
-c 'if g:ale_fix_on_save != 0 | cquit | endif' \
-c 'qa!' 2>&1
truncate -s 11000000 "$TMP_ROOT/large.py"
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N "$TMP_ROOT/large.py" \
-c 'set filetype=python' \
-c 'if &l:syntax !=# "" || &l:undolevels != -1 || &l:swapfile || get(b:, "ale_enabled", 1) != 0 | cquit | endif' \
-c 'qa!' 2>&1
mkdir -p "$TMP_ROOT/fake-bin" "$TMP_ROOT/c runner"
cat > "$TMP_ROOT/fake-bin/gcc" <<'GCCEOF'
#!/usr/bin/env bash
set -eu
printf '%s\n' "$@" > "$GCC_ARGS"
out=""
while [ "$#" -gt 0 ]; do
if [ "$1" = "-o" ]; then
shift
out="$1"
fi
shift || true
done
test -n "$out"
printf '%s\n' '#!/usr/bin/env bash' 'exit 0' > "$out"
chmod +x "$out"
GCCEOF
chmod +x "$TMP_ROOT/fake-bin/gcc"
c_file="$TMP_ROOT/c runner/main.c"
c_file_real="$(cd "$TMP_ROOT/c runner" && pwd -P)/main.c"
printf '%s\n' 'int main(void) { return 0; }' > "$c_file"
GCC_ARGS="$TMP_ROOT/gcc-args.txt" \
PATH="$TMP_ROOT/fake-bin:$PATH" \
XDG_CONFIG_HOME="$EMPTY_XDG" \
vim -u .vimrc -i NONE -es -N "$c_file" \
-c 'set filetype=c' \
-c 'call feedkeys("\<Space>rr", "xt")' \
-c 'qa!' 2>&1
c_out="$(sed -n '2p' "$TMP_ROOT/gcc-args.txt")"
test -n "$c_out"
test "$c_out" != "/tmp/a.out"
test ! -e "$c_out"
grep -Fxq "$c_file_real" "$TMP_ROOT/gcc-args.txt"
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE --startuptime "$TMP_ROOT/startup.log" \
-es -N -c 'qa!' 2>/dev/null
tail -1 "$TMP_ROOT/startup.log"
STARTUP_MS="$(awk 'END { print $1 }' "$TMP_ROOT/startup.log")"
awk -v ms="$STARTUP_MS" -v limit="$STARTUP_LIMIT_MS" \
'BEGIN { if (ms > limit) exit 1 }'
}
check_vim

View file

@ -1,9 +1,31 @@
#!/usr/bin/env bash
# Project test runner. CI calls the same groups maintainers can run locally.
# Project test runner. CI calls the same groups that maintainers can run locally.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
TMP_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/chopsticks-test-XXXXXX")"
EMPTY_XDG="$TMP_ROOT/xdg-empty"
STARTUP_LIMIT_MS="${STARTUP_LIMIT_MS:-150}"
cleanup() {
rm -rf "$TMP_ROOT"
}
trap cleanup EXIT
cd "$ROOT"
mkdir -p "$EMPTY_XDG"
step() {
printf '\n==> %s\n' "$1"
}
need() {
command -v "$1" >/dev/null 2>&1 || {
echo "Missing required command: $1" >&2
exit 1
}
}
usage() {
cat <<'EOF'
@ -28,17 +50,385 @@ list_groups() {
printf '%s\n' quick shell docs installer bootstrap vim all
}
check_shell() {
step "Shell syntax and lint"
need bash
bash -n install.sh
bash -n get.sh
bash -n scripts/test.sh
test -x install.sh
test -x get.sh
test -x scripts/test.sh
need shellcheck
shellcheck install.sh get.sh scripts/test.sh
}
check_docs() {
step "Markdown lint"
need markdownlint
markdownlint README.md QUICKSTART.md CONTRIBUTING.md CHANGELOG.md
}
check_installer_modes() {
step "Installer profile-only modes"
XDG_CONFIG_HOME="$TMP_ROOT/dry" ./install.sh --dry-run --profile=full \
| tee "$TMP_ROOT/install-dry-run.txt"
grep -q 'Profile: full' "$TMP_ROOT/install-dry-run.txt"
test ! -e "$TMP_ROOT/dry/chopsticks.vim"
XDG_CONFIG_HOME="$TMP_ROOT/config" ./install.sh --configure-only --profile=minimal
grep -q "let g:chopsticks_profile = 'minimal'" "$TMP_ROOT/config/chopsticks.vim"
XDG_CONFIG_HOME="$TMP_ROOT/config" ./install.sh --configure-only --profile=full
grep -q "let g:chopsticks_profile = 'full'" "$TMP_ROOT/config/chopsticks.vim"
XDG_CONFIG_HOME="$TMP_ROOT/default" ./install.sh --configure-only --yes
grep -q "let g:chopsticks_profile = 'engineer'" "$TMP_ROOT/default/chopsticks.vim"
}
check_bootstrap() {
step "Bootstrap dry-run safety"
CHOPSTICKS_DEST="$TMP_ROOT/bootstrap" ./get.sh --dry-run --profile=minimal \
| tee "$TMP_ROOT/get-dry-run.txt"
grep -q 'Would clone' "$TMP_ROOT/get-dry-run.txt"
test ! -e "$TMP_ROOT/bootstrap"
mkdir -p "$TMP_ROOT/no-git-bin"
printf '%s\n' \
'#!/usr/bin/env bash' \
"echo \"brew was called\" >> \"\$BREW_LOG\"" \
'exit 42' > "$TMP_ROOT/no-git-bin/brew"
chmod +x "$TMP_ROOT/no-git-bin/brew"
BREW_LOG="$TMP_ROOT/no-git-brew.log" \
PATH="$TMP_ROOT/no-git-bin" \
CHOPSTICKS_DEST="$TMP_ROOT/no-git-bootstrap" \
/bin/bash ./get.sh --dry-run --profile=full \
| tee "$TMP_ROOT/get-no-git-dry-run.txt"
grep -q 'Would require: git' "$TMP_ROOT/get-no-git-dry-run.txt"
grep -q 'Would clone' "$TMP_ROOT/get-no-git-dry-run.txt"
test ! -e "$TMP_ROOT/no-git-brew.log"
test ! -e "$TMP_ROOT/no-git-bootstrap"
mkdir -p "$TMP_ROOT/not-chopsticks"
git -c init.defaultBranch=main init "$TMP_ROOT/not-chopsticks" >/dev/null
git -C "$TMP_ROOT/not-chopsticks" remote add origin https://github.com/example/not-chopsticks.git
if CHOPSTICKS_DEST="$TMP_ROOT/not-chopsticks" ./get.sh --dry-run; then
echo "Expected get.sh to reject non-chopsticks repo" >&2
exit 1
fi
mkdir -p "$TMP_ROOT/chopsticks-existing"
git -c init.defaultBranch=main init "$TMP_ROOT/chopsticks-existing" >/dev/null
git -C "$TMP_ROOT/chopsticks-existing" remote add origin https://github.com/m1ngsama/chopsticks.git
touch "$TMP_ROOT/chopsticks-existing/install.sh" "$TMP_ROOT/chopsticks-existing/.vimrc"
CHOPSTICKS_DEST="$TMP_ROOT/chopsticks-existing" ./get.sh --dry-run --yes \
| tee "$TMP_ROOT/get-existing.txt"
grep -q 'Would update existing chopsticks repo' "$TMP_ROOT/get-existing.txt"
}
check_plugin_dirs() {
step "Plugin directories"
for plugin in \
fzf fzf.vim vim-fugitive vim-gitgutter ale vim-lsp vim-lsp-settings \
asyncomplete.vim asyncomplete-lsp.vim vim-markdown
do
test -d "$HOME/.vim/plugged/$plugin" || {
echo "Missing plugin directory: $plugin" >&2
exit 1
}
done
}
check_vim() {
step "Vim smoke tests"
need vim
check_plugin_dirs
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N -c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
-c "redir! > $TMP_ROOT/plugs.txt" \
-c 'silent echo len(g:plugs)' \
-c 'redir END' \
-c 'qa!' 2>/dev/null
PLUGS="$(tr -d '[:space:]' < "$TMP_ROOT/plugs.txt")"
echo "Plugins registered: $PLUGS"
if [ "$PLUGS" -lt 20 ]; then
echo "Expected 20+ plugins, got $PLUGS" >&2
exit 1
fi
mkdir -p "$TMP_ROOT/chopsticks path/modules"
cp .vimrc "$TMP_ROOT/chopsticks path/.vimrc"
cp modules/*.vim "$TMP_ROOT/chopsticks path/modules/"
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u "$TMP_ROOT/chopsticks path/.vimrc" \
-i NONE -es -N -c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u NONE -i NONE -es -N \
-c 'let g:chopsticks_profile = "minimal"' \
-c 'source .vimrc' \
-c 'if has_key(g:plugs, "ale") || has_key(g:plugs, "vim-lsp") || has_key(g:plugs, "vim-lsp-settings") || has_key(g:plugs, "asyncomplete.vim") || has_key(g:plugs, "auto-pairs") | cquit | endif' \
-c 'qa!' 2>&1
mkdir -p "$TMP_ROOT/local"
printf "%s\n" "let g:chopsticks_profile = 'minimal'" > "$TMP_ROOT/local/config.vim"
vim -u NONE -i NONE -es -N \
-c "let g:chopsticks_local_config = '$TMP_ROOT/local/config.vim'" \
-c 'source .vimrc' \
-c 'if g:chopsticks_profile !=# "minimal" || has_key(g:plugs, "ale") || has_key(g:plugs, "vim-lsp") || has_key(g:plugs, "auto-pairs") | cquit | endif' \
-c 'qa!' 2>&1
mkdir -p "$TMP_ROOT/xdg"
printf "%s\n" "let g:chopsticks_profile = 'minimal'" > "$TMP_ROOT/xdg/chopsticks.vim"
XDG_CONFIG_HOME="$TMP_ROOT/xdg" vim -u NONE -i NONE -es -N \
-c 'source .vimrc' \
-c 'if g:chopsticks_profile !=# "minimal" || has_key(g:plugs, "ale") || has_key(g:plugs, "vim-lsp") || has_key(g:plugs, "auto-pairs") | cquit | endif' \
-c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
-c 'ChopsticksStatus' \
-c "redir! > $TMP_ROOT/status-default.txt" \
-c 'silent %print' \
-c 'redir END' \
-c 'qa!' 2>&1
if grep -Fq 'vim-lsp not loaded' "$TMP_ROOT/status-default.txt"; then
cat "$TMP_ROOT/status-default.txt"
exit 1
fi
grep -Fq 'OK vim-lsp stack (installed)' "$TMP_ROOT/status-default.txt"
grep -Fq 'python (:LspInstallServer in a python file)' "$TMP_ROOT/status-default.txt"
grep -Fq 'LSP actions are buffer-local and start after a server attaches.' "$TMP_ROOT/status-default.txt"
grep -Fq 'Open that filetype and run :LspInstallServer once.' "$TMP_ROOT/status-default.txt"
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 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(",/", "v") !~# "escape" || maparg(",v", "n") !=# last_change_map || maparg(",ff", "n") !~# "SmartFiles" | cquit | endif' \
-c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u NONE -i NONE -es -N \
-c 'let g:chopsticks_enable_jk_escape = 1' \
-c 'source .vimrc' \
-c 'if maparg("jk", "i") !~# "<Esc>" | cquit | endif' \
-c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u NONE -i NONE -es -N \
-c 'let g:chopsticks_enable_ctrl_s_save = 1' \
-c 'let g:chopsticks_enable_sudo_save_bang = 1' \
-c 'let g:chopsticks_enable_completion_keymaps = 1' \
-c 'source .vimrc' \
-c 'if maparg("<C-s>", "n") !~# ":w" || maparg("<C-s>", "i") !~# ":w" || maparg("w!!", "c") !~# "sudo tee" | cquit | endif' \
-c 'if maparg("<Tab>", "i") !~# "pumvisible" || maparg("<S-Tab>", "i") !~# "pumvisible" || maparg("<CR>", "i") !~# "asyncomplete#close_popup" | cquit | endif' \
-c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u NONE -i NONE -es -N \
-c 'let g:chopsticks_enable_auto_pairs = 1' \
-c 'source .vimrc' \
-c 'if !has_key(g:plugs, "auto-pairs") | cquit | endif' \
-c 'qa!' 2>&1
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 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
-c 'if &exrc || &secure | cquit | endif' \
-c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u NONE -i NONE -es -N \
-c 'let g:chopsticks_enable_exrc = 1' \
-c 'source .vimrc' \
-c 'if !&exrc || !&secure | cquit | endif' \
-c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
-c 'if maparg(",F", "n") !=# "" | cquit | endif' \
-c 'if maparg(",F", "v") !~# "=" | cquit | endif' \
-c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u NONE -i NONE -es -N \
-c 'let g:chopsticks_enable_reindent_file = 1' \
-c 'source .vimrc' \
-c 'if maparg(",F", "n") !~# "gg=G" | cquit | endif' \
-c 'qa!' 2>&1
TERM=xterm-256color XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
-c 'if g:is_tty || &ttimeoutlen != 10 | cquit | endif' \
-c 'qa!' 2>&1
TERM=linux XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
-c 'if !g:is_tty || &ttimeoutlen != 50 | cquit | endif' \
-c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
-c 'if !exists("loaded_gzip") || !exists("loaded_logiPat") || !exists("loaded_rrhelper") || !exists("loaded_spellfile_plugin") | cquit | endif' \
-c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
-c 'silent! delcommand LspStatus' \
-c 'silent! delcommand LspInstallServer' \
-c 'ChopsticksStatus' \
-c "redir! > $TMP_ROOT/status-lsp-not-loaded.txt" \
-c 'silent %print' \
-c 'redir END' \
-c 'qa!' 2>&1
grep -Fq 'OK vim-lsp stack (installed; not loaded yet)' "$TMP_ROOT/status-lsp-not-loaded.txt"
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u NONE -i NONE -es -N \
-c 'let g:chopsticks_profile = "minimal"' \
-c 'source .vimrc' \
-c 'ChopsticksStatus' \
-c "redir! > $TMP_ROOT/status-minimal.txt" \
-c 'silent %print' \
-c 'redir END' \
-c 'qa!' 2>&1
grep -Fq 'off vim-lsp stack (LSP disabled by profile)' "$TMP_ROOT/status-minimal.txt"
grep -Fq 'off python (LSP disabled by profile)' "$TMP_ROOT/status-minimal.txt"
if grep -Fq 'LSP actions are buffer-local' "$TMP_ROOT/status-minimal.txt"; then
cat "$TMP_ROOT/status-minimal.txt"
exit 1
fi
mkdir -p "$TMP_ROOT/missing-home/.vim/autoload"
cp "$HOME/.vim/autoload/plug.vim" "$TMP_ROOT/missing-home/.vim/autoload/plug.vim"
HOME="$TMP_ROOT/missing-home" XDG_CONFIG_HOME="$EMPTY_XDG" \
vim -u .vimrc -i NONE -es -N \
-c 'ChopsticksStatus' \
-c "redir! > $TMP_ROOT/status-missing-plugin.txt" \
-c 'silent %print' \
-c 'redir END' \
-c 'qa!' 2>&1
grep -Fq 'vim-lsp not installed; run :PlugInstall' "$TMP_ROOT/status-missing-plugin.txt"
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
-c 'doautocmd User lsp_buffer_enabled' \
-c 'if maparg("gd", "n") !=# "" || maparg("K", "n") !=# "" || maparg("gi", "n") !=# "" || maparg("gr", "n") !=# "" | cquit | endif' \
-c 'if maparg(",dd", "n") !~# "lsp-definition" | cquit | endif' \
-c 'if maparg(",dt", "n") !~# "lsp-type-definition" | cquit | endif' \
-c 'if maparg(",di", "n") !~# "lsp-implementation" | cquit | endif' \
-c 'if maparg(",dr", "n") !~# "lsp-references" | cquit | endif' \
-c 'if maparg(",dk", "n") !~# "lsp-hover" | cquit | endif' \
-c 'if maparg(",dp", "n") !~# "lsp-previous-diagnostic" | cquit | endif' \
-c 'if maparg(",dn", "n") !~# "lsp-next-diagnostic" | cquit | endif' \
-c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
-c 'normal ,?' \
-c "redir! > $TMP_ROOT/cheat-default.txt" \
-c 'silent %print' \
-c 'redir END' \
-c 'qa!' 2>&1
grep -Fq ':ChopsticksStatus check LSP setup' "$TMP_ROOT/cheat-default.txt"
grep -Fq ',ff files' "$TMP_ROOT/cheat-default.txt"
grep -Fq ',dd definition' "$TMP_ROOT/cheat-default.txt"
grep -Fq ',dk hover docs' "$TMP_ROOT/cheat-default.txt"
grep -Fq ',dp ,dn LSP diagnostics' "$TMP_ROOT/cheat-default.txt"
grep -Fq '<C-w>hjkl navigate splits' "$TMP_ROOT/cheat-default.txt"
if grep -Eq 'Ctrl\\+p find file|Ctrl\\+hjkl navigate splits|Ctrl\\+s save|jk exit insert|gd definition|K hover docs|\\[g \\]g LSP diagnostics' "$TMP_ROOT/cheat-default.txt"; then
cat "$TMP_ROOT/cheat-default.txt"
exit 1
fi
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u NONE -i NONE -es -N \
-c 'let g:chopsticks_profile = "minimal"' \
-c 'source .vimrc' \
-c 'normal ,?' \
-c "redir! > $TMP_ROOT/cheat.txt" \
-c 'silent %print' \
-c 'redir END' \
-c 'qa!' 2>&1
if grep -Eq 'definition|LspInstallServer|ALE errors|undo tree|markdown preview' "$TMP_ROOT/cheat.txt"; then
cat "$TMP_ROOT/cheat.txt"
exit 1
fi
grep -q ',cr run file' "$TMP_ROOT/cheat.txt"
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N README.md \
-c 'set filetype=markdown' \
-c 'if &l:spell || &l:conceallevel != 0 || &l:signcolumn !=# "no" || exists("g:lsp_settings_filetype_markdown") | cquit | endif' \
-c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
-c 'if maparg("s", "n") !=# "" | cquit | endif' \
-c 'if maparg(",w", "n") =~# "!" | cquit | endif' \
-c 'if !&swapfile || !&writebackup || &directory !~# "\.vim/.swap" | cquit | endif' \
-c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u NONE -i NONE -es -N \
-c 'let g:ale_fix_on_save = 0' \
-c 'source .vimrc' \
-c 'if g:ale_fix_on_save != 0 | cquit | endif' \
-c 'qa!' 2>&1
truncate -s 11000000 "$TMP_ROOT/large.py"
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N "$TMP_ROOT/large.py" \
-c 'set filetype=python' \
-c 'if &l:syntax !=# "" || &l:undolevels != -1 || &l:swapfile || get(b:, "ale_enabled", 1) != 0 | cquit | endif' \
-c 'qa!' 2>&1
mkdir -p "$TMP_ROOT/fake-bin" "$TMP_ROOT/c runner"
cat > "$TMP_ROOT/fake-bin/gcc" <<'GCCEOF'
#!/usr/bin/env bash
set -eu
printf '%s\n' "$@" > "$GCC_ARGS"
out=""
while [ "$#" -gt 0 ]; do
if [ "$1" = "-o" ]; then
shift
out="$1"
fi
shift || true
done
test -n "$out"
printf '%s\n' '#!/usr/bin/env bash' 'exit 0' > "$out"
chmod +x "$out"
GCCEOF
chmod +x "$TMP_ROOT/fake-bin/gcc"
c_file="$TMP_ROOT/c runner/main.c"
c_file_real="$(cd "$TMP_ROOT/c runner" && pwd -P)/main.c"
printf '%s\n' 'int main(void) { return 0; }' > "$c_file"
GCC_ARGS="$TMP_ROOT/gcc-args.txt" \
PATH="$TMP_ROOT/fake-bin:$PATH" \
XDG_CONFIG_HOME="$EMPTY_XDG" \
vim -u .vimrc -i NONE -es -N "$c_file" \
-c 'set filetype=c' \
-c 'normal ,cr' \
-c 'qa!' 2>&1
c_out="$(sed -n '2p' "$TMP_ROOT/gcc-args.txt")"
test -n "$c_out"
test "$c_out" != "/tmp/a.out"
test ! -e "$c_out"
grep -Fxq "$c_file_real" "$TMP_ROOT/gcc-args.txt"
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE --startuptime "$TMP_ROOT/startup.log" \
-es -N -c 'qa!' 2>/dev/null
tail -1 "$TMP_ROOT/startup.log"
STARTUP_MS="$(awk 'END { print $1 }' "$TMP_ROOT/startup.log")"
awk -v ms="$STARTUP_MS" -v limit="$STARTUP_LIMIT_MS" \
'BEGIN { if (ms > limit) exit 1 }'
}
run_group() {
case "$1" in
quick | shell | docs | installer | bootstrap)
bash "$SCRIPT_DIR/test-quick.sh" "$1"
;;
vim)
bash "$SCRIPT_DIR/test-vim.sh"
quick)
check_shell
check_docs
check_installer_modes
check_bootstrap
;;
shell) check_shell ;;
docs) check_docs ;;
installer) check_installer_modes ;;
bootstrap) check_bootstrap ;;
vim) check_vim ;;
all)
bash "$SCRIPT_DIR/test-quick.sh" quick
bash "$SCRIPT_DIR/test-vim.sh"
run_group quick
check_vim
;;
list | --list) list_groups ;;
-h | --help) usage ;;