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)
This commit is contained in:
m1ngsama 2026-03-29 16:36:43 +08:00 committed by GitHub
parent db16c2f8fc
commit 3daf725ad8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 174 additions and 69 deletions

1
.gitignore vendored
View file

@ -1 +1,2 @@
*.json
!coc-settings.json

50
.vimrc
View file

@ -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
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
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
@ -538,7 +555,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 +580,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)
@ -577,10 +594,11 @@ 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 client
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
let g:go_def_mode = 'gopls' " fallback if CoC unavailable (won't start without g:go_gopls_enabled)
let g:go_info_mode = 'gopls'
" 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
@ -655,7 +673,10 @@ if g:use_coc
endfunction
" Highlight symbol and its references on cursor hold
augroup CocHighlight
autocmd!
autocmd CursorHold * silent call CocActionAsync('highlight')
augroup END
" Symbol renaming
nmap <leader>rn <Plug>(coc-rename)
@ -832,15 +853,18 @@ fun! CleanExtraSpaces()
call setreg('/', old_query)
endfun
if has("autocmd")
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
endif
augroup END
" ============================================================================
" => Auto Commands
" ============================================================================
augroup ChopstickFiletype
autocmd!
" Return to last edit position when opening files
autocmd BufReadPost * if line("'\"") > 1 && line("'\"") <= line("$") | exe "normal! g'\"" | endif
@ -880,6 +904,7 @@ 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
augroup END
" ============================================================================
" => Status Line
@ -1071,7 +1096,10 @@ endfunction
" Additional optimizations for TTY/basic terminals
if g:is_tty
" Disable syntax highlighting for very large files in TTY
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%)
@ -1101,7 +1129,10 @@ set timeoutlen=500
if exists('g:plugs["vim-which-key"]')
" Register after plugins are loaded (autoload functions available at VimEnter)
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>
@ -1262,7 +1293,10 @@ if exists('g:plugs["vim-startify"]')
let g:startify_files_number = 10
" Required for NERDTree compatibility (prevents buftype conflicts)
augroup ChopstickStartify
autocmd!
autocmd User Startified setlocal buftype=
augroup END
endif
" ============================================================================

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

@ -94,9 +94,24 @@ 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_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"
# 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"
@ -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
@ -167,6 +194,7 @@ if ask "Install system tools (ripgrep, fzf, ctags, shellcheck, marksman)?"; then
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 "hadolint" hadolint "brew install hadolint"
install_sys "marksman" marksman "brew install marksman"
elif [[ $HAS_APT -eq 1 ]]; then
sudo apt-get update -qq
@ -174,6 +202,24 @@ if ask "Install system tools (ripgrep, fzf, ctags, shellcheck, marksman)?"; then
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"
# 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"
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"
@ -312,6 +367,7 @@ if [[ $HAS_GO -eq 1 ]]; then
}
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"