Compare commits

..

No commits in common. "e5412e464e9ae6a2a328a363b67a4bc2df97bac6" and "002bc7bd3ebd7eaf145a669fec0759f724f2cced" have entirely different histories.

8 changed files with 29 additions and 150 deletions

4
.github/demo.tape vendored
View file

@ -24,8 +24,8 @@ Type "vim server.py"
Enter Enter
Sleep 3.5s Sleep 3.5s
# ── 2. Fuzzy find files (,ff → type → select) ──────────────────────────── # ── 2. Fuzzy find files (Ctrl+p → type → select) ──────────────────────────
Type ",ff" Ctrl+p
Sleep 1.5s Sleep 1.5s
Type "route" Type "route"
Sleep 2.5s Sleep 2.5s

View file

@ -2,8 +2,6 @@
## Unreleased ## Unreleased
## 2.2.0 — 2026-05-17
### Added ### Added
- `~/.config/chopsticks.vim` local pre-load config for profile and user choices - `~/.config/chopsticks.vim` local pre-load config for profile and user choices
@ -25,30 +23,6 @@
### Fixed ### Fixed
- Built-in plugin guards for `gzip`, `logiPat`, `rrhelper`, and
`spellfile_plugin` are now set in both `g:`-prefixed and unscoped
forms — those four old-style runtime plugins check `loaded_X` (no
`g:`), so the previous `g:loaded_X` lines did nothing. Saves ~270μs
total at startup, mostly from gzip
- `install.sh` interactive menu and QUICKSTART now show `,ff` (current
fuzzy-find binding) instead of the stale `Ctrl+p` left over from the
native-first cleanup; `.github/demo.tape` updated to match (the
binary GIF still shows `Ctrl+p` until someone regenerates it from a
fresh fixture)
- README badge and `install.sh` recommend Vim 8.1+ instead of 8.0+ —
the runtime conditionally relies on patches `8.1.0360` (diffopt) and
`8.1.1517` (completeopt+=popup), so 8.0 users hit option errors
- `ttimeoutlen` is now 50ms when `g:is_tty` (was 10ms unconditionally);
fixes F-keys, arrow keys, and Alt-prefixes fragmenting on SSH where
one-way latency exceeds 10ms. Local terminals keep the 10ms snappy
default
- `install.sh` no longer silently `PlugClean!`s user-added plugins from
`~/.vim/plugged`; it now lists undeclared plugin directories first and
asks before removing them (`--yes` skips the removal entirely)
- `install.sh` Python tools now prefer `pipx` and `pip3 --user` over
`pip3 install --break-system-packages`; the break-system path is gated
behind `CHOPSTICKS_ALLOW_BREAK_SYSTEM=1` so PEP 668 distros are no
longer silently polluted
- `g:loaded_logipat` typo → `g:loaded_logiPat` — logiPat was loading fully (0.478ms wasted) - `g:loaded_logipat` typo → `g:loaded_logiPat` — logiPat was loading fully (0.478ms wasted)
- `get.sh` now refuses to update an existing `~/.vim` git repo unless its - `get.sh` now refuses to update an existing `~/.vim` git repo unless its
origin is chopsticks origin is chopsticks
@ -59,9 +33,6 @@
### Changed ### Changed
- `install.sh` "First steps inside Vim" block now leads with `,?`
(cheat sheet) — the single best onboarding asset is now the first
thing a new user sees after install, not the fourth
- `set exrc`/`set secure` are now opt-in via `g:chopsticks_enable_exrc = 1`; - `set exrc`/`set secure` are now opt-in via `g:chopsticks_enable_exrc = 1`;
Vim no longer sources project-local `.vimrc`/`.exrc` from the working Vim no longer sources project-local `.vimrc`/`.exrc` from the working
directory by default directory by default

View file

@ -45,7 +45,7 @@ Esc back to Normal
## Find things ## Find things
``` ```
,ff fuzzy find file (git-aware) Ctrl+p fuzzy find file (git-aware)
,rg ripgrep project ,rg ripgrep project
,b search buffers ,b search buffers
,fh recent files ,fh recent files

View file

@ -10,7 +10,7 @@
<p align="center"> <p align="center">
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square" alt="MIT License"></a> <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square" alt="MIT License"></a>
<a href="https://www.vim.org/"><img src="https://img.shields.io/badge/Vim-8.1%2B-brightgreen?style=flat-square" alt="Vim 8.1+"></a> <a href="https://www.vim.org/"><img src="https://img.shields.io/badge/Vim-8.0%2B-brightgreen?style=flat-square" alt="Vim 8.0+"></a>
<a href="#install"><img src="https://img.shields.io/badge/platform-macOS%20%7C%20Linux-lightgrey?style=flat-square" alt="Platform"></a> <a href="#install"><img src="https://img.shields.io/badge/platform-macOS%20%7C%20Linux-lightgrey?style=flat-square" alt="Platform"></a>
<a href="https://github.com/m1ngsama/chopsticks/actions"><img src="https://img.shields.io/github/actions/workflow/status/m1ngsama/chopsticks/test.yml?style=flat-square&label=tests" alt="Tests"></a> <a href="https://github.com/m1ngsama/chopsticks/actions"><img src="https://img.shields.io/github/actions/workflow/status/m1ngsama/chopsticks/test.yml?style=flat-square&label=tests" alt="Tests"></a>
<a href="https://github.com/m1ngsama/chopsticks/releases"><img src="https://img.shields.io/github/v/release/m1ngsama/chopsticks?style=flat-square&color=orange" alt="Release"></a> <a href="https://github.com/m1ngsama/chopsticks/releases"><img src="https://img.shields.io/github/v/release/m1ngsama/chopsticks?style=flat-square&color=orange" alt="Release"></a>

View file

@ -519,7 +519,7 @@ fi
[[ -n "$VIM_BIN" ]] || die "vim installed but not found in PATH" [[ -n "$VIM_BIN" ]] || die "vim installed but not found in PATH"
ok "Found: $("$VIM_BIN" --version | head -n1) ($VIM_BIN)" ok "Found: $("$VIM_BIN" --version | head -n1) ($VIM_BIN)"
"$VIM_BIN" --version | grep -q 'Vi IMproved 8\|Vi IMproved 9' || \ "$VIM_BIN" --version | grep -q 'Vi IMproved 8\|Vi IMproved 9' || \
warn "Vim 8.1+ recommended for full async/LSP support — some features may not work" warn "Vim 8.0+ recommended for full async/LSP support — some features may not work"
# Node.js (optional — vim-lsp needs no Node.js; only npm formatters do) # Node.js (optional — vim-lsp needs no Node.js; only npm formatters do)
HAS_NODE=0; command -v node >/dev/null 2>&1 && HAS_NODE=1 HAS_NODE=0; command -v node >/dev/null 2>&1 && HAS_NODE=1
@ -656,59 +656,10 @@ VIMEOF
fi fi
} }
# Echoes one name per line for each directory under ~/.vim/plugged that no if [[ -d "$HOME/.vim/plugged" ]] && [[ -n "$(find "$HOME/.vim/plugged" -mindepth 1 -maxdepth 1 2>/dev/null)" ]]; then
# chopsticks profile/flag combination would declare. The detection script warn "PlugClean: removing plugins not listed in .vimrc from ~/.vim/plugged"
# bypasses local config and forces every opt-in on, so a plugin that's only
# loaded inside tmux or behind a flag does NOT show up as "extra". Empty
# output means PlugClean would be a no-op for the user's tree.
list_extra_plugins() {
local declared_file="$_TMPDIR/declared-plugins.txt"
local declare_script="$_TMPDIR/declared-plugins.vim"
local plugged="$HOME/.vim/plugged"
[[ -d "$plugged" ]] || return 0
cat > "$declare_script" <<'VIMEOF'
let g:chopsticks_local_config = '/dev/null'
let $TMUX = '1'
let g:chopsticks_profile = 'full'
let g:chopsticks_enable_auto_pairs = 1
let g:chopsticks_enable_terminal_keymaps = 1
execute 'source ' . fnameescape($CHOPSTICKS_VIMRC)
if !exists('g:plugs')
cquit
endif
call writefile(sort(keys(g:plugs)), $CHOPSTICKS_DECLARED_PLUGINS)
qa!
VIMEOF
CHOPSTICKS_VIMRC="$SCRIPT_DIR/.vimrc" \
CHOPSTICKS_DECLARED_PLUGINS="$declared_file" \
"$VIM_BIN" -u NONE -i NONE -es -N -S "$declare_script" >/dev/null 2>&1 || return 1
[[ -e "$declared_file" ]] || return 0
local plugin name
while IFS= read -r plugin; do
[[ -d "$plugin" ]] || continue
name="$(basename "$plugin")"
if ! grep -Fxq "$name" "$declared_file"; then
printf '%s\n' "$name"
fi
done < <(find "$plugged" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort)
}
extras="$(list_extra_plugins || true)"
if [[ -n "$extras" ]]; then
warn "Plugins under ~/.vim/plugged that the active profile does not declare:"
while IFS= read -r ex; do
[[ -n "$ex" ]] && echo " - $ex"
done <<< "$extras"
if [[ $AUTO_YES -eq 1 ]]; then
info "Leaving them in place (--yes mode). Run :PlugClean inside Vim to remove."
elif ask "Remove these directories with PlugClean!?"; then
_vim_run 'PlugClean!' || true # plug.vim may exit non-zero after removal; harmless
else
info "Leaving them in place. Run :PlugClean inside Vim if you change your mind."
fi
fi fi
_vim_run 'PlugClean!' || true # remove plugins no longer in vimrc; ignore exit code (none expected)
_vim_run 'PlugInstall --sync' || true # fzf post-install hook may exit non-zero; harmless _vim_run 'PlugInstall --sync' || true # fzf post-install hook may exit non-zero; harmless
verify_plugins || die "Plugin installation failed — retry with a stable network connection." verify_plugins || die "Plugin installation failed — retry with a stable network connection."
@ -748,11 +699,11 @@ fi
# ── System tools ───────────────────────────────────────────────────────────── # ── System tools ─────────────────────────────────────────────────────────────
if [[ $HAS_PKG_MGR -eq 1 ]]; then if [[ $HAS_PKG_MGR -eq 1 ]]; then
_I_RIPGREP=$_idx _I_RIPGREP=$_idx
_ITEMS+=("ripgrep|,rg / ,rG project-wide search · powers FZF preview|1") _ITEMS+=("ripgrep|,rg / ,rG project-wide search · powers Ctrl+p preview|1")
: $(( _idx++ )) : $(( _idx++ ))
_I_FZF=$_idx _I_FZF=$_idx
_ITEMS+=("fzf|,ff fuzzy file search · ,b buffers · ,rt tag search|1") _ITEMS+=("fzf|Ctrl+p fuzzy file search · ,b buffers · ,rt tag search|1")
: $(( _idx++ )) : $(( _idx++ ))
_I_CTAGS=$_idx _I_CTAGS=$_idx
@ -1015,45 +966,19 @@ elif [[ $_I_PYTHON -lt 0 ]] || ! _selected "$_I_PYTHON"; then
skip "Python tool suite (skipped by user)" skip "Python tool suite (skipped by user)"
SKIPPED+=("black" "isort" "flake8" "pylint" "yamllint" "sqlfluff") SKIPPED+=("black" "isort" "flake8" "pylint" "yamllint" "sqlfluff")
else else
# Prefer pipx (isolated per-tool venvs, the recommended path on PEP 668
# distros like Debian 12+/Ubuntu 23.04+). Bootstrap pipx via the system
# package manager if missing. Only fall back to --break-system-packages
# if the user explicitly opts in.
if ! command -v pipx >/dev/null 2>&1; then
if pkg_install pipx pipx python-pipx pipx 2>/dev/null; then
ok "pipx (recommended Python CLI installer)"
else
warn "pipx not available — falling back to pip3 --user"
fi
fi
case ":$PATH:" in
*":$HOME/.local/bin:"*) ;;
*) warn "\$HOME/.local/bin is not on PATH — pipx/pip --user tools will not be found"
info "Add to your shell rc: export PATH=\"\$HOME/.local/bin:\$PATH\"" ;;
esac
pip_install() { pip_install() {
local pkg="$1" check="${2:-$1}" local pkg="$1" check="${2:-$1}"
if command -v "$check" >/dev/null 2>&1; then if command -v "$check" >/dev/null 2>&1; then
ok "$pkg (already installed)"; return ok "$pkg (already installed)"; return
fi fi
if command -v pipx >/dev/null 2>&1 && pipx install --quiet "$pkg" 2>/dev/null; then if pip3 install --quiet "$pkg" 2>/dev/null; then
ok "$pkg (via pipx)"; INSTALLED+=("$pkg") ok "$pkg"; INSTALLED+=("$pkg")
return elif pip3 install --quiet --break-system-packages "$pkg" 2>/dev/null; then
warn "$pkg installed with --break-system-packages (consider using a virtualenv)"
ok "$pkg"; INSTALLED+=("$pkg")
else
fail "$pkg"; FAILED+=("$pkg")
fi fi
if pip3 install --quiet --user "$pkg" 2>/dev/null; then
ok "$pkg (via pip --user)"; INSTALLED+=("$pkg")
return
fi
if [[ "${CHOPSTICKS_ALLOW_BREAK_SYSTEM:-0}" == "1" ]] && \
pip3 install --quiet --break-system-packages "$pkg" 2>/dev/null; then
warn "$pkg installed with --break-system-packages (CHOPSTICKS_ALLOW_BREAK_SYSTEM=1)"
INSTALLED+=("$pkg")
return
fi
fail "$pkg — install pipx, or set CHOPSTICKS_ALLOW_BREAK_SYSTEM=1 to bypass PEP 668"
FAILED+=("$pkg")
} }
pip_install black pip_install black
pip_install isort pip_install isort
@ -1201,10 +1126,10 @@ echo -e " ${CYAN}vim .${NC} Open dashboard in current directory"
echo -e " ${CYAN}vim myfile${NC} Edit a specific file" echo -e " ${CYAN}vim myfile${NC} Edit a specific file"
echo "" echo ""
echo -e "${BOLD} First steps inside Vim${NC}" echo -e "${BOLD} First steps inside Vim${NC}"
echo -e " ${CYAN},?${NC} Open cheat sheet — your map of every keybinding"
echo -e " ${CYAN}Esc${NC} Exit insert mode → back to Normal" echo -e " ${CYAN}Esc${NC} Exit insert mode → back to Normal"
echo -e " ${CYAN},x${NC} Save and quit"
echo -e " ${CYAN}:q!${NC} + Enter Emergency quit without saving" echo -e " ${CYAN}:q!${NC} + Enter Emergency quit without saving"
echo -e " ${CYAN},x${NC} Save and quit"
echo -e " ${CYAN},?${NC} Open cheat sheet"
if [[ $CONFIG_PROFILE != "minimal" ]]; then if [[ $CONFIG_PROFILE != "minimal" ]]; then
echo -e " ${CYAN}:LspInstallServer${NC} Install LSP for current filetype" echo -e " ${CYAN}:LspInstallServer${NC} Install LSP for current filetype"
fi fi

View file

@ -42,10 +42,7 @@ set noerrorbells
set novisualbell set novisualbell
set t_vb= set t_vb=
set ttimeout set ttimeout
" Wait long enough on SSH/TTY for multi-byte key codes to arrive intact — set ttimeoutlen=10
" 10ms fragments F-keys, arrows, and Alt-prefixes when one-way latency > 10ms.
" 50ms is well under perceptible <Esc>→Normal delay locally.
let &ttimeoutlen = g:is_tty ? 50 : 10
if $COLORTERM ==# 'gnome-terminal' if $COLORTERM ==# 'gnome-terminal'
set t_Co=256 set t_Co=256

View file

@ -42,18 +42,16 @@ let g:chopsticks_markdown_conceal = get(g:, 'chopsticks_markdown_conceal',
let g:chopsticks_lsp_virtual_text = get(g:, 'chopsticks_lsp_virtual_text', let g:chopsticks_lsp_virtual_text = get(g:, 'chopsticks_lsp_virtual_text',
\ s:profile_full && !g:is_tty) \ s:profile_full && !g:is_tty)
" Skip built-in plugins we never use. " Skip built-in plugins we never use
" Modern plugins check g:loaded_X; older ones (gzip, logiPat, rrhelper,
" spellfile) check the unscoped loaded_X form, so we set both.
let g:loaded_2html_plugin = 1 let g:loaded_2html_plugin = 1
let g:loaded_getscriptPlugin = 1 let g:loaded_getscriptPlugin = 1
let g:loaded_gzip = 1 | let loaded_gzip = 1 let g:loaded_gzip = 1
let g:loaded_logiPat = 1 | let loaded_logiPat = 1 let g:loaded_logiPat = 1
let g:loaded_rrhelper = 1 | let loaded_rrhelper = 1 let g:loaded_rrhelper = 1
let g:loaded_tarPlugin = 1 let g:loaded_tarPlugin = 1
let g:loaded_vimballPlugin = 1 let g:loaded_vimballPlugin = 1
let g:loaded_zipPlugin = 1 let g:loaded_zipPlugin = 1
let g:loaded_tutor_mode_plugin = 1 let g:loaded_tutor_mode_plugin = 1
let g:loaded_spellfile_plugin = 1 | let loaded_spellfile_plugin = 1 let g:loaded_spellfile_plugin = 1
let g:loaded_openPlugin = 1 let g:loaded_openPlugin = 1
let g:loaded_manpager_plugin = 1 let g:loaded_manpager_plugin = 1

View file

@ -165,7 +165,7 @@ check_vim() {
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u "$TMP_ROOT/chopsticks path/.vimrc" \ XDG_CONFIG_HOME="$EMPTY_XDG" vim -u "$TMP_ROOT/chopsticks path/.vimrc" \
-i NONE -es -N -c 'qa!' 2>&1 -i NONE -es -N -c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u NONE -i NONE -es -N \ vim -u NONE -i NONE -es -N \
-c 'let g:chopsticks_profile = "minimal"' \ -c 'let g:chopsticks_profile = "minimal"' \
-c 'source .vimrc' \ -c 'source .vimrc' \
-c 'if has_key(g:plugs, "ale") || has_key(g:plugs, "vim-lsp") || has_key(g:plugs, "vim-lsp-settings") || has_key(g:plugs, "asyncomplete.vim") || has_key(g:plugs, "auto-pairs") | cquit | endif' \ -c 'if has_key(g:plugs, "ale") || has_key(g:plugs, "vim-lsp") || has_key(g:plugs, "vim-lsp-settings") || has_key(g:plugs, "asyncomplete.vim") || has_key(g:plugs, "auto-pairs") | cquit | endif' \
@ -257,18 +257,6 @@ check_vim() {
-c 'if maparg(",F", "n") !~# "gg=G" | cquit | endif' \ -c 'if maparg(",F", "n") !~# "gg=G" | cquit | endif' \
-c 'qa!' 2>&1 -c 'qa!' 2>&1
TERM=xterm-256color XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
-c 'if g:is_tty || &ttimeoutlen != 10 | cquit | endif' \
-c 'qa!' 2>&1
TERM=linux XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
-c 'if !g:is_tty || &ttimeoutlen != 50 | cquit | endif' \
-c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
-c 'if !exists("loaded_gzip") || !exists("loaded_logiPat") || !exists("loaded_rrhelper") || !exists("loaded_spellfile_plugin") | cquit | endif' \
-c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \ XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
-c 'silent! delcommand LspStatus' \ -c 'silent! delcommand LspStatus' \
-c 'silent! delcommand LspInstallServer' \ -c 'silent! delcommand LspInstallServer' \
@ -279,7 +267,7 @@ check_vim() {
-c 'qa!' 2>&1 -c 'qa!' 2>&1
grep -Fq 'OK vim-lsp stack (installed; not loaded yet)' "$TMP_ROOT/status-lsp-not-loaded.txt" grep -Fq 'OK vim-lsp stack (installed; not loaded yet)' "$TMP_ROOT/status-lsp-not-loaded.txt"
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u NONE -i NONE -es -N \ vim -u NONE -i NONE -es -N \
-c 'let g:chopsticks_profile = "minimal"' \ -c 'let g:chopsticks_profile = "minimal"' \
-c 'source .vimrc' \ -c 'source .vimrc' \
-c 'ChopsticksStatus' \ -c 'ChopsticksStatus' \
@ -334,7 +322,7 @@ check_vim() {
exit 1 exit 1
fi fi
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u NONE -i NONE -es -N \ vim -u NONE -i NONE -es -N \
-c 'let g:chopsticks_profile = "minimal"' \ -c 'let g:chopsticks_profile = "minimal"' \
-c 'source .vimrc' \ -c 'source .vimrc' \
-c 'normal ,?' \ -c 'normal ,?' \
@ -359,7 +347,7 @@ check_vim() {
-c 'if !&swapfile || !&writebackup || &directory !~# "\.vim/.swap" | cquit | endif' \ -c 'if !&swapfile || !&writebackup || &directory !~# "\.vim/.swap" | cquit | endif' \
-c 'qa!' 2>&1 -c 'qa!' 2>&1
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u NONE -i NONE -es -N \ vim -u NONE -i NONE -es -N \
-c 'let g:ale_fix_on_save = 0' \ -c 'let g:ale_fix_on_save = 0' \
-c 'source .vimrc' \ -c 'source .vimrc' \
-c 'if g:ale_fix_on_save != 0 | cquit | endif' \ -c 'if g:ale_fix_on_save != 0 | cquit | endif' \