feat: major feature upgrade — zen mode, run file, smart search, and more

New plugins (3):
- vim-sleuth: auto-detect indentation from existing files
- goyo.vim + limelight.vim: zen mode for focused writing (,zen)

New built-in features (8):
- Run current file (,cr) — auto-detects Python/Go/Rust/JS/C/Shell/etc.
- Yank highlight — flashes yanked text for visual feedback
- Auto-clear search highlight after cursor stops moving
- Git conflict marker navigation ([x / ]x)
- Window maximize toggle (,z)
- Sudo save (:w!!)
- QuickFix auto-open + ]q/[q navigation
- ALE error/warning count in statusline

More FZF mappings:
- ,fh recent files, ,fl buffer lines, ,fL all lines
- ,fc commands, ,fm marks, ,f/ search history, ,f: command history

README rewritten with compelling feature showcase (30 plugins).
Cheat sheet and QUICKSTART updated with all new features.
This commit is contained in:
m1ngsama 2026-04-21 23:42:41 +08:00
parent cc328cebf2
commit 84d999f91f
3 changed files with 355 additions and 396 deletions

242
.vimrc
View file

@ -117,6 +117,7 @@ Plug 'tpope/vim-surround'
Plug 'tpope/vim-commentary' Plug 'tpope/vim-commentary'
Plug 'tpope/vim-repeat' Plug 'tpope/vim-repeat'
Plug 'tpope/vim-unimpaired' Plug 'tpope/vim-unimpaired'
Plug 'tpope/vim-sleuth'
Plug 'wellle/targets.vim' Plug 'wellle/targets.vim'
Plug 'jiangmiao/auto-pairs' Plug 'jiangmiao/auto-pairs'
Plug 'easymotion/vim-easymotion' Plug 'easymotion/vim-easymotion'
@ -136,8 +137,10 @@ Plug 'HerringtonDarkholme/yats.vim'
Plug 'preservim/vim-markdown' Plug 'preservim/vim-markdown'
Plug 'fatih/vim-go' Plug 'fatih/vim-go'
" ── Markdown Preview ────────────────────────────────────────────────────────── " ── Markdown Preview & Writing ────────────────────────────────────────────────
Plug 'previm/previm' Plug 'previm/previm'
Plug 'junegunn/goyo.vim'
Plug 'junegunn/limelight.vim'
" ── UI ──────────────────────────────────────────────────────────────────────── " ── UI ────────────────────────────────────────────────────────────────────────
Plug 'mbbill/undotree' Plug 'mbbill/undotree'
@ -359,12 +362,19 @@ function! s:SmartFiles() abort
endfunction endfunction
if exists('g:plugs["fzf.vim"]') if exists('g:plugs["fzf.vim"]')
nnoremap <C-p> :call <SID>SmartFiles()<CR> nnoremap <C-p> :call <SID>SmartFiles()<CR>
nnoremap <leader>b :Buffers<CR> nnoremap <leader>b :Buffers<CR>
nnoremap <leader>rg :Rg<CR> nnoremap <leader>rg :Rg<CR>
nnoremap <leader>rG :RgWord<CR> nnoremap <leader>rG :RgWord<CR>
nnoremap <leader>rt :Tags<CR> nnoremap <leader>rt :Tags<CR>
nnoremap <leader>gF :GFiles<CR> nnoremap <leader>gF :GFiles<CR>
nnoremap <leader>fh :History<CR>
nnoremap <leader>fc :Commands<CR>
nnoremap <leader>fm :Marks<CR>
nnoremap <leader>fl :BLines<CR>
nnoremap <leader>fL :Lines<CR>
nnoremap <leader>f/ :History/<CR>
nnoremap <leader>f: :History:<CR>
endif endif
let g:fzf_layout = { 'down': '40%' } let g:fzf_layout = { 'down': '40%' }
@ -595,6 +605,34 @@ if exists('g:plugs["previm"]')
endif endif
let g:previm_enable_realtime = 1 let g:previm_enable_realtime = 1
" ============================================================================
" => Goyo + Limelight (zen mode for focused writing)
" ============================================================================
if exists('g:plugs["goyo.vim"]')
let g:goyo_width = 80
let g:goyo_height = '85%'
nnoremap <leader>zen :Goyo<CR>
function! s:goyo_enter()
if exists('g:plugs["limelight.vim"]') | Limelight | endif
set wrap linebreak scrolloff=999
endfunction
function! s:goyo_leave()
if exists('g:plugs["limelight.vim"]') | Limelight! | endif
set nowrap nolinebreak scrolloff=10
endfunction
augroup ChopstickGoyo
autocmd!
autocmd User GoyoEnter nested call s:goyo_enter()
autocmd User GoyoLeave nested call s:goyo_leave()
augroup END
endif
let g:limelight_conceal_ctermfg = 240
let g:limelight_conceal_guifg = '#586e75'
" ============================================================================ " ============================================================================
" => EasyMotion " => EasyMotion
" ============================================================================ " ============================================================================
@ -746,6 +784,16 @@ function! SLGit() abort
return empty(l:b) ? '' : ' ' . l:b . ' ' return empty(l:b) ? '' : ' ' . l:b . ' '
endfunction endfunction
" ALE error/warning count
function! SLAle() abort
if !exists('*ale#statusline#Count') | return '' | endif
let l:c = ale#statusline#Count(bufnr(''))
let l:e = l:c.error + l:c.style_error
let l:w = l:c.warning + l:c.style_warning
if l:e == 0 && l:w == 0 | return '' | endif
return printf(' E:%d W:%d ', l:e, l:w)
endfunction
" Assemble the statusline on every redraw " Assemble the statusline on every redraw
function! SLBuild() abort function! SLBuild() abort
let [l:label, l:hl] = SLMode() let [l:label, l:hl] = SLMode()
@ -753,6 +801,7 @@ function! SLBuild() abort
let l:s .= '%#SLBody# %f ' let l:s .= '%#SLBody# %f '
let l:s .= '%#SLFlag#%m%r' let l:s .= '%#SLFlag#%m%r'
let l:s .= '%#SLBody#%=' let l:s .= '%#SLBody#%='
let l:s .= '%#SLFlag#' . SLAle()
let l:s .= '%#SLGit#' . SLGit() let l:s .= '%#SLGit#' . SLGit()
let l:s .= '%#SLFtype# %y ' let l:s .= '%#SLFtype# %y '
let l:s .= '%#SLRight# %l:%c %P ' let l:s .= '%#SLRight# %l:%c %P '
@ -1041,89 +1090,71 @@ function! s:CheatSheet() abort
call setline(1, [ call setline(1, [
\ '=== chopsticks — Quick Reference ===', \ '=== chopsticks — Quick Reference ===',
\ '', \ '',
\ 'MODES',
\ ' Normal Default. Navigate and run commands.',
\ ' Insert Type text. Enter: i/a/o Leave: Esc or jk',
\ ' Visual Select. Enter: v/V Leave: Esc',
\ '',
\ 'SURVIVAL', \ 'SURVIVAL',
\ ' Esc / jk Exit insert or visual mode', \ ' Esc / jk Exit insert or visual mode',
\ ' :q! + Enter Quit without saving', \ ' :q! + Enter Quit without saving',
\ ' ,x Save and quit | ,w Save', \ ' ,x Save+quit ,w Save Ctrl+s Save (any mode)',
\ ' Ctrl+s Save (normal + insert mode)', \ ' :w!! Sudo save (when you forgot to open as root)',
\ '', \ '',
\ 'FILES & NAVIGATION', \ 'FILES & SEARCH',
\ ' Ctrl+p Fuzzy find file (git-aware, FZF)', \ ' Ctrl+p Fuzzy find file (git-aware)',
\ ' ,e Open netrw file browser', \ ' ,e / ,E File browser / vertical split',
\ ' ,E Open netrw in vertical split', \ ' ,b Search open buffers',
\ ' ,b Search open buffers (FZF)',
\ ' ,rg Search project contents (ripgrep)', \ ' ,rg Search project contents (ripgrep)',
\ ' ,rG Ripgrep word under cursor', \ ' ,rG Ripgrep word under cursor',
\ ' ,fh Recent files history',
\ ' ,fl / ,fL Search lines in buffer / all buffers',
\ ' ,fc Commands | ,fm Marks',
\ ' ,f/ / ,f: Search / command history',
\ ' ,, Switch to last file (Ctrl+^)', \ ' ,, Switch to last file (Ctrl+^)',
\ ' Ctrl+o / i Jump back / forward in history',
\ '', \ '',
\ 'CODE INTELLIGENCE (vim-lsp)', \ 'CODE INTELLIGENCE (vim-lsp)',
\ ' gd Go to definition', \ ' gd Definition gy Type def gi Impl gr Refs',
\ ' gy Go to type definition', \ ' K Hover documentation',
\ ' gi Go to implementation', \ ' [g / ]g Prev / next LSP diagnostic',
\ ' gr Show references', \ ' [e / ]e Prev / next ALE error',
\ ' K Hover documentation', \ ' ,ca Code action ,rn Rename ,f Format',
\ ' [g / ]g Prev / next LSP diagnostic', \ ' ,o File outline ,ws Workspace symbols',
\ ' [e / ]e Prev / next ALE error', \ ' ,cr Run current file',
\ ' ,ca Code action',
\ ' ,rn Rename symbol',
\ ' ,f Format buffer (or visual selection)',
\ ' ,o File outline (symbols)',
\ ' ,ws Workspace symbols',
\ '', \ '',
\ 'MARKDOWN', \ 'MARKDOWN & WRITING',
\ ' ,mp Open live browser preview (previm)', \ ' ,mp Live browser preview (previm)',
\ ' ,mt Table of contents', \ ' ,mt Table of contents',
\ ' zr Unfold all headings', \ ' ,zen Zen mode (Goyo + Limelight)',
\ ' zm Fold all headings', \ ' zr / zm Unfold / fold all headings',
\ '', \ '',
\ 'EDITING', \ 'EDITING',
\ ' gc Toggle comment (visual mode too)', \ ' gc Toggle comment (visual mode too)',
\ ' s + 2 chars EasyMotion — jump anywhere on screen', \ ' s + 2 chars EasyMotion jump anywhere',
\ ' ,j / ,k EasyMotion — line motions', \ ' ,u / F5 Undo tree',
\ ' ,u / F5 Undo tree (visual branch history)', \ ' ,y / ,Y Yank to system clipboard',
\ ' ,y / ,Y Yank / yank line to system clipboard', \ ' Alt+j / Alt+k Move line down / up',
\ ' ,p / ,P Paste from system clipboard', \ ' ,F Re-indent file ,W Strip trailing whitespace',
\ ' Alt+j / Alt+k Move line down / up (also visual mode)',
\ ' ,F Re-indent entire file',
\ ' ,W Strip trailing whitespace',
\ ' ,* Search and replace word under cursor', \ ' ,* Search and replace word under cursor',
\ '', \ '',
\ 'SPELLING',
\ ' ,ss Toggle spell checking',
\ ' ,sn / ,sp Next / prev misspelling',
\ ' ,sa Add word to dictionary',
\ ' ,s? Suggest corrections',
\ '',
\ 'GIT', \ 'GIT',
\ ' ,gs Status ,gd Diff ,gb Blame', \ ' ,gs Status ,gd Diff ,gb Blame',
\ ' ,gc Commit ,gp Push ,gl Pull', \ ' ,gc Commit ,gp Push ,gl Pull',
\ ' [x / ]x Navigate git conflict markers',
\ '', \ '',
\ 'WINDOWS & PANES', \ 'WINDOWS & PANES',
\ ' Ctrl+h/j/k/l Navigate Vim splits and tmux panes', \ ' Ctrl+h/j/k/l Navigate splits and tmux panes',
\ ' ,h / ,l Prev / next buffer', \ ' ,h / ,l Prev / next buffer ,bd Close buffer',
\ ' ,bd Close buffer (keep layout)', \ ' ,z Maximize / restore current window',
\ ' ,tn / ,tc New tab / close tab | ,tl Last tab', \ ' ,tv / ,th Terminal (vertical / horizontal)',
\ ' ,tv / ,th Open terminal (vertical / horizontal)',
\ ' Esc Esc Exit terminal mode', \ ' Esc Esc Exit terminal mode',
\ ' ,= / ,- Resize height (grow / shrink)', \ ' ,= / ,- Resize height ,+ / ,_ Resize width',
\ ' ,+ / ,_ Resize width (grow / shrink)', \ '',
\ 'QUICKFIX',
\ ' ,qo / ,qc Open / close quickfix',
\ ' ]q / [q Next / prev quickfix entry',
\ '', \ '',
\ 'UTILITIES', \ 'UTILITIES',
\ ' ,ev / ,sv Edit / reload ~/.vimrc', \ ' ,ev / ,sv Edit / reload ~/.vimrc',
\ ' ,cp / ,cf Copy file path / filename to clipboard', \ ' ,cp / ,cf Copy file path / filename to clipboard',
\ ' ,ms Open scratch markdown buffer', \ ' ,ms Scratch buffer ,cd CD to file dir',
\ ' ,cd Change CWD to current file directory', \ ' ,ss Toggle spell ,so Source current vim file',
\ ' F2 Paste F3 Line# F4 Relative# F6 Invisible', \ ' F2 Paste F3 Line# F4 Relative# F6 Invisible',
\ '',
\ 'SESSION',
\ ' :Obsess Start tracking session',
\ ' :Obsess! Stop tracking',
\ '', \ '',
\ '(press q to close)', \ '(press q to close)',
\ ]) \ ])
@ -1132,11 +1163,100 @@ function! s:CheatSheet() abort
endfunction endfunction
nnoremap <silent> <leader>? :call <SID>CheatSheet()<CR> nnoremap <silent> <leader>? :call <SID>CheatSheet()<CR>
" ============================================================================
" => Yank Highlight (flash yanked region for visual feedback)
" ============================================================================
if exists('##TextYankPost') && has('timers')
function! s:YankHighlight() abort
if v:event.operator !=# 'y' | return | endif
let l:m = matchadd('IncSearch',
\ printf('\%%>%dl\%%<%dl', line("'[") - 1, line("']") + 1))
call timer_start(150, {-> matchdelete(l:m)})
endfunction
augroup ChopstickYankHL
autocmd!
autocmd TextYankPost * call s:YankHighlight()
augroup END
endif
" ============================================================================
" => Auto-Clear Search Highlight (clear after idle)
" ============================================================================
augroup ChopstickSearchHL
autocmd!
autocmd CursorHold * if get(v:, 'hlsearch', 0) | let v:hlsearch = 0 | endif
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' | execute '!gcc -o /tmp/a.out ' . l:file . ' && /tmp/a.out'
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>
" ============================================================================
" => Git Conflict Navigation ([x / ]x)
" ============================================================================
nnoremap <silent> ]x /^\(<<<<<<<\\|=======\\|>>>>>>>\)<CR>
nnoremap <silent> [x ?^\(<<<<<<<\\|=======\\|>>>>>>>\)<CR>
" ============================================================================
" => Window Maximize Toggle (,z)
" ============================================================================
function! s:ToggleMaximize() abort
if exists('t:maximize_session')
execute t:maximize_session
unlet t:maximize_session
else
let t:maximize_session = winrestcmd()
resize | vertical resize
endif
endfunction
nnoremap <silent> <leader>z :call <SID>ToggleMaximize()<CR>
" ============================================================================
" => Sudo Save (:w!! when you forget to open as root)
" ============================================================================
cnoremap w!! w !sudo tee > /dev/null %
" ============================================================================
" => QuickFix Improvements
" ============================================================================
augroup ChopstickQF
autocmd!
autocmd QuickFixCmdPost [^l]* cwindow
autocmd QuickFixCmdPost l* lwindow
augroup END
nnoremap <silent> ]q :cnext<CR>
nnoremap <silent> [q :cprev<CR>
" ============================================================================ " ============================================================================
" => Debug Helpers " => Debug Helpers
" ============================================================================ " ============================================================================
" Show syntax highlight stack for word under cursor
nnoremap <leader>sh :call <SID>SynStack()<CR> nnoremap <leader>sh :call <SID>SynStack()<CR>
function! <SID>SynStack() function! <SID>SynStack()
if !exists("*synstack") | return | endif if !exists("*synstack") | return | endif

View file

@ -142,13 +142,15 @@ K Show documentation
| `[g` | Jump to previous diagnostic | | `[g` | Jump to previous diagnostic |
| `K` | Read the error message | | `K` | Read the error message |
| `,ca` | Apply code action / auto-fix | | `,ca` | Apply code action / auto-fix |
| `,cr` | Run current file (auto-detects language) |
### Markdown ### Markdown & Writing
| Key | Action | | Key | Action |
|-----|--------| |-----|--------|
| `,mp` | Open live preview in browser | | `,mp` | Open live preview in browser |
| `,mt` | Table of contents (side window) | | `,mt` | Table of contents (side window) |
| `,zen` | Zen mode (Goyo + Limelight) |
| `zr` / `zm` | Unfold / fold all headings | | `zr` / `zm` | Unfold / fold all headings |
Formatting in the buffer is live: `**bold**` renders as bold, Formatting in the buffer is live: `**bold**` renders as bold,
@ -164,6 +166,7 @@ the cursor enters that line.
,gc commit ,gc commit
,gp push ,gp push
,gl pull ,gl pull
[x / ]x navigate conflict markers
``` ```
--- ---
@ -192,28 +195,31 @@ CODE
K Show documentation K Show documentation
[g / ]g Prev / next LSP diagnostic [g / ]g Prev / next LSP diagnostic
[e / ]e Prev / next ALE error [e / ]e Prev / next ALE error
,rn Rename symbol ,rn Rename ,ca Code action ,f Format
,ca Code action ,cr Run current file
,f Format buffer / selection
MARKDOWN
,mp Live preview | ,mt Table of contents
GIT
,gs Status | ,gd Diff | ,gb Blame
,gc Commit | ,gp Push | ,gl Pull
WINDOWS / PANES
Ctrl+h/j/k/l Move between Vim windows or tmux panes
,h / ,l Prev / next buffer
,tv / ,th Terminal (vertical / horizontal)
Esc Esc Exit terminal mode
,u Undo tree
SEARCH SEARCH
,rg Ripgrep ,rG Ripgrep word ,fh Recent files
,fl Lines in buffer ,fc Commands ,fm Marks
/text Forward | ?text Backward | n next | N prev /text Forward | ?text Backward | n next | N prev
// Search visually selected text // Search visually selected text
,* Replace word under cursor (file-wide) ,* Replace word under cursor (file-wide)
MARKDOWN
,mp Preview | ,mt TOC | ,zen Zen mode
GIT
,gs Status ,gd Diff ,gb Blame
,gc Commit ,gp Push ,gl Pull
[x / ]x Navigate conflict markers
WINDOWS
Ctrl+h/j/k/l Navigate splits and tmux panes
,z Maximize / restore window
,h / ,l Prev / next buffer
,tv / ,th Terminal (vertical / horizontal)
Esc Esc Exit terminal mode
]q / [q Next / prev quickfix entry
``` ```
--- ---

467
README.md
View file

@ -1,14 +1,11 @@
# chopsticks # chopsticks
> Flowing vim for any machine — SSH servers included. > Flowing vim for any machine — SSH servers included.
> Solarized · vim-lsp (no Node.js) · Markdown-first · One-command install.
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square)](LICENSE) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square)](LICENSE)
[![Vim 8.0+](https://img.shields.io/badge/Vim-8.0%2B-brightgreen?style=flat-square)](https://www.vim.org/) [![Vim 8.0+](https://img.shields.io/badge/Vim-8.0%2B-brightgreen?style=flat-square)](https://www.vim.org/)
[![Platform](https://img.shields.io/badge/platform-macOS%20%7C%20Linux-lightgrey?style=flat-square)](#installation) [![Platform](https://img.shields.io/badge/platform-macOS%20%7C%20Linux-lightgrey?style=flat-square)](#installation)
[![Release](https://img.shields.io/github/v/release/m1ngsama/chopsticks?style=flat-square&label=release&color=orange)](https://github.com/m1ngsama/chopsticks/releases) [![Release](https://img.shields.io/github/v/release/m1ngsama/chopsticks?style=flat-square&label=release&color=orange)](https://github.com/m1ngsama/chopsticks/releases)
[![Last Commit](https://img.shields.io/github/last-commit/m1ngsama/chopsticks?style=flat-square)](https://github.com/m1ngsama/chopsticks/commits/main)
[![Stars](https://img.shields.io/github/stars/m1ngsama/chopsticks?style=flat-square)](https://github.com/m1ngsama/chopsticks/stargazers)
```bash ```bash
curl -fsSL https://raw.githubusercontent.com/m1ngsama/chopsticks/main/get.sh | bash curl -fsSL https://raw.githubusercontent.com/m1ngsama/chopsticks/main/get.sh | bash
@ -16,45 +13,26 @@ curl -fsSL https://raw.githubusercontent.com/m1ngsama/chopsticks/main/get.sh | b
--- ---
## Contents ## What You Get
- [Design Principles](#design-principles) **30 plugins. One command. Zero Node.js dependency.**
- [Requirements](#requirements)
- [Installation](#installation)
- [LSP](#lsp)
- [Key Mappings](#key-mappings)
- [Markdown](#markdown)
- [Features](#features)
- [Plugins](#plugins)
- [Customization](#customization)
- [Troubleshooting](#troubleshooting)
--- | Feature | What it does |
|---------|-------------|
## Design Principles | **LSP everywhere** | Completion, go-to-definition, hover docs, rename — pure VimScript, works over SSH |
| **Format on save** | ALE runs black, prettier, gofmt automatically. Errors show in statusline. |
| Principle | What it means | | **Fuzzy everything** | Files, buffers, grep, tags, marks, command history — all via FZF with preview |
|-----------|--------------| | **Zen mode** | Goyo + Limelight: distraction-free writing. `,zen` and the world disappears |
| **Flowing writing** | Every plugin earns its place by reducing interruptions to thought | | **Run any file** | `,cr` detects filetype and runs it: Python, Go, Rust, JS, C, Shell, and more |
| **No Node.js** | vim-lsp runs on pure VimScript — works on any machine, including SSH servers | | **Smart indentation** | vim-sleuth auto-detects tabs vs spaces from existing files. No config needed. |
| **Solarized** | One palette, everywhere — vim statusline matches tmux bar exactly | | **Yank highlight** | Yanked text flashes — instant visual feedback, never guess what you copied |
| **TTY-aware** | SSH and console environments degrade gracefully without breaking | | **Search that clears** | Search highlights disappear after you stop moving. No more `,<cr>` spam. |
| **KISS** | No icon fonts, no Nerd Font glyphs — plain ASCII throughout | | **Git workflow** | Status, diff, blame, push, pull. Conflict marker navigation with `[x`/`]x`. |
| **Window maximize** | `,z` toggles current split to full screen and back. |
--- | **Sudo save** | `:w!!` when you forgot to open as root |
| **ALE in statusline** | Error and warning counts always visible — no surprises |
## Requirements | **30+ key mappings** | Every common action is one or two keystrokes away. `,?` shows them all. |
| **TTY-aware** | SSH, console, slow connections — degrades gracefully, never breaks |
| Tool | Role |
|------|------|
| Vim 8.0+ | Required — `install.sh` installs it if missing |
| git | Required |
| curl | Required |
| ripgrep | Recommended — enables `,rg` project search |
| fzf | Recommended — enables `Ctrl+p` fuzzy finder |
| Node.js | Optional — enables npm formatters (prettier, eslint) |
`install.sh` detects your environment and installs missing dependencies.
--- ---
@ -66,12 +44,6 @@ curl -fsSL https://raw.githubusercontent.com/m1ngsama/chopsticks/main/get.sh | b
curl -fsSL https://raw.githubusercontent.com/m1ngsama/chopsticks/main/get.sh | bash curl -fsSL https://raw.githubusercontent.com/m1ngsama/chopsticks/main/get.sh | bash
``` ```
Non-interactive / CI:
```bash
curl -fsSL https://raw.githubusercontent.com/m1ngsama/chopsticks/main/get.sh | bash -s -- --yes
```
### Git clone ### Git clone
```bash ```bash
@ -79,95 +51,48 @@ git clone https://github.com/m1ngsama/chopsticks.git ~/.vim
cd ~/.vim && ./install.sh cd ~/.vim && ./install.sh
``` ```
### What the installer does ### Non-interactive / CI
1. Detects OS and package manager
2. Verifies or installs `curl`, `git`, `vim`
3. Backs up existing `~/.vimrc`, then symlinks `~/.vimrc → ~/.vim/.vimrc`
4. Installs vim-plug and runs `:PlugInstall`
5. Offers to install system tools (ripgrep, fzf, ctags, shellcheck, hadolint, marksman)
6. Offers to install npm formatters (prettier, eslint, etc.) — requires Node.js
7. Offers to install Python formatters/linters (black, isort, flake8, etc.)
8. Offers to install Go tools (gopls, goimports, staticcheck)
9. Offers to append vim-tmux-navigator bindings to `~/.tmux.conf`
**Supported platforms:** macOS (Homebrew), Debian/Ubuntu (apt), Arch (pacman), Fedora (dnf).
---
## LSP
Code intelligence is provided by **vim-lsp** — a pure VimScript LSP client with no
Node.js dependency. It works on any machine, including servers accessed via SSH.
Install a language server for the current file:
```vim
:LspInstallServer " auto-detects filetype and installs the correct server
:LspStatus " check server status
```
Supported languages and their servers:
| Language | Server |
|----------|--------|
| Python | pylsp |
| JavaScript / TypeScript | typescript-language-server |
| Go | gopls |
| Rust | rust-analyzer |
| C / C++ | clangd |
| Shell | bash-language-server |
| HTML | vscode-html-language-server |
| CSS / SCSS | vscode-css-language-server |
| JSON | vscode-json-language-server |
| YAML | yaml-language-server |
| Markdown | marksman |
| SQL | sqls |
**Note:** While vim-lsp itself needs no Node.js, some language servers (TypeScript,
HTML, CSS, JSON, YAML) are npm packages that require Node.js to run. Python (pylsp),
Go (gopls), and Rust (rust-analyzer) language servers do not need Node.js.
**Markdown LSP** requires `marksman` as a standalone binary:
```bash ```bash
brew install marksman # macOS curl -fsSL ... | bash -s -- --yes
sudo pacman -S marksman # Arch
# or: install.sh handles it automatically
``` ```
**Supported:** macOS (Homebrew), Debian/Ubuntu (apt), Arch (pacman), Fedora (dnf).
The installer detects your environment, installs vim-plug, plugins, and offers
system tools (ripgrep, fzf, ctags, shellcheck, marksman) plus language-specific
formatters and linters.
--- ---
## Key Mappings ## Key Mappings
**Leader key:** `,` (comma) **Leader key:** `,` (comma). Press `,?` to open the built-in cheat sheet.
Press `,?` at any time to open the built-in cheat sheet. ### Files and Search
### Files and Buffers
| Key | Action | | Key | Action |
|-----|--------| |-----|--------|
| `Ctrl+p` | Fuzzy file search — git-aware (FZF) | | `Ctrl+p` | Fuzzy file search — git-aware |
| `,e` | Open netrw file browser | | `,b` | Search open buffers |
| `,E` | Open netrw in vertical split | | `,rg` | Project-wide ripgrep search |
| `,b` | Search open buffers (FZF) | | `,rG` | Ripgrep word under cursor |
| `,rg` | Project-wide search (ripgrep + FZF) | | `,fh` | Recent files history |
| `,rG` | Ripgrep word under cursor (fixed-string) | | `,fl` / `,fL` | Search lines in buffer / all buffers |
| `,fc` | Commands | `,fm` Marks |
| `,f/` / `,f:` | Search / command history |
| `,e` / `,E` | File browser / vertical split |
| `,,` | Switch to last file | | `,,` | Switch to last file |
| `,l` / `,h` | Next / previous buffer | | `,cd` | Change CWD to current file's directory |
| `,bd` | Close current buffer (preserves window layout) |
| `,wa` | Save all open buffers |
| `,cd` | Change working directory to current file's directory |
### Code Intelligence (vim-lsp) ### Code Intelligence (vim-lsp)
| Key | Action | | Key | Action |
|-----|--------| |-----|--------|
| `gd` | Go to definition | | `gd` | Go to definition |
| `gy` | Go to type definition | | `gy` | Type definition |
| `gi` | Go to implementation | | `gi` | Implementation |
| `gr` | Show references | | `gr` | References |
| `K` | Hover documentation | | `K` | Hover documentation |
| `[g` / `]g` | Previous / next LSP diagnostic | | `[g` / `]g` | Previous / next LSP diagnostic |
| `[e` / `]e` | Previous / next ALE error | | `[e` / `]e` | Previous / next ALE error |
@ -176,104 +101,91 @@ Press `,?` at any time to open the built-in cheat sheet.
| `,ca` | Code action | | `,ca` | Code action |
| `,o` | File outline (symbols) | | `,o` | File outline (symbols) |
| `,ws` | Workspace symbols | | `,ws` | Workspace symbols |
| `Tab` / `Shift+Tab` | Navigate completion popup | | `,cr` | **Run current file** (auto-detects language) |
| `Enter` | Confirm completion |
### Markdown Install LSP servers on demand:
```vim
:LspInstallServer " auto-detects filetype
:LspStatus " check server status
```
| Language | Server |
|----------|--------|
| Python | pylsp |
| JS / TS | typescript-language-server |
| Go | gopls |
| Rust | rust-analyzer |
| C / C++ | clangd |
| Shell | bash-language-server |
| HTML / CSS / JSON / YAML | vscode-*-language-server |
| Markdown | marksman |
| SQL | sqls |
### Markdown and Writing
| Key | Action | | Key | Action |
|-----|--------| |-----|--------|
| `,mp` | Open live preview in browser (previm) | | `,zen` | **Zen mode** — Goyo + Limelight, distraction-free |
| `,mt` | Table of contents (side window) | | `,mp` | Live browser preview (previm) |
| `,mt` | Table of contents |
| `zr` / `zm` | Unfold / fold all headings | | `zr` / `zm` | Unfold / fold all headings |
### Git (vim-fugitive) Markdown buffers automatically enable `wrap`, `spell`, and concealment
(bold renders as bold, headings hide `#` markers, raw syntax shows on cursor line).
### Git
| Key | Action | | Key | Action |
|-----|--------| |-----|--------|
| `,gs` | Git status | | `,gs` | Status |
| `,gc` | Git commit | | `,gd` | Diff |
| `,gp` | Git push | | `,gb` | Blame |
| `,gl` | Git pull | | `,gc` | Commit |
| `,gd` | Git diff | | `,gp` | Push |
| `,gb` | Git blame | | `,gl` | Pull |
| `[x` / `]x` | Navigate conflict markers |
### Editing ### Editing
| Key | Action | | Key | Action |
|-----|--------| |-----|--------|
| `s` + 2 chars | EasyMotion — jump anywhere on screen | | `s` + 2 chars | EasyMotion — jump anywhere on screen |
| `gc` | Toggle comment (visual mode too) | | `gc` | Toggle comment (works in visual mode) |
| `Space` | Toggle code fold |
| `Y` | Yank to end of line | | `Y` | Yank to end of line |
| `Ctrl+d` / `Ctrl+u` | Half-page scroll, cursor centred | | `,y` / `,Y` | Yank to system clipboard |
| `Alt+j` / `Alt+k` | Move line down / up (normal and visual) | | `Alt+j` / `Alt+k` | Move line down / up |
| `,u` | Undo tree (visual branch history) | | `,u` | Undo tree (visual branch history) |
| `F2` | Toggle paste mode | | `,F` | Re-indent entire file |
| `F3` / `F4` | Toggle line numbers / relative numbers | | `,W` | Strip trailing whitespace |
| `F5` | Toggle undo tree | | `,*` | Search and replace word under cursor |
| `F6` | Toggle invisible characters |
| `gV` | Reselect last paste | | `gV` | Reselect last paste |
| `//` | Search visual selection | | `//` | Search visual selection |
### Survival ### Windows and Navigation
| Key | Action |
|-----|--------|
| `jk` | Exit insert mode |
| `Esc` | Exit insert / visual mode |
| `jk` | Exit insert mode |
| `Ctrl+s` | Save (any mode) |
| `,w` | Save |
| `,x` | Save and quit |
| `,q` | Quit |
| `,?` | Open cheat sheet |
### Windows, Tabs, tmux
| Key | Action | | Key | Action |
|-----|--------| |-----|--------|
| `Ctrl+h/j/k/l` | Navigate Vim splits **and** tmux panes | | `Ctrl+h/j/k/l` | Navigate Vim splits **and** tmux panes |
| `,tv` / `,th` | Open terminal (vertical / horizontal split) | | `,z` | **Maximize / restore** current window |
| `,h` / `,l` | Previous / next buffer |
| `,bd` | Close buffer (keep window layout) |
| `,tv` / `,th` | Terminal (vertical / horizontal) |
| `Esc Esc` | Exit terminal mode | | `Esc Esc` | Exit terminal mode |
| `,tn` / `,tc` | New tab / close tab | | `]q` / `[q` | Next / previous quickfix entry |
| `,tl` | Toggle to last tab | | `Ctrl+d` / `Ctrl+u` | Half-page scroll, cursor centred |
### Quick Reference
| Key | Action |
|-----|--------|
| `,?` | Open built-in cheat sheet |
| `jk` | Exit insert mode |
| `Ctrl+s` | Save (any mode) |
| `:w!!` | Sudo save |
| `,w` / `,x` / `,q` | Save / save+quit / quit |
| `,ev` / `,sv` | Edit / reload `~/.vimrc` | | `,ev` / `,sv` | Edit / reload `~/.vimrc` |
| `,cp` / `,cf` | Copy file path / filename to clipboard | | `,so` | Source current vim file |
| `,*` | Search and replace word under cursor | | `F2` Paste | `F3` Line# | `F4` Relative# | `F6` Invisible chars |
| `,F` | Re-indent entire file |
| `,W` | Strip trailing whitespace |
| `,ms` | Open scratch markdown buffer |
| `,ss` | Toggle spell checking |
---
## Markdown
chopsticks treats Markdown as a first-class language.
### In-buffer rendering (concealment)
`vim-markdown` hides syntax markers and renders formatting inline:
- `**bold**` displays as bold text
- `# Heading` hides the `#` characters
- Tables align automatically
The raw syntax reappears when the cursor enters that line.
### Live browser preview (previm)
```vim
,mp " open rendered preview in browser — updates on every save
```
No Node.js required. Uses `open` (macOS) or `xdg-open` (Linux).
### Table of contents
```vim
,mt " open TOC in a side window — press Enter to jump to heading
```
--- ---
@ -281,15 +193,24 @@ No Node.js required. Uses `open` (macOS) or `xdg-open` (Linux).
### Statusline ### Statusline
A native, hand-written statusline using the Solarized palette: Native, hand-written. Solarized palette. Shows mode, file, git branch, filetype,
ALE error/warning count, and cursor position. Background matches tmux bar.
``` ```
N ~/.vimrc [+] main [vim] 42:7 68% N ~/.vimrc [+] E:1 W:3 main [vim] 42:7 68%
``` ```
- Mode block changes colour by mode (Normal=yellow, Insert=blue, Visual=magenta, Replace=red) Mode block changes colour: Normal=yellow, Insert=blue, Visual=magenta, Replace=red.
- Git branch via vim-fugitive
- Background matches tmux status bar for a seamless bottom band ### Smart Defaults
- **vim-sleuth** auto-detects indentation from existing files
- **Yank highlight** flashes copied text for 150ms
- **Search highlight** auto-clears after cursor stops moving
- **QuickFix** auto-opens after `:grep`, `:make`, or `:Rg`
- **Format on save** via ALE (black, prettier, gofmt, rustfmt, etc.)
- **Auto-create directories** on save — write to `new/path/file.txt` without `mkdir` first
- **Cursor restore** — reopens files at the last cursor position
### Session Management ### Session Management
@ -300,9 +221,20 @@ A native, hand-written statusline using the Solarized palette:
Sessions auto-restore when you open Vim in the same directory. Sessions auto-restore when you open Vim in the same directory.
### Project-Local Config ### TTY / SSH Support
Drop a `.vimrc` in any project root to override settings: Detected automatically. In TTY mode:
- True colour and cursorline disabled
- FZF preview windows disabled
- IndentLine guides disabled
- Simplified statusline
- Syntax column limit reduced
### Large File Handling
Files over 10 MB: syntax highlighting, undo history, and linting automatically disabled.
### Project-Local Config
```vim ```vim
" my-project/.vimrc " my-project/.vimrc
@ -310,143 +242,44 @@ set shiftwidth=2
let g:ale_python_black_options = '--line-length=100' let g:ale_python_black_options = '--line-length=100'
``` ```
### tmux Integration
`Ctrl+h/j/k/l` navigates seamlessly between Vim splits and tmux panes.
Add to `~/.tmux.conf` (or let `install.sh` append it):
```tmux
is_vim="ps -o state= -o comm= -t '#{pane_tty}' | grep -iqE '^[^TXZ ]+ +(\S+\/)?g?(view|n?vim?x?)(diff)?$'"
bind-key -n 'C-h' if-shell "$is_vim" 'send-keys C-h' 'select-pane -L'
bind-key -n 'C-j' if-shell "$is_vim" 'send-keys C-j' 'select-pane -D'
bind-key -n 'C-k' if-shell "$is_vim" 'send-keys C-k' 'select-pane -U'
bind-key -n 'C-l' if-shell "$is_vim" 'send-keys C-l' 'select-pane -R'
```
### TTY / SSH Support
Detected automatically when `$TERM` is unset, `dumb`, `linux`, `screen`, or contains `builtin`. In TTY mode:
- True colour and cursorline disabled
- FZF preview windows disabled
- IndentLine guides disabled
- Simplified statusline (no colour)
- Syntax column limit reduced to 120 characters
### Large File Handling
Files over 10 MB automatically disable syntax highlighting, undo history, and
linting to prevent stalling.
--- ---
## Plugins ## Plugins (30)
### Navigation | Category | Plugins |
- **fzf + fzf.vim** — fuzzy finder for files, buffers, tags, ripgrep |----------|---------|
| **Navigation** | fzf, fzf.vim |
### Git | **Git** | vim-fugitive, vim-gitgutter |
- **vim-fugitive** — full Git integration | **LSP** | vim-lsp, vim-lsp-settings, asyncomplete.vim, asyncomplete-lsp.vim |
- **vim-gitgutter** — diff signs in the sign column | **Lint/Format** | ALE |
| **Editing** | vim-surround, vim-commentary, vim-repeat, vim-unimpaired, vim-sleuth, targets.vim, auto-pairs, vim-easymotion |
### LSP and Completion | **Language** | vim-javascript, yats.vim, vim-markdown, vim-go |
- **vim-lsp** — pure VimScript LSP client | **Writing** | previm, goyo.vim, limelight.vim |
- **vim-lsp-settings** — auto-configures language servers | **UI** | vim-solarized8, undotree, vim-startify, indentLine |
- **asyncomplete.vim** — async completion engine | **Session** | vim-obsession, vim-tmux-navigator |
- **asyncomplete-lsp.vim** — LSP completion source
### Linting and Formatting
- **ALE** — async linting and format-on-save
### Markdown
- **vim-markdown** — folding, concealment, table alignment
- **previm** — live browser preview
### Language Syntax
- **vim-javascript** — enhanced JS syntax
- **yats.vim** — TypeScript syntax
- **vim-go** — Go syntax and tooling
### Editing
- **vim-surround** — change/delete/add surroundings
- **vim-commentary**`gc` to toggle comments
- **vim-repeat** — repeat plugin maps with `.`
- **vim-unimpaired** — bracket shortcut pairs
- **targets.vim** — additional text objects
- **auto-pairs** — auto-close brackets and quotes
- **vim-easymotion**`s` + 2 chars to jump anywhere
### UI
- **vim-solarized8** — color scheme (truecolor support)
- **undotree** — visual undo branch history
- **vim-startify** — startup dashboard and session list
- **indentLine** — indent guides (non-TTY)
### Session and Navigation
- **vim-obsession** — session tracking
- **vim-tmux-navigator** — seamless Vim/tmux pane navigation
--- ---
## Customization ## Customization
### Per-project overrides Edit `~/.vimrc` directly (`,ev` opens it, `,sv` reloads). Per-project overrides
go in a `.vimrc` at your project root.
Create `.vimrc` in your project root:
```vim
" project/.vimrc
set shiftwidth=2
let g:ale_python_black_options = '--line-length=120'
```
### Modify keybindings
Edit `~/.vimrc` directly (`,ev` opens it from inside Vim). Reload with `,sv`.
--- ---
## Troubleshooting ## Troubleshooting
**Plugins not loading** | Problem | Solution |
|---------|----------|
```vim | Plugins not loading | `:PlugInstall` then `:PlugUpdate` |
:PlugInstall " install missing plugins | LSP not starting | `:LspInstallServer` for current filetype |
:PlugUpdate " update all plugins | Colors look wrong | `export COLORTERM=truecolor` in your shell rc |
``` | ALE linters missing | `which flake8 black prettier eslint` to verify PATH |
| `Ctrl+s` freezes terminal | Add `stty -ixon` to `~/.bashrc` or `~/.zshrc` |
**LSP server not starting** | Markdown preview broken | Ensure `open` (macOS) or `xdg-open` (Linux) works |
```vim
:LspInstallServer " install server for current filetype
:LspStatus " check server status
```
**Markdown preview not opening**
`previm` uses `open` (macOS) or `xdg-open` (Linux). Make sure a browser is
set as the default handler for HTML files.
**Colors look wrong**
```bash
export TERM=xterm-256color # add to ~/.bashrc or ~/.zshrc
export COLORTERM=truecolor # for true colour
```
**ALE linters not found**
```bash
which flake8 black prettier eslint # verify tools are on PATH
```
**`Ctrl+s` freezes the terminal**
Add `stty -ixon` to your `~/.bashrc`, `~/.zshrc`, or `~/.config/fish/config.fish`.
--- ---
## License ## License
[MIT](LICENSE) © m1ng [MIT](LICENSE)