Compare commits

...

4 commits

Author SHA1 Message Date
9442aa0499 ux: dashboard-style startup — fullscreen Startify, m1ngsama header
- Remove NERDTree auto-open on bare `vim` — Startify now renders
  fullscreen like neovim's snacks dashboard (use Ctrl+n for tree)
- `vim .` keeps NERDTree left + Startify right (directory workflow)
- Replace minimal text header with centered m1ngsama ASCII block art
  (mirrors neovim config aesthetic)
- Center art and info subheader dynamically based on window width
- Add startify_padding_left = 4 for list item breathing room
- Reorder lists: Sessions → Recent Files → Current Dir → Bookmarks
2026-03-29 19:54:44 +08:00
3236155b84 docs: comprehensive v1.0.0 documentation update
- README: full rewrite — updated language table (staticcheck, yamllint,
  hadolint, marksman via coc-settings.json), install.sh platform coverage,
  removed coc-marksman references, added CHANGELOG link
- QUICKSTART: rewrite to reflect automated install.sh, updated language
  workflows (staticcheck for Go, marksman for Markdown)
- CHANGELOG: new file documenting v1.0.0, v0.9.0, and v0.1.0 releases
- .gitignore: add *.swp, *.swo, .DS_Store, Session.vim
2026-03-29 18:05:51 +08:00
3daf725ad8
audit: wrap loose autocmds in augroups, fix install.sh robustness (#13)
.vimrc:
- Wrap all loose autocmds in named augroups with autocmd! (prevents
  doubling on :source $MYVIMRC): ChopstickTabHistory, ChopstickResize,
  ChopstickStdin, CocHighlight, ChopstickCleanup, ChopstickFiletype,
  ChopstickTTYLargeFile, ChopstickWhichKey, ChopstickStartify

install.sh:
- Add Arch Linux (pacman) branch for system tools
- Add hadolint to system tools (brew/apt binary download/pacman)
- Add staticcheck to Go tools
- Add yamllint to pip tools
- Remove sqlfmt from npm (SQL unified to sqlfluff via pip)
- Remove coc-marksman (package does not exist on npm)
- Add coc-settings.json symlink step with backup
- Add pip3 bootstrap when python3 present but pip3 absent
- Fix PlugInstall and CocInstall to use </dev/null (TTY-safe)

coc-settings.json:
- New file: configures marksman as Markdown LSP server for CoC
  (replaces the broken coc-marksman npm package approach)
2026-03-29 16:36:43 +08:00
db16c2f8fc
fix: vim-go startup hang, E495 errors, and afile guard safety (#12)
vim-go / install hang:
- Remove { 'do': ':GoUpdateBinaries' } from vim-go Plug declaration.
  This hook downloads gopls, dlv, gotest and ~10 other binaries during
  PlugInstall, causing multi-minute hangs that look like vim is frozen.
  Run :GoUpdateBinaries manually if Go debugging tools are needed.

vim-go / CoC conflict:
- Add g:go_gopls_enabled = 0 — disables vim-go's own gopls client.
  Without this, vim-go registers a BufWinEnter * autocmd that calls
  go#lsp#DidOpen(expand('<afile>:p')), which fires on NERDTree/Startify
  buffers (no file name), triggers E495, and makes vim wait for Enter.
- g:go_fmt_autosave = 0, g:go_imports_autosave = 0 — CoC/ALE handle
  format-on-save; vim-go's auto-save would double-format.
- g:go_code_completion_enabled = 0 — CoC handles completion.
- Keep vim-go syntax highlights (go_highlight_*) which CoC doesn't provide.

<afile> guard (E495 hardening):
- BWCCreateDir (BufWritePre): wrap in !empty(expand('<afile>')) guard.
- LargeFile (BufReadPre): same guard — special buffers have no file name.
- CleanExtraSpaces (BufWritePre): guard with empty(&buftype) in addition,
  so it only runs for normal file buffers, not terminals or help windows.
- TTY BufReadPre size check: same guard.
2026-03-29 12:22:21 +08:00
7 changed files with 647 additions and 372 deletions

5
.gitignore vendored
View file

@ -1 +1,6 @@
*.json
!coc-settings.json
*.swp
*.swo
.DS_Store
Session.vim

192
.vimrc
View file

@ -181,7 +181,7 @@ Plug 'dense-analysis/ale' " Async linting engine
" ===== Language Support =====
Plug 'sheerun/vim-polyglot' " Language pack
Plug 'fatih/vim-go', { 'do': ':GoUpdateBinaries' } " Go support
Plug 'fatih/vim-go' " Go support (run :GoUpdateBinaries manually if needed)
" ===== Color Schemes =====
Plug 'morhetz/gruvbox' " Gruvbox theme
@ -235,6 +235,14 @@ call plug#end()
let g:use_coc = (has('nvim') || has('patch-8.0.1453')) && executable('node') && exists('g:plugs["coc.nvim"]')
let g:use_vimlsp = !g:use_coc && has('patch-8.0.0') && exists('g:plugs["vim-lsp"]')
" Suppress coc.nvim's blocking startup warning on Vim < 9.0.0438
" (the guard above already prevents coc from loading, but the warning
" fires from the plugin file itself if coc.nvim is in runtimepath)
if !g:use_coc
let g:coc_start_at_startup = 0
let g:coc_disable_startup_warning = 1
endif
" Limit popup menu height (applies to all completion)
set pumheight=15
@ -343,7 +351,10 @@ map <leader>t<leader> :tabnext<cr>
" Let 'tl' toggle between this and the last accessed tab
let g:lasttab = 1
nmap <Leader>tl :exe "tabn ".g:lasttab<CR>
au TabLeave * let g:lasttab = tabpagenr()
augroup ChopstickTabHistory
autocmd!
autocmd TabLeave * let g:lasttab = tabpagenr()
augroup END
" Opens a new tab with the current buffer's path
map <leader>te :tabedit <C-r>=expand("%:p:h")<cr>/
@ -411,7 +422,10 @@ nnoremap <leader>qo :copen<CR>
nnoremap <leader>qc :cclose<CR>
" Auto-equalize splits when terminal window is resized
autocmd VimResized * wincmd =
augroup ChopstickResize
autocmd!
autocmd VimResized * wincmd =
augroup END
" ============================================================================
" => Plugin Settings
@ -437,7 +451,10 @@ let NERDTreeIgnore=['\.pyc$', '\~$', '\.swp$', '\.git$', '\.DS_Store', 'node_mod
let NERDTreeWinSize=35
" Track stdin reads so startup autocmds can skip pipe/heredoc input
autocmd StdinReadPre * let s:std_in=1
augroup ChopstickStdin
autocmd!
autocmd StdinReadPre * let s:std_in=1
augroup END
" Startup layout (non-TTY only — keeps TTY startup instant)
if !g:is_tty
@ -451,12 +468,8 @@ if !g:is_tty
\ wincmd p |
\ if exists(':Startify') == 2 | Startify | else | enew | endif |
\ endif
" vim (no args) → Startify renders first; open NERDTree alongside it
autocmd User Startified
\ if argc() == 0 && !exists('s:std_in') |
\ NERDTree |
\ wincmd p |
\ endif
" vim (no args) → Startify fullscreen dashboard, no auto NERDTree
" Use Ctrl+n to open NERDTree when needed
augroup END
endif
@ -538,7 +551,7 @@ let g:ale_linters = {
\ 'python': ['flake8', 'pylint'],
\ 'javascript': ['eslint'],
\ 'typescript': ['eslint', 'tsserver'],
\ 'go': ['gopls', 'golint'],
\ 'go': ['gopls', 'staticcheck'],
\ 'rust': ['cargo'],
\ 'sh': ['shellcheck'],
\ 'yaml': ['yamllint'],
@ -563,7 +576,7 @@ let g:ale_fixers = {
\ 'scss': ['prettier'],
\ 'less': ['prettier'],
\ 'markdown': ['prettier'],
\ 'sql': ['sqlfmt'],
\ 'sql': ['sqlfluff'],
\}
" Don't fix on save if LSP is handling formatting (avoids double-format)
@ -574,6 +587,21 @@ let g:ale_lint_on_text_changed = 'never'
let g:ale_lint_on_insert_leave = 0
let g:ale_lint_on_enter = 0
" --- vim-go: disable built-in LSP/gopls — CoC (coc-go) handles all Go intelligence ---
" vim-go's gopls conflicts with coc-go and causes E495 errors on startup
" (BufWinEnter afile expand fails for non-file buffers like NERDTree/Startify)
let g:go_gopls_enabled = 0 " disable vim-go's own gopls — coc-go handles LSP
let g:go_code_completion_enabled = 0 " let CoC handle completion
" Use godef as fallback for jump-to-def when CoC unavailable; gopls+disabled = error
let g:go_def_mode = g:use_coc ? 'gopls' : 'godef'
let g:go_info_mode = g:use_coc ? 'gopls' : 'godef'
let g:go_fmt_autosave = 0 " CoC/ALE handle format-on-save
let g:go_imports_autosave = 0
let g:go_highlight_types = 1 " keep syntax features
let g:go_highlight_fields = 1
let g:go_highlight_functions = 1
let g:go_highlight_function_calls = 1
" Navigate between errors: [e/]e (unimpaired style), <leader>aD for detail
nmap <silent> [e :ALENext<cr>
nmap <silent> ]e :ALEPrevious<cr>
@ -641,7 +669,10 @@ if g:use_coc
endfunction
" Highlight symbol and its references on cursor hold
autocmd CursorHold * silent call CocActionAsync('highlight')
augroup CocHighlight
autocmd!
autocmd CursorHold * silent call CocActionAsync('highlight')
augroup END
" Symbol renaming
nmap <leader>rn <Plug>(coc-rename)
@ -818,54 +849,58 @@ fun! CleanExtraSpaces()
call setreg('/', old_query)
endfun
if has("autocmd")
" Run for all files; ALE trim_whitespace is idempotent so no conflict
autocmd BufWritePre * call CleanExtraSpaces()
endif
augroup ChopstickCleanup
autocmd!
" Run for real files only; skip special buffers (NERDTree, Startify, terminal, etc.)
autocmd BufWritePre * if empty(&buftype) && !empty(expand('<afile>')) | call CleanExtraSpaces() | endif
augroup END
" ============================================================================
" => Auto Commands
" ============================================================================
" Return to last edit position when opening files
autocmd BufReadPost * if line("'\"") > 1 && line("'\"") <= line("$") | exe "normal! g'\"" | endif
augroup ChopstickFiletype
autocmd!
" Return to last edit position when opening files
autocmd BufReadPost * if line("'\"") > 1 && line("'\"") <= line("$") | exe "normal! g'\"" | endif
" Set specific file types
autocmd BufNewFile,BufRead *.json setlocal filetype=json
autocmd BufNewFile,BufRead *.md setlocal filetype=markdown
autocmd BufNewFile,BufRead *.jsx setlocal filetype=javascript.jsx
autocmd BufNewFile,BufRead *.tsx setlocal filetype=typescript.tsx
" Set specific file types
autocmd BufNewFile,BufRead *.json setlocal filetype=json
autocmd BufNewFile,BufRead *.md setlocal filetype=markdown
autocmd BufNewFile,BufRead *.jsx setlocal filetype=javascript.jsx
autocmd BufNewFile,BufRead *.tsx setlocal filetype=typescript.tsx
" Python specific settings
autocmd FileType python setlocal expandtab shiftwidth=4 tabstop=4 colorcolumn=88
" Python specific settings
autocmd FileType python setlocal expandtab shiftwidth=4 tabstop=4 colorcolumn=88
" JavaScript specific settings
autocmd FileType javascript,typescript setlocal expandtab shiftwidth=2 tabstop=2
" JavaScript specific settings
autocmd FileType javascript,typescript setlocal expandtab shiftwidth=2 tabstop=2
" Go specific settings
autocmd FileType go setlocal noexpandtab shiftwidth=4 tabstop=4
" Go specific settings
autocmd FileType go setlocal noexpandtab shiftwidth=4 tabstop=4
" HTML/CSS specific settings
autocmd FileType html,css setlocal expandtab shiftwidth=2 tabstop=2
" HTML/CSS specific settings
autocmd FileType html,css setlocal expandtab shiftwidth=2 tabstop=2
" YAML specific settings
autocmd FileType yaml setlocal expandtab shiftwidth=2 tabstop=2
" YAML specific settings
autocmd FileType yaml setlocal expandtab shiftwidth=2 tabstop=2
" Markdown specific settings
autocmd FileType markdown setlocal wrap linebreak spell tw=0
" Markdown specific settings
autocmd FileType markdown setlocal wrap linebreak spell tw=0
" Shell script settings
autocmd FileType sh setlocal expandtab shiftwidth=2 tabstop=2
" Shell script settings
autocmd FileType sh setlocal expandtab shiftwidth=2 tabstop=2
" Makefile settings (must use tabs)
autocmd FileType make setlocal noexpandtab shiftwidth=8 tabstop=8
" Makefile settings (must use tabs)
autocmd FileType make setlocal noexpandtab shiftwidth=8 tabstop=8
" JSON specific settings
autocmd FileType json setlocal expandtab shiftwidth=2 tabstop=2
" JSON specific settings
autocmd FileType json setlocal expandtab shiftwidth=2 tabstop=2
" Docker specific settings
autocmd BufNewFile,BufRead Dockerfile* setlocal filetype=dockerfile
autocmd FileType dockerfile setlocal expandtab shiftwidth=2 tabstop=2
" Docker specific settings
autocmd BufNewFile,BufRead Dockerfile* setlocal filetype=dockerfile
autocmd FileType dockerfile setlocal expandtab shiftwidth=2 tabstop=2
augroup END
" ============================================================================
" => Status Line
@ -981,7 +1016,8 @@ function! s:MkNonExDir(file, buf)
endfunction
augroup BWCCreateDir
autocmd!
autocmd BufWritePre * :call s:MkNonExDir(expand('<afile>'), +expand('<abuf>'))
" Guard: <afile> is empty for special buffers (NERDTree, Startify, etc.)
autocmd BufWritePre * if !empty(expand('<afile>')) | call s:MkNonExDir(expand('<afile>'), +expand('<abuf>')) | endif
augroup END
" ============================================================================
@ -1036,7 +1072,8 @@ endif
let g:LargeFile = 1024 * 1024 * 10
augroup LargeFile
autocmd!
autocmd BufReadPre * let f=getfsize(expand("<afile>")) | if f > g:LargeFile || f == -2 | call LargeFileSettings() | endif
" Guard: <afile> is empty for special buffers
autocmd BufReadPre * if !empty(expand('<afile>')) | let f=getfsize(expand('<afile>')) | if f > g:LargeFile || f == -2 | call LargeFileSettings() | endif | endif
augroup END
function! LargeFileSettings()
@ -1055,7 +1092,10 @@ endfunction
" Additional optimizations for TTY/basic terminals
if g:is_tty
" Disable syntax highlighting for very large files in TTY
autocmd BufReadPre * if getfsize(expand("<afile>")) > 512000 | setlocal syntax=OFF | endif
augroup ChopstickTTYLargeFile
autocmd!
autocmd BufReadPre * if !empty(expand('<afile>')) && getfsize(expand('<afile>')) > 512000 | setlocal syntax=OFF | endif
augroup END
" Simpler status line for TTY
set statusline=%f\ %h%w%m%r\ %=%(%l,%c%V\ %=\ %P%)
@ -1085,7 +1125,10 @@ set timeoutlen=500
if exists('g:plugs["vim-which-key"]')
" Register after plugins are loaded (autoload functions available at VimEnter)
autocmd VimEnter * call which_key#register(',', 'g:which_key_map')
augroup ChopstickWhichKey
autocmd!
autocmd VimEnter * call which_key#register(',', 'g:which_key_map')
augroup END
nnoremap <silent> <leader> :<C-u>WhichKey ','<CR>
vnoremap <silent> <leader> :<C-u>WhichKeyVisual ','<CR>
@ -1200,8 +1243,24 @@ endif
" ============================================================================
if exists('g:plugs["vim-startify"]')
" Dynamic header: config name, vim version, current dir, git branch, key tips
" Centered dashboard header — matches neovim startup aesthetic
function! StartifyHeader() abort
let l:art = [
\ '███╗ ███╗ ██╗███╗ ██╗ ██████╗ ███████╗ █████╗ ███╗ ███╗ █████╗ ',
\ '████╗ ████║███║████╗ ██║██╔════╝ ██╔════╝██╔══██╗████╗ ████║██╔══██╗',
\ '██╔████╔██║╚██║██╔██╗ ██║██║ ███╗███████╗███████║██╔████╔██║███████║ ',
\ '██║╚██╔╝██║ ██║██║╚██╗██║██║ ██║╚════██║██╔══██║██║╚██╔╝██║██╔══██║',
\ '██║ ╚═╝ ██║ ██║██║ ╚████║╚██████╔╝███████║██║ ██║██║ ╚═╝ ██║██║ ██║',
\ '╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝',
\ ]
let l:art_w = 71
let l:pad = max([0, (winwidth(0) - l:art_w) / 2])
let l:indent = repeat(' ', l:pad)
let l:lines = []
for l:line in l:art
call add(l:lines, l:indent . l:line)
endfor
" Info subheader: vim version + cwd + git branch
let l:ver = 'Vim ' . (v:version / 100) . '.' . printf('%02d', v:version % 100)
let l:cwd = fnamemodify(getcwd(), ':t')
let l:git = ''
@ -1212,20 +1271,21 @@ if exists('g:plugs["vim-startify"]')
let l:git = ' [' . substitute(l:branch, '\n\+$', '', '') . ']'
endif
endif
return [
\ ' chopsticks | ' . l:ver . ' | ' . l:cwd . l:git,
\ ' , = leader | , + pause = key hints | Ctrl-p = files | ,rg = search',
\ '',
\ ]
let l:info = l:ver . ' ' . l:cwd . l:git
let l:info_pad = max([0, (winwidth(0) - len(l:info)) / 2])
call add(l:lines, '')
call add(l:lines, repeat(' ', l:info_pad) . l:info)
call add(l:lines, '')
return l:lines
endfunction
let g:startify_custom_header = 'StartifyHeader()'
" Sessions first: restores full project state; dir + recent files below
" Sessions first, then recent files — mirrors dashboard plugin ordering
let g:startify_lists = [
\ { 'type': 'sessions', 'header': [' Sessions'] },
\ { 'type': 'dir', 'header': [' Directory: ' . getcwd()] },
\ { 'type': 'files', 'header': [' Recent Files'] },
\ { 'type': 'bookmarks', 'header': [' Bookmarks'] },
\ { 'type': 'sessions', 'header': [' Sessions'] },
\ { 'type': 'files', 'header': [' Recent Files'] },
\ { 'type': 'dir', 'header': [' Current Dir'] },
\ { 'type': 'bookmarks', 'header': [' Bookmarks'] },
\ ]
" Quick-access bookmarks for common config files
@ -1242,11 +1302,17 @@ if exists('g:plugs["vim-startify"]')
let g:startify_fortune_use_unicode = 0 " ASCII only (KISS)
let g:startify_enable_special = 0 " Hide <empty> / <quit> clutter
" Limit recent files shown
let g:startify_files_number = 10
" Show more recent files
let g:startify_files_number = 8
" Left padding for list items (gives visual breathing room)
let g:startify_padding_left = 4
" Required for NERDTree compatibility (prevents buftype conflicts)
autocmd User Startified setlocal buftype=
augroup ChopstickStartify
autocmd!
autocmd User Startified setlocal buftype=
augroup END
endif
" ============================================================================

85
CHANGELOG.md Normal file
View file

@ -0,0 +1,85 @@
# Changelog
All notable changes to chopsticks are documented here.
---
## [1.0.0] - 2026-03-29
First stable release. Full-stack engineering environment out of the box — automatic
installation, tiered LSP, TTY fallback, and coverage for 14 languages.
### Added
- **Arch Linux support** in `install.sh` — pacman branch for all system tools
- **`hadolint`** added to system tools installation (Dockerfile linting)
- **`staticcheck`** added to Go tools (replaces archived `golint`)
- **`yamllint`** added to pip tools (YAML linting)
- **`coc-settings.json`** — configures `marksman` as Markdown LSP for CoC via
`languageserver` entry; symlinked automatically by `install.sh`
- **pip3 bootstrap** in `install.sh` — auto-installs pip3 when python3 is present
but pip3 is absent (common on Ubuntu minimal images)
- **9 named augroups** in `.vimrc` — all loose `autocmd` statements now wrapped
with `autocmd!` to prevent doubling on `:source $MYVIMRC`:
`ChopstickTabHistory`, `ChopstickResize`, `ChopstickStdin`, `CocHighlight`,
`ChopstickCleanup`, `ChopstickFiletype`, `ChopstickTTYLargeFile`,
`ChopstickWhichKey`, `ChopstickStartify`
- **TTY-safe plugin install**`vim +PlugInstall +qall </dev/null` prevents
Vim from blocking in non-interactive/piped environments
### Changed
- SQL tooling unified to **`sqlfluff`** (pip) — `sqlfmt` removed from npm section
- Go linter changed from `golint` (archived 2023) to **`staticcheck`**
- Markdown LSP changed from broken `coc-marksman` (npm) to **`marksman`** binary
configured via `coc-settings.json`
### Fixed
- **vim-go startup hang** on Arch Linux — removed `:GoUpdateBinaries` post-install
hook; set `g:go_gopls_enabled = 0` to prevent conflict with `coc-go`
- **E495 errors** (`<afile>` in special buffers) — all `<afile>` usages guarded
with `!empty(expand('<afile>'))` and `empty(&buftype)` checks
- **`g:go_def_mode` conflict** — now conditional: uses `gopls` when CoC active,
`godef` when vim-lsp active (avoids error when gopls is disabled)
- **vim startup UX** — NERDTree + Startify layout for `vim .` and bare `vim`
- **`coc-marksman` silent failure** — package does not exist on npm; replaced with
native `languageserver` configuration in `coc-settings.json`
- **CoC startup warning** in no-node environments — `g:coc_start_at_startup = 0`
and `g:coc_disable_startup_warning = 1` set when `g:use_coc = 0`
---
## [0.9.0] - 2026-02-21
### Added
- **Full-stack language coverage** — LSP, lint, and format for: Python,
JavaScript, TypeScript, Go, Rust, Shell, YAML, HTML, CSS/SCSS, Less,
JSON, Markdown, SQL, Dockerfile
- **`install.sh` overhaul** — automated installation of system tools, npm tools,
pip tools, Go tools, and CoC language server extensions with platform detection
and interactive prompts; `--yes` flag for non-interactive mode
- **vim-startify** startup screen with dynamic header (version, cwd, git branch)
- **vim-which-key** keybinding popup on `,` + 500ms pause
- **Startup layout**`vim .` opens NERDTree left + Startify right; bare `vim`
opens Startify with NERDTree alongside
- **Session management** via vim-obsession + vim-prosession
- **Large file handling** — syntax and undo disabled for files > 10 MB
- **Project-local config**`.vimrc` in project root auto-loaded via `set exrc`
- **Persistent undo**`~/.vim/.undo/` with `undolevels=1000`
### Changed
- Tiered LSP backend: CoC (Node.js) preferred, vim-lsp (no Node.js) as fallback
- All CoC and vim-lsp keybindings unified (`gd`, `K`, `[g`/`]g`, `,rn`, `,ca`)
- ALE `fix_on_save` disabled when vim-lsp active (prevents double-format)
- NERDTree autocmd wrapped in `augroup NERDTreeAutoClose`
### Fixed
- Multiple leader key conflicts resolved (`,ad`, `,cd`, `,cp`, `,sp`, `,t`)
- CtrlP removed (redundant with FZF)
- Duplicate `set` options cleaned up
- `<leader>A` dead mapping (no alternate-file plugin) removed
---
## [0.1.0] - 2024
Initial release — base Vim configuration with vim-plug, basic plugins, and
TTY/non-TTY detection.

View file

@ -2,6 +2,8 @@
Five minutes from zero to a working Vim engineering environment.
---
## Step 1: Install
```bash
@ -9,8 +11,15 @@ git clone https://github.com/m1ngsama/chopsticks.git ~/.vim
cd ~/.vim && ./install.sh
```
The script handles everything: symlinks, vim-plug, plugin download.
No root access required.
The script handles everything: symlinks, vim-plug, plugins, and all tools.
It detects your OS (macOS/Debian/Arch/Fedora) and installs what it can automatically.
**Non-interactive (CI / server):**
```bash
./install.sh --yes
```
---
## Step 2: Open Vim
@ -18,15 +27,23 @@ No root access required.
vim
```
The startup screen shows recent files and sessions. Press `q` to dismiss
or just start typing a filename to open.
The startup screen (vim-startify) shows recent files and sessions.
Press `Ctrl+p` to find a file, or just type a path.
## Step 3: Install LSP (pick one path)
To open a project:
```bash
vim . # NERDTree on left, Startify on right
vim myfile # opens file directly
```
### Path A: With Node.js (full CoC)
---
## Step 3: Set Up LSP (pick your path)
### Path A: With Node.js (CoC — full LSP)
```bash
node --version # confirm >= 14.14
node --version # must be >= 14.14
```
Inside Vim, install language servers for your stack:
@ -35,80 +52,75 @@ Inside Vim, install language servers for your stack:
:CocInstall coc-pyright coc-tsserver coc-go coc-rust-analyzer
```
### Path B: Without Node.js (vim-lsp)
Or let `install.sh` do it — it asks during setup.
Open a file of your language, then run:
### Path B: Without Node.js (vim-lsp — no dependencies)
Open a source file, then run:
```vim
:LspInstallServer
```
This auto-detects and installs the correct language server binary.
This auto-detects and installs the correct language server for the current filetype.
---
## The 10 Keys That Matter
```
, (pause 500ms) Show all shortcuts
Ctrl+p Fuzzy find file
Ctrl+n Toggle file tree
gd Go to definition
K Show documentation
[g / ]g Prev / next diagnostic
,rn Rename symbol
,rg Search project contents
,gs Git status
,w / ,q Save / Quit
```
---
## Daily Use
### The 10 keys that matter most
### Navigate Code
| Key | Action |
|-----|--------|
| `gd` | Go to definition |
| `gy` | Go to type definition |
| `gi` | Go to implementation |
| `gr` | List all references |
| `K` | Show docs for symbol under cursor |
| `Ctrl+o` | Jump back |
| `Ctrl+i` | Jump forward |
### Edit Code
| Key | Action |
|-----|--------|
| `Tab` | Select next completion item |
| `Enter` | Confirm completion |
| `gc` | Toggle comment (visual mode too) |
| `cs"'` | Change surrounding `"` to `'` |
| `ds(` | Delete surrounding `(` |
| `s`+2ch | EasyMotion: jump anywhere |
### Manage Errors
| Key | Action |
|-----|--------|
| `]g` | Jump to next diagnostic |
| `[g` | Jump to previous diagnostic |
| `K` | Read the error message |
| `,ca` | Apply code action / auto-fix |
### Git Workflow
```
,w Save
,q Quit
Ctrl+n File tree
Ctrl+p Fuzzy find file
gd Go to definition
K Show docs
[g ]g Prev/next diagnostic
,rn Rename symbol
,gs Git status
, (pause 500ms) Show all shortcuts
```
### Open a project
```bash
cd ~/my-project && vim
```
NERDTree auto-opens when you launch Vim on a directory. Use `Ctrl+p` to
fuzzy-search files by name. Use `,rg` to search file contents.
### Navigate code
| Key | Action |
|-------|---------------------------------|
| `gd` | Go to definition |
| `gy` | Go to type definition |
| `gi` | Go to implementation |
| `gr` | List all references |
| `K` | Show docs for symbol under cursor |
| `Ctrl+o` | Jump back |
| `Ctrl+i` | Jump forward |
### Edit code
| Key | Action |
|---------|-------------------------------------|
| `Tab` | Select next completion item |
| `Enter` | Confirm completion |
| `gc` | Toggle comment (works in visual mode too) |
| `cs"'` | Change surrounding `"` to `'` |
| `ds(` | Delete surrounding `(` |
| `s`+2ch | EasyMotion: jump anywhere |
### Manage errors
| Key | Action |
|--------|-------------------------------|
| `]g` | Jump to next diagnostic |
| `[g` | Jump to previous diagnostic |
| `K` | Read the error message |
| `,ca` | Apply code action / auto-fix |
### Git workflow
```
,gs git status (stage files with 's', commit with 'cc')
,gs git status (stage with 's', commit with 'cc')
,gd diff current file
,gb blame current file
,gc commit
@ -118,37 +130,45 @@ fuzzy-search files by name. Use `,rg` to search file contents.
---
## Common Workflows
## Language Workflows
### Python project
### Python
```bash
# tools installed by install.sh; or manually:
pip install black flake8 pylint isort
vim my_script.py
```
Auto-formats with black on save. Lint errors show in the sign column as
`X` (error) and `!` (warning). Jump between them with `[g` / `]g`.
Auto-formats with `black` + `isort` on save. Lint errors show as `X`/`!` in the sign column.
### JavaScript / TypeScript project
### JavaScript / TypeScript
```bash
npm install -g prettier eslint typescript
vim src/index.ts
```
Auto-formats with prettier on save. Use `:CocInstall coc-tsserver` for
full IntelliSense (requires Node.js).
Auto-formats with `prettier` on save.
### Go project
### Go
```bash
# tools installed by install.sh; or manually:
go install golang.org/x/tools/gopls@latest
vim main.go
go install golang.org/x/tools/cmd/goimports@latest
go install honnef.co/go/tools/cmd/staticcheck@latest
```
gofmt runs on save automatically. `gd` jumps to definitions even across
package boundaries when gopls is running.
`gofmt` + `goimports` run on save automatically.
### Markdown
Install `marksman` for LSP support (completions, link checking):
```bash
brew install marksman # macOS
sudo pacman -S marksman # Arch
# or: ./install.sh (handles it automatically)
```
---
@ -160,16 +180,26 @@ Edit config live:
,sv " reloads config without restarting
```
Per-project overrides: create `.vimrc` in your project root.
Per-project settings: create `.vimrc` in your project root.
```vim
" project/.vimrc
set shiftwidth=2
let g:ale_python_black_options = '--line-length=100'
```
Change color scheme in `~/.vimrc`:
```vim
colorscheme dracula " or: gruvbox, solarized, onedark
```
---
## Quick Reference
## Quick Reference Card
```
FILES
Ctrl+n File tree toggle
Ctrl+p Fuzzy find file
Ctrl+p Fuzzy find file (git-aware)
,b Search open buffers
,rg Search file contents (ripgrep)
,w Save | ,q Quit | ,x Save+quit
@ -185,8 +215,8 @@ CODE
,F Format whole file
GIT
,gs Status | ,gd Diff | ,gb Blame
,gc Commit | ,gp Push | ,gl Pull
,gs Status | ,gd Diff | ,gb Blame
,gc Commit | ,gp Push | ,gl Pull
WINDOWS
Ctrl+h/j/k/l Move between panes
@ -198,8 +228,7 @@ WINDOWS
SEARCH
/text Search forward
?text Search backward
,<CR> Clear search highlight
,* Replace word under cursor (project)
,* Replace word under cursor (project-wide)
```
---

408
README.md
View file

@ -1,9 +1,10 @@
# chopsticks - Vim Configuration
# chopsticks Vim Configuration
A native Vim configuration optimized for engineering workflows. Designed for
Vim 8.0+ with automatic fallbacks for minimal environments (TTY, no Node.js).
A native Vim configuration optimized for full-stack engineering workflows.
Vim 8.0+ · Tiered LSP · TTY-aware · Zero icon fonts · 14 languages.
## Quick Install
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
[![Vim 8.0+](https://img.shields.io/badge/Vim-8.0%2B-brightgreen.svg)](https://www.vim.org/)
```bash
git clone https://github.com/m1ngsama/chopsticks.git ~/.vim
@ -16,30 +17,31 @@ See [QUICKSTART.md](QUICKSTART.md) for the 5-minute guide.
## Design Principles
- **KISS**: No icon fonts, no unicode glyphs, plain ASCII throughout
- **Tiered LSP**: CoC (full) with vim-lsp fallback - works with or without Node.js
- **TTY-aware**: Automatic detection and optimization for console environments
- **Engineering-first**: Git workflow, session management, project-local config
- **KISS** — No icon fonts, no unicode glyphs, plain ASCII throughout
- **Tiered LSP** — CoC (full) with vim-lsp fallback; works with or without Node.js
- **TTY-aware** — Automatic detection and optimization for console/SSH environments
- **Engineering-first** — Git workflow, session management, project-local config
- **Batteries included**`install.sh` handles all dependencies automatically
---
## Requirements
| Requirement | Minimum | Notes |
|----------------|-------------|--------------------------------|
| Vim | 8.0+ | vim9script not required |
| git | any | For cloning and fugitive |
| curl | any | For vim-plug auto-install |
| Node.js | 14.14+ | Optional, enables CoC LSP |
| ripgrep (rg) | any | Optional, enables :Rg search |
| fzf | any | Optional, enables :Files/:GFiles |
| ctags | any | Optional, enables :TagbarToggle |
| Requirement | Minimum | Notes |
|-------------|---------|-------|
| Vim | 8.0+ | vim9script not required |
| git | any | For cloning and fugitive |
| curl | any | For vim-plug auto-install |
| Node.js | 14.14+ | Optional — enables CoC LSP |
| ripgrep (rg) | any | Optional — enables `:Rg` search |
| fzf | any | Optional — enables `Ctrl+p` fuzzy search |
| ctags | any | Optional — enables `F8` tag browser |
---
## Installation
### Automatic
### Automatic (recommended)
```bash
git clone https://github.com/m1ngsama/chopsticks.git ~/.vim
@ -47,22 +49,30 @@ cd ~/.vim
./install.sh
```
The script:
1. Checks for a working Vim installation
2. Backs up your existing `~/.vimrc` if present
3. Creates a symlink: `~/.vimrc -> ~/.vim/.vimrc`
4. Installs vim-plug
5. Runs `:PlugInstall` to download all plugins
6. Optionally installs CoC language servers (if Node.js is available)
The installer:
1. Checks Vim version and detects OS / package managers
2. Backs up any existing `~/.vimrc` (timestamped)
3. Creates symlinks: `~/.vimrc -> ~/.vim/.vimrc` and `~/.vim/coc-settings.json`
4. Installs vim-plug and runs `:PlugInstall`
5. Optionally installs system tools, language tools, and CoC extensions
Supported platforms: **macOS** (Homebrew), **Debian/Ubuntu** (apt), **Arch Linux** (pacman), **Fedora** (dnf).
Use `--yes` for non-interactive / CI environments:
```bash
./install.sh --yes
```
### Manual
```bash
git clone https://github.com/m1ngsama/chopsticks.git ~/.vim
ln -sf ~/.vim/.vimrc ~/.vimrc
ln -sf ~/.vim/coc-settings.json ~/.vim/coc-settings.json
curl -fLo ~/.vim/autoload/plug.vim --create-dirs \
https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
vim +PlugInstall +qall
vim +PlugInstall +qall </dev/null
```
---
@ -71,13 +81,13 @@ vim +PlugInstall +qall
Code intelligence is provided by one of two backends, selected automatically:
| Condition | Backend | Features |
|----------------------------------|----------------|-----------------------------------------------|
| Vim 8.0.1453+ AND Node.js 14.14+ | **CoC** | Full LSP, snippets, extensions ecosystem |
| Vim 8.0+ (no Node.js) | **vim-lsp** | LSP via language server binaries, asyncomplete|
| Any Vim | **ALE** | Linting and auto-fix (always active) |
| Condition | Backend | Features |
|-----------|---------|----------|
| Vim 8.0.1453+ AND Node.js 14.14+ | **CoC** | Full LSP, snippets, extensions ecosystem |
| Vim 8.0+ (no Node.js) | **vim-lsp** | LSP via language server binaries, asyncomplete |
| Any Vim | **ALE** | Linting and auto-fix (always active) |
Both backends expose identical key mappings (`gd`, `K`, `[g`, `]g`, `<leader>rn`, `<leader>ca`).
Both backends expose identical key mappings: `gd`, `K`, `[g`, `]g`, `<leader>rn`, `<leader>ca`.
### CoC setup (with Node.js)
@ -90,12 +100,16 @@ Install language server extensions from inside Vim:
:CocInstall coc-rust-analyzer " Rust
:CocInstall coc-json coc-yaml " JSON, YAML
:CocInstall coc-html coc-css " HTML, CSS
:CocInstall coc-marksman " Markdown
:CocInstall coc-sh " Shell
:CocInstall coc-sql " SQL
```
`install.sh` installs all of the above automatically when prompted.
**Markdown LSP** — `marksman` is configured via `coc-settings.json` (not a CoC
extension — install `marksman` binary via `brew install marksman` or download from
[releases](https://github.com/artempyanykh/marksman/releases)).
### vim-lsp setup (without Node.js)
Install language server binaries for your languages, then run:
@ -104,8 +118,8 @@ Install language server binaries for your languages, then run:
:LspInstallServer " auto-installs the right server for the current filetype
```
Supported languages: Python, Go, Rust, TypeScript, JavaScript, Shell,
HTML, CSS/SCSS, JSON, YAML, Markdown, SQL — via `vim-lsp-settings`.
Supported languages: Python, Go, Rust, TypeScript, JavaScript, Shell, HTML,
CSS/SCSS, JSON, YAML, Markdown, SQL — via `vim-lsp-settings`.
---
@ -117,148 +131,144 @@ Press `,` and wait 500ms for an interactive guide to all bindings (vim-which-key
### Files and Buffers
| Key | Action |
|------------|-------------------------------------|
| `Ctrl+n` | Toggle file tree (NERDTree) |
| `,n` | Reveal current file in NERDTree |
| `Ctrl+p` | Fuzzy file search (FZF) |
| `,b` | Search open buffers (FZF) |
| `,rg` | Project-wide search (ripgrep+FZF) |
| `,rt` | Search tags (FZF) |
| `,gF` | Search git-tracked files (FZF) |
| `,l` | Next buffer |
| `,h` | Previous buffer |
| `,bd` | Close current buffer |
| `,,` | Switch to last file |
| Key | Action |
|-----|--------|
| `Ctrl+n` | Toggle file tree (NERDTree) |
| `,n` | Reveal current file in NERDTree |
| `Ctrl+p` | Fuzzy file search (FZF — git-aware) |
| `,b` | Search open buffers (FZF) |
| `,rg` | Project-wide search (ripgrep+FZF) |
| `,rt` | Search tags (FZF) |
| `,gF` | Search git-tracked files (FZF) |
| `,l` | Next buffer |
| `,h` | Previous buffer |
| `,bd` | Close current buffer |
| `,,` | Switch to last file |
### Windows and Tabs
| Key | Action |
|--------------|---------------------------------|
| `Ctrl+h/j/k/l` | Navigate between windows |
| `<Leader>=` | Increase window height |
| `<Leader>-` | Decrease window height |
| `<Leader>+` | Increase window width |
| `<Leader>_` | Decrease window width |
| `,tn` | New tab |
| `,tc` | Close tab |
| `,tl` | Toggle to last tab |
| Key | Action |
|-----|--------|
| `Ctrl+h/j/k/l` | Navigate between windows |
| `<Leader>=` | Increase window height |
| `<Leader>-` | Decrease window height |
| `<Leader>+` | Increase window width |
| `<Leader>_` | Decrease window width |
| `,tn` | New tab |
| `,tc` | Close tab |
| `,tl` | Toggle to last tab |
### Code Intelligence (CoC / vim-lsp)
| Key | Action |
|-------------|---------------------------------|
| `gd` | Go to definition |
| `gy` | Go to type definition |
| `gi` | Go to implementation |
| `gr` | Show references |
| `K` | Hover documentation |
| `[g` | Previous diagnostic |
| `]g` | Next diagnostic |
| `,rn` | Rename symbol |
| `,f` | Format selection |
| `,ca` | Code action (cursor) |
| `,qf` | Quick-fix current line (CoC) |
| `,cl` | Run code lens (CoC) |
| `,o` | File outline |
| `,ws` | Workspace symbols |
| `,cD` | Diagnostics list |
| `,cr` | Resume last CoC list |
| `Tab` | Next completion item |
| `Shift+Tab` | Previous completion item |
| `Enter` | Confirm completion |
| Key | Action |
|-----|--------|
| `gd` | Go to definition |
| `gy` | Go to type definition |
| `gi` | Go to implementation |
| `gr` | Show references |
| `K` | Hover documentation |
| `[g` | Previous diagnostic |
| `]g` | Next diagnostic |
| `,rn` | Rename symbol |
| `,f` | Format selection |
| `,ca` | Code action (cursor) |
| `,o` | File outline |
| `,ws` | Workspace symbols |
| `,cD` | Diagnostics list |
| `,cr` | Resume last CoC list |
| `,qf` | Quick-fix current line (CoC) |
| `,cl` | Run code lens (CoC) |
| `Tab` | Next completion item |
| `Shift+Tab` | Previous completion item |
| `Enter` | Confirm completion |
Text objects (CoC only): `if`/`af` (function), `ic`/`ac` (class)
### Linting (ALE)
| Key | Action |
|----------|-----------------------|
| `[e` | Next error/warning |
| `]e` | Previous error/warning|
| `,aD` | Show error details |
| Key | Action |
|-----|--------|
| `[e` | Next error/warning |
| `]e` | Previous error/warning |
| `,aD` | Show error details |
Signs: `X` = error, `!` = warning
### Git Workflow (fugitive)
| Key | Action |
|--------|---------------------------------|
| `,gs` | Git status |
| `,gc` | Git commit |
| `,gp` | Git push |
| `,gl` | Git pull |
| `,gd` | Git diff |
| `,gb` | Git blame |
| `,gF` | Search git-tracked files (FZF) |
| Key | Action |
|-----|--------|
| `,gs` | Git status |
| `,gc` | Git commit |
| `,gp` | Git push |
| `,gl` | Git pull |
| `,gd` | Git diff |
| `,gb` | Git blame |
| `,gF` | Search git-tracked files (FZF) |
### Engineering Utilities
| Key | Action |
|----------|-------------------------------------|
| `,ev` | Edit `~/.vimrc` |
| `,sv` | Reload `~/.vimrc` |
| `,F` | Format entire file (= indent) |
| `,W` | Strip trailing whitespace |
| `,wa` | Save all open buffers |
| `,wd` | Change CWD to current buffer's dir |
| `,cp` | Copy file path to clipboard |
| `,cf` | Copy filename to clipboard |
| `,y` | Yank to system clipboard |
| `,Y` | Yank line to system clipboard |
| `,*` | Search+replace word under cursor |
| `,qo` | Open quickfix list |
| `,qc` | Close quickfix list |
| `,tv` | Open terminal (vertical split) |
| `,th` | Open terminal (horizontal, 10r) |
| `Esc` | Exit terminal mode |
| Key | Action |
|-----|--------|
| `,ev` | Edit `~/.vimrc` |
| `,sv` | Reload `~/.vimrc` |
| `,F` | Format entire file |
| `,W` | Strip trailing whitespace |
| `,wa` | Save all open buffers |
| `,wd` | Change CWD to current buffer's dir |
| `,cp` | Copy file path to clipboard |
| `,cf` | Copy filename to clipboard |
| `,y` | Yank to system clipboard |
| `,Y` | Yank line to system clipboard |
| `,*` | Search+replace word under cursor |
| `,qo` | Open quickfix list |
| `,qc` | Close quickfix list |
| `,tv` | Open terminal (vertical split) |
| `,th` | Open terminal (horizontal, 10 rows) |
| `Esc` | Exit terminal mode |
### Navigation and Editing
| Key | Action |
|------------|---------------------------------------------|
| `s`+2ch | EasyMotion jump to any location |
| `Space` | Toggle code fold |
| `Y` | Yank to end of line (like `D`, `C`) |
| `n` / `N` | Search next/prev (cursor centered) |
| `Ctrl+d/u` | Half-page scroll (cursor centered) |
| `>` | Indent (keeps visual selection) |
| `<` | Dedent (keeps visual selection) |
| `[q` / `]q`| Previous/next quickfix (vim-unimpaired) |
| `[e` / `]e`| Previous/next ALE error/warning |
| `F2` | Toggle paste mode |
| `F3` | Toggle line numbers |
| `F4` | Toggle relative line numbers |
| `F5` | Toggle undo history (UndoTree) |
| `F8` | Toggle code tag browser (Tagbar) |
| `0` | Jump to first non-blank character |
| `Alt+j/k` | Move line up/down |
| Key | Action |
|-----|--------|
| `s`+2ch | EasyMotion jump to any location |
| `Space` | Toggle code fold |
| `Y` | Yank to end of line (like `D`, `C`) |
| `n` / `N` | Search next/prev (cursor centered) |
| `Ctrl+d/u` | Half-page scroll (cursor centered) |
| `>` | Indent (keeps visual selection) |
| `<` | Dedent (keeps visual selection) |
| `[q` / `]q` | Previous/next quickfix (vim-unimpaired) |
| `[e` / `]e` | Previous/next ALE error/warning |
| `F2` | Toggle paste mode |
| `F3` | Toggle line numbers |
| `F4` | Toggle relative line numbers |
| `F5` | Toggle undo history (UndoTree) |
| `F8` | Toggle code tag browser (Tagbar) |
| `0` | Jump to first non-blank character |
| `Alt+j/k` | Move line up/down |
---
## Features
### vim-startify: Startup Screen
### Startup Screen (vim-startify)
Opens when Vim is launched without a file argument. Shows:
- Session list for current directory
- Recently opened files
- Sessions for the current directory
- Bookmarks
Session auto-saves on quit. Auto-loads `Session.vim` if found in the current
directory. Auto-changes to git root on file open.
### vim-which-key: Keybinding Guide
**`vim .` layout** — NERDTree on the left, Startify on the right.
### Keybinding Guide (vim-which-key)
Press `,` and pause for 500ms. A popup lists all available leader bindings
organized by group. Useful for onboarding and discovering shortcuts.
### indentLine: Indent Guides
Draws `|` characters at each indent level. Disabled automatically in TTY
environments and for filetypes where it causes display problems (JSON,
Markdown, help).
### Session Management
```vim
@ -266,8 +276,8 @@ Markdown, help).
:Obsess! " Stop tracking
```
Sessions are stored in `~/.vim/sessions/` and automatically resumed by
vim-prosession on the next Vim launch in the same directory.
Sessions stored in `~/.vim/sessions/` and automatically resumed by vim-prosession
on the next Vim launch in the same directory.
### Project-Local Config
@ -283,91 +293,88 @@ Loaded automatically. Security-restricted via `set secure`.
### Large File Handling
Files over 10MB automatically disable syntax highlighting and undo history
Files over 10 MB automatically disable syntax highlighting and undo history
to prevent Vim from freezing.
### TTY / Console Support
Detected automatically when `$TERM` is `linux` or `screen`, or when running
on a basic built-in terminal. In TTY mode:
Detected automatically when `$TERM` is `linux` or `screen`. In TTY mode:
- True color and cursorline disabled
- Powerline separators replaced with plain ASCII
- FZF preview windows disabled
- NERDTree auto-open skipped
- Syntax column limit reduced to 120
- Simpler status line used
- Simpler status line
---
## Language Support
| Language | Indent | Formatter | Linter |
|----------------|--------|------------------|--------------------------|
| Python | 4sp | black + isort | flake8, pylint |
| JavaScript | 2sp | prettier | eslint |
| TypeScript | 2sp | prettier | eslint, tsserver |
| Go | tab | gofmt, goimports | gopls, golint |
| Rust | 4sp | rustfmt | cargo |
| Shell | 2sp | - | shellcheck |
| YAML | 2sp | prettier | yamllint |
| HTML | 2sp | prettier | - |
| CSS / SCSS | 2sp | prettier | stylelint |
| Less | 2sp | prettier | - |
| JSON | 2sp | prettier | - |
| Markdown | 2sp | prettier | markdownlint |
| SQL | 4sp | sqlfmt | sqlfluff |
| Dockerfile | 2sp | - | hadolint |
| Language | Indent | Formatter | Linter | LSP (CoC) |
|----------|--------|-----------|--------|-----------|
| Python | 4sp | black + isort | flake8, pylint | coc-pyright |
| JavaScript | 2sp | prettier | eslint | coc-tsserver |
| TypeScript | 2sp | prettier | eslint, tsserver | coc-tsserver |
| Go | tab | gofmt, goimports | staticcheck | coc-go |
| Rust | 4sp | rustfmt | cargo | coc-rust-analyzer |
| Shell | 2sp | — | shellcheck | coc-sh |
| YAML | 2sp | prettier | yamllint | coc-yaml |
| HTML | 2sp | prettier | — | coc-html |
| CSS / SCSS | 2sp | prettier | stylelint | coc-css |
| Less | 2sp | prettier | — | — |
| JSON | 2sp | prettier | — | coc-json |
| Markdown | 2sp | prettier | markdownlint | marksman (coc-settings.json) |
| SQL | 4sp | sqlfluff | sqlfluff | — |
| Dockerfile | 2sp | — | hadolint | — |
Install linters separately — `install.sh` lists the exact commands.
ALE runs them asynchronously on save (`ale_fix_on_save = 1` when using CoC).
`install.sh` installs all linters and formatters automatically.
ALE runs them asynchronously; format-on-save active when using CoC.
---
## Plugin List
### Navigation
- **NERDTree** - File tree explorer
- **fzf + fzf.vim** - Fuzzy finder
- **CtrlP** - Fallback fuzzy finder (no fzf dependency)
- **NERDTree** — File tree explorer
- **fzf + fzf.vim** — Fuzzy finder (file, buffer, tag, ripgrep)
### Git
- **vim-fugitive** - Git commands inside Vim
- **vim-gitgutter** - Diff signs in the sign column
- **vim-fugitive** Git commands inside Vim
- **vim-gitgutter** Diff signs in the sign column
### LSP and Completion
- **coc.nvim** - Full LSP + completion (requires Node.js 14.14+)
- **vim-lsp** - Pure VimScript LSP client (fallback, no Node.js)
- **vim-lsp-settings** - Auto-configure language servers for vim-lsp
- **asyncomplete.vim** - Async completion (used with vim-lsp)
- **coc.nvim** Full LSP + completion (requires Node.js 14.14+)
- **vim-lsp** Pure VimScript LSP client (fallback, no Node.js)
- **vim-lsp-settings** Auto-configure language servers for vim-lsp
- **asyncomplete.vim** Async completion (used with vim-lsp)
### Linting
- **ALE** - Asynchronous Lint Engine (always active)
- **ALE** Asynchronous Lint Engine (always active)
### UI
- **vim-airline** - Status and tabline
- **vim-startify** - Startup screen
- **vim-which-key** - Keybinding hint popup
- **indentLine** - Indent guide lines (non-TTY)
- **undotree** - Undo history visualizer
- **tagbar** - Code structure sidebar
- **vim-airline** Status and tabline
- **vim-startify** — Startup screen with sessions
- **vim-which-key** Keybinding hint popup
- **indentLine** Indent guide lines (non-TTY)
- **undotree** Undo history visualizer
- **tagbar** Code structure sidebar
### Editing
- **vim-surround** - Change surrounding quotes, brackets, tags
- **vim-commentary** - `gc` to toggle comments
- **auto-pairs** - Auto-close brackets and quotes
- **vim-easymotion** - Jump anywhere with 2 keystrokes
- **vim-unimpaired** - Bracket shortcut pairs
- **targets.vim** - Extra text objects
- **vim-snippets** - Snippet library (used with CoC/UltiSnips)
- **vim-surround** Change surrounding quotes, brackets, tags
- **vim-commentary** `gc` to toggle comments
- **auto-pairs** Auto-close brackets and quotes
- **vim-easymotion** Jump anywhere with 2 keystrokes
- **vim-unimpaired** Bracket shortcut pairs
- **targets.vim** Extra text objects
- **vim-snippets** Snippet library (used with CoC/UltiSnips)
### Language Packs
- **vim-polyglot** - Syntax for 100+ languages
- **vim-go** - Go development tools
- **vim-polyglot** Syntax for 100+ languages
- **vim-go** — Go development tools (formatting + highlighting; LSP handled by coc-go)
### Session
- **vim-obsession** - Continuous session saving
- **vim-prosession** - Project-level session management
- **vim-obsession** Continuous session saving
- **vim-prosession** Project-level session management
### Color Schemes
- **gruvbox** (default), **dracula**, **solarized**, **onedark**
@ -400,6 +407,14 @@ True color is enabled automatically when the terminal supports it
node --version # must be >= 14.14
```
**Markdown LSP not starting:**
```bash
marksman --version # must be installed separately
brew install marksman # macOS
sudo pacman -S marksman # Arch
# or: ./install.sh (installs automatically)
```
**vim-lsp server not starting:**
```vim
:LspInstallServer " install server for current filetype
@ -408,8 +423,7 @@ node --version # must be >= 14.14
**Colors look wrong:**
```bash
# Add to ~/.bashrc or ~/.zshrc
export TERM=xterm-256color
export TERM=xterm-256color # add to ~/.bashrc or ~/.zshrc
```
**ALE not finding linters:**
@ -421,12 +435,18 @@ which flake8 black prettier eslint # confirm tools are on PATH
## References
- [amix/vimrc](https://github.com/amix/vimrc)
- [vim-plug](https://github.com/junegunn/vim-plug)
- [coc.nvim](https://github.com/neoclide/coc.nvim)
- [vim-lsp](https://github.com/prabirshrestha/vim-lsp)
- [vim-lsp-settings](https://github.com/mattn/vim-lsp-settings)
- [amix/vimrc](https://github.com/amix/vimrc)
---
## Changelog
See [CHANGELOG.md](CHANGELOG.md).
## License
MIT
[MIT](LICENSE) © m1ng

13
coc-settings.json Normal file
View file

@ -0,0 +1,13 @@
{
"languageserver": {
"marksman": {
"command": "marksman",
"args": ["server"],
"filetypes": ["markdown"],
"rootPatterns": [".git", ".marksman.toml"]
}
},
"coc.preferences.formatOnSaveFiletypes": [
"markdown"
]
}

View file

@ -93,13 +93,28 @@ HAS_BREW=0; command -v brew >/dev/null 2>&1 && HAS_BREW=1
HAS_APT=0; command -v apt >/dev/null 2>&1 && HAS_APT=1
HAS_DNF=0; command -v dnf >/dev/null 2>&1 && HAS_DNF=1
HAS_PACMAN=0; command -v pacman >/dev/null 2>&1 && HAS_PACMAN=1
HAS_NODE=0; command -v node >/dev/null 2>&1 && HAS_NODE=1 && ok "Node.js $(node --version) detected"
HAS_PIP=0; command -v pip3 >/dev/null 2>&1 && HAS_PIP=1 && ok "Python/pip3 detected"
HAS_GO=0; command -v go >/dev/null 2>&1 && HAS_GO=1 && ok "Go $(go version | awk '{print $3}') detected"
HAS_NODE=0; command -v node >/dev/null 2>&1 && HAS_NODE=1 && ok "Node.js $(node --version) detected"
HAS_PYTHON=0; command -v python3 >/dev/null 2>&1 && HAS_PYTHON=1
HAS_PIP=0; command -v pip3 >/dev/null 2>&1 && HAS_PIP=1
HAS_GO=0; command -v go >/dev/null 2>&1 && HAS_GO=1 && ok "Go $(go version | awk '{print $3}') detected"
[[ $HAS_NODE -eq 0 ]] && warn "Node.js not found — JS/TS/Markdown npm tools will be skipped"
[[ $HAS_PIP -eq 0 ]] && warn "pip3 not found — Python tools will be skipped"
[[ $HAS_GO -eq 0 ]] && warn "Go not found — Go tools will be skipped"
# Bootstrap pip3 when python3 exists but pip3 is absent (common on Ubuntu minimal images)
if [[ $HAS_PYTHON -eq 1 && $HAS_PIP -eq 0 ]]; then
warn "python3 found but pip3 missing — attempting bootstrap"
if python3 -m ensurepip --upgrade >/dev/null 2>&1 || \
(command -v apt-get >/dev/null 2>&1 && sudo apt-get install -y python3-pip >/dev/null 2>&1) || \
(command -v pacman >/dev/null 2>&1 && sudo pacman -S --noconfirm python-pip >/dev/null 2>&1) || \
(command -v dnf >/dev/null 2>&1 && sudo dnf install -y python3-pip >/dev/null 2>&1); then
command -v pip3 >/dev/null 2>&1 && HAS_PIP=1 && ok "pip3 bootstrapped"
else
warn "pip3 bootstrap failed — Python tools will be skipped"
fi
fi
[[ $HAS_PIP -eq 1 ]] && ok "Python/pip3 detected"
[[ $HAS_NODE -eq 0 ]] && warn "Node.js not found — JS/TS/Markdown npm tools will be skipped"
[[ $HAS_PIP -eq 0 ]] && warn "pip3 not found — Python tools will be skipped"
[[ $HAS_GO -eq 0 ]] && warn "Go not found — Go tools will be skipped"
# ============================================================================
# Symlink
@ -116,6 +131,17 @@ fi
ln -sf "$SCRIPT_DIR/.vimrc" "$HOME/.vimrc"
ok "~/.vimrc -> $SCRIPT_DIR/.vimrc"
# CoC settings (marksman markdown LSP + format-on-save config)
mkdir -p "$HOME/.vim"
COC_CFG="$HOME/.vim/coc-settings.json"
if [ -f "$COC_CFG" ] && [ ! -L "$COC_CFG" ]; then
TS=$(date +%Y%m%d_%H%M%S)
warn "Backing up existing coc-settings.json to ~/.vim/coc-settings.json.backup.$TS"
mv "$COC_CFG" "$COC_CFG.backup.$TS"
fi
ln -sf "$SCRIPT_DIR/coc-settings.json" "$COC_CFG"
ok "~/.vim/coc-settings.json -> $SCRIPT_DIR/coc-settings.json"
# ============================================================================
# vim-plug + plugins
# ============================================================================
@ -132,7 +158,8 @@ else
fi
step "Installing Vim plugins"
vim +PlugInstall +qall
# </dev/null prevents Vim from reading stdin in non-interactive/piped environments
vim +PlugInstall +qall </dev/null
ok "Plugins installed"
# ============================================================================
@ -141,7 +168,7 @@ ok "Plugins installed"
step "System tools"
if ask "Install system tools (ripgrep, fzf, ctags, shellcheck, marksman)?"; then
if ask "Install system tools (ripgrep, fzf, ctags, shellcheck, hadolint, marksman)?"; then
install_sys() {
local name="$1"; local check="$2"; shift 2
if command -v "$check" >/dev/null 2>&1; then
@ -163,17 +190,36 @@ if ask "Install system tools (ripgrep, fzf, ctags, shellcheck, marksman)?"; then
if [[ $OS == macos ]]; then
command -v brew >/dev/null 2>&1 || { warn "brew not found — skipping system tools"; }
install_sys "ripgrep" rg "brew install ripgrep"
install_sys "fzf" fzf "brew install fzf"
install_sys "universal-ctags" ctags "brew install universal-ctags"
install_sys "ripgrep" rg "brew install ripgrep"
install_sys "fzf" fzf "brew install fzf"
install_sys "universal-ctags" ctags "brew install universal-ctags"
install_sys "shellcheck" shellcheck "brew install shellcheck"
install_sys "marksman" marksman "brew install marksman"
install_sys "hadolint" hadolint "brew install hadolint"
install_sys "marksman" marksman "brew install marksman"
elif [[ $HAS_APT -eq 1 ]]; then
sudo apt-get update -qq
install_sys "ripgrep" rg "sudo apt-get install -y ripgrep"
install_sys "fzf" fzf "sudo apt-get install -y fzf"
install_sys "universal-ctags" ctags "sudo apt-get install -y universal-ctags"
install_sys "shellcheck" shellcheck "sudo apt-get install -y shellcheck"
install_sys "shellcheck" shellcheck "sudo apt-get install -y shellcheck"
# hadolint: no apt package, download binary
if ! command -v hadolint >/dev/null 2>&1; then
ARCH=$(uname -m)
[[ "$ARCH" == "x86_64" ]] && HARCH="x86_64" || HARCH="arm64"
HVER=$(curl -s https://api.github.com/repos/hadolint/hadolint/releases/latest \
| grep '"tag_name"' | cut -d'"' -f4)
if [[ -n "$HVER" ]]; then
curl -fsSL "https://github.com/hadolint/hadolint/releases/download/${HVER}/hadolint-Linux-${HARCH}" \
-o /tmp/hadolint && chmod +x /tmp/hadolint && sudo mv /tmp/hadolint /usr/local/bin/hadolint
ok "hadolint"
INSTALLED+=("hadolint")
else
warn "hadolint: could not detect latest release, install manually"
SKIPPED+=("hadolint")
fi
else
ok "hadolint (already installed)"
fi
# marksman: no apt package, download binary
if ! command -v marksman >/dev/null 2>&1; then
ARCH=$(uname -m)
@ -192,21 +238,30 @@ if ask "Install system tools (ripgrep, fzf, ctags, shellcheck, marksman)?"; then
else
ok "marksman (already installed)"
fi
elif [[ $HAS_PACMAN -eq 1 ]]; then
install_sys "ripgrep" rg "sudo pacman -S --noconfirm ripgrep"
install_sys "fzf" fzf "sudo pacman -S --noconfirm fzf"
install_sys "universal-ctags" ctags "sudo pacman -S --noconfirm ctags"
install_sys "shellcheck" shellcheck "sudo pacman -S --noconfirm shellcheck"
install_sys "hadolint" hadolint "sudo pacman -S --noconfirm hadolint"
install_sys "marksman" marksman "sudo pacman -S --noconfirm marksman"
elif [[ $HAS_DNF -eq 1 ]]; then
install_sys "ripgrep" rg "sudo dnf install -y ripgrep"
install_sys "fzf" fzf "sudo dnf install -y fzf"
install_sys "shellcheck" shellcheck "sudo dnf install -y ShellCheck"
install_sys "shellcheck" shellcheck "sudo dnf install -y ShellCheck"
skip "universal-ctags — install manually: sudo dnf install ctags"
SKIPPED+=("ctags")
skip "hadolint — install manually from https://github.com/hadolint/hadolint/releases"
SKIPPED+=("hadolint")
skip "marksman — install manually from https://github.com/artempyanykh/marksman/releases"
SKIPPED+=("marksman")
else
warn "Unknown Linux distro — skipping system tools (install manually)"
SKIPPED+=("ripgrep" "fzf" "ctags" "shellcheck" "marksman")
SKIPPED+=("ripgrep" "fzf" "ctags" "shellcheck" "hadolint" "marksman")
fi
else
skip "system tools"
SKIPPED+=("ripgrep" "fzf" "ctags" "shellcheck" "marksman")
SKIPPED+=("ripgrep" "fzf" "ctags" "shellcheck" "hadolint" "marksman")
fi
# ============================================================================
@ -237,7 +292,6 @@ if [[ $HAS_NODE -eq 1 ]]; then
npm_install stylelint-config-standard
npm_install eslint
npm_install typescript tsc
npm_install sqlfmt
else
skip "npm tools"
SKIPPED+=("prettier" "markdownlint-cli" "stylelint" "eslint" "typescript")
@ -254,7 +308,7 @@ fi
step "Python tools (formatters + linters)"
if [[ $HAS_PIP -eq 1 ]]; then
if ask "Install Python tools (black, isort, flake8, pylint, sqlfluff)?"; then
if ask "Install Python tools (black, isort, flake8, pylint, yamllint, sqlfluff)?"; then
pip_install() {
local pkg="$1"; local check="${2:-$1}"
if command -v "$check" >/dev/null 2>&1; then
@ -274,24 +328,25 @@ if [[ $HAS_PIP -eq 1 ]]; then
pip_install isort
pip_install flake8
pip_install pylint
pip_install yamllint
pip_install sqlfluff
else
skip "Python tools"
SKIPPED+=("black" "isort" "flake8" "pylint" "sqlfluff")
SKIPPED+=("black" "isort" "flake8" "pylint" "yamllint" "sqlfluff")
fi
else
skip "Python tools (pip3 not installed)"
SKIPPED+=("black" "isort" "flake8" "pylint" "sqlfluff")
SKIPPED+=("black" "isort" "flake8" "pylint" "yamllint" "sqlfluff")
fi
# ============================================================================
# Go tools (gopls, goimports)
# Go tools (gopls, goimports, staticcheck)
# ============================================================================
step "Go tools"
if [[ $HAS_GO -eq 1 ]]; then
if ask "Install Go tools (gopls, goimports)?"; then
if ask "Install Go tools (gopls, goimports, staticcheck)?"; then
# Go installs binaries to $(go env GOPATH)/bin — add to PATH for this session
GOBIN="$(go env GOPATH)/bin"
export PATH="$PATH:$GOBIN"
@ -310,8 +365,9 @@ if [[ $HAS_GO -eq 1 ]]; then
FAILED+=("$name")
fi
}
go_install gopls "golang.org/x/tools/gopls@latest" gopls
go_install goimports "golang.org/x/tools/cmd/goimports@latest" goimports
go_install gopls "golang.org/x/tools/gopls@latest" gopls
go_install goimports "golang.org/x/tools/cmd/goimports@latest" goimports
go_install staticcheck "honnef.co/go/tools/cmd/staticcheck@latest" staticcheck
# Remind user to add GOPATH/bin to their shell profile
if ! echo "$PATH" | grep -q "$GOBIN"; then
@ -319,11 +375,11 @@ if [[ $HAS_GO -eq 1 ]]; then
fi
else
skip "Go tools"
SKIPPED+=("gopls" "goimports")
SKIPPED+=("gopls" "goimports" "staticcheck")
fi
else
skip "Go tools (go not installed)"
SKIPPED+=("gopls" "goimports")
SKIPPED+=("gopls" "goimports" "staticcheck")
fi
# ============================================================================
@ -334,7 +390,8 @@ step "CoC language server extensions"
if [[ $HAS_NODE -eq 1 ]]; then
if ask "Install CoC language servers (LSP for all configured languages)?"; then
vim +'CocInstall -sync coc-json coc-tsserver coc-pyright coc-sh coc-html coc-css coc-yaml coc-go coc-rust-analyzer coc-marksman coc-sql' +qall
# Note: coc-marksman doesn't exist on npm — markdown LSP is handled via coc-settings.json
vim +'CocInstall -sync coc-json coc-tsserver coc-pyright coc-sh coc-html coc-css coc-yaml coc-go coc-rust-analyzer coc-sql' +qall </dev/null
ok "CoC language servers installed"
else
skip "CoC language servers"