mirror of
https://oauth2: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@github.com/m1ngsama/chopsticks.git
synced 2026-05-10 19:10:59 +08:00
Compare commits
35 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 259744b5e3 | |||
| 67ffdfe99f | |||
| 1b9e8b3999 | |||
| ddb4c691b9 | |||
| d253583d0b | |||
| 7ff5326932 | |||
| a9e16d22d3 | |||
| db9b96f577 | |||
| cddb5fa725 | |||
| 28e4ed3794 | |||
| 4fd9a09948 | |||
| 01c67d841b | |||
| 196e88c2c8 | |||
| 4965b65041 | |||
| 2c7d190314 | |||
| a42c980d3e | |||
| f6631dfdf1 | |||
| 56c3917682 | |||
| 996b428809 | |||
| ba39fca13b | |||
| ecf0a1b260 | |||
| 565f2fc80b | |||
| 0686f9c6be | |||
| 73a55e18e6 | |||
| 0e92e6c1a8 | |||
| f8d45472dc | |||
| fbb692846b | |||
| 51b10aa16d | |||
| f6b6976bc0 | |||
| f43c8e5f0f | |||
| 5371521360 | |||
| b1746c72b7 | |||
| 261b6cdce6 | |||
| 2e37efe644 | |||
| 97ca2be139 |
26 changed files with 1681 additions and 794 deletions
BIN
.github/demo.gif
vendored
BIN
.github/demo.gif
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 460 KiB After Width: | Height: | Size: 990 KiB |
72
.github/demo.tape
vendored
72
.github/demo.tape
vendored
|
|
@ -1,47 +1,63 @@
|
||||||
|
# chopsticks demo — "what can I do with this?"
|
||||||
|
# Rule: NEVER use Escape to close FZF — always Enter (deterministic).
|
||||||
|
# Pacing: hold each scene long enough for the viewer to read.
|
||||||
|
|
||||||
Output .github/demo.gif
|
Output .github/demo.gif
|
||||||
Set Shell bash
|
Set Shell bash
|
||||||
Set FontSize 14
|
Set FontSize 14
|
||||||
Set Width 960
|
Set Width 1080
|
||||||
Set Height 540
|
Set Height 620
|
||||||
Set Theme "Builtin Solarized Dark"
|
Set Theme "Builtin Solarized Dark"
|
||||||
Set TypingSpeed 50ms
|
Set TypingSpeed 50ms
|
||||||
Set Padding 10
|
Set Padding 10
|
||||||
|
|
||||||
Type "vim ~/.vim/modules/core.vim"
|
# ── 0. Start API server in background ──────────────────────────────────────
|
||||||
|
Type "cd /tmp/demo-project"
|
||||||
|
Enter
|
||||||
|
Sleep 0.5s
|
||||||
|
Type "python3 serve.py > /dev/null 2>&1 &"
|
||||||
|
Enter
|
||||||
|
Sleep 1s
|
||||||
|
|
||||||
|
# ── 1. Open file — syntax highlighting + statusline ────────────────────────
|
||||||
|
Type "vim server.py"
|
||||||
|
Enter
|
||||||
|
Sleep 3.5s
|
||||||
|
|
||||||
|
# ── 2. Fuzzy find files (Ctrl+p → type → select) ──────────────────────────
|
||||||
|
Ctrl+p
|
||||||
|
Sleep 1.5s
|
||||||
|
Type "route"
|
||||||
|
Sleep 2.5s
|
||||||
|
Enter
|
||||||
|
Sleep 3s
|
||||||
|
|
||||||
|
# ── 3. Ripgrep project search (,rg → query → select) ──────────────────────
|
||||||
|
Type ",rg"
|
||||||
|
Sleep 1.5s
|
||||||
|
Type "def "
|
||||||
|
Sleep 3s
|
||||||
|
Enter
|
||||||
|
Sleep 3s
|
||||||
|
|
||||||
|
# ── 4. Curl the running API from inside Vim ────────────────────────────────
|
||||||
|
Type ":!curl -s localhost:8080/users | python3 -m json.tool"
|
||||||
|
Enter
|
||||||
|
Sleep 4.5s
|
||||||
Enter
|
Enter
|
||||||
Sleep 1.5s
|
Sleep 1.5s
|
||||||
|
|
||||||
# Show the code with solarized theme and statusline
|
# ── 5. Cheat sheet (,?) ───────────────────────────────────────────────────
|
||||||
Sleep 1s
|
# Reset to server.py so cheat sheet shows code on left, keys on right.
|
||||||
|
Type ":edit server.py"
|
||||||
# Use Ctrl+p for fuzzy find
|
|
||||||
Type "\x10"
|
|
||||||
Sleep 1s
|
|
||||||
Type "lsp"
|
|
||||||
Sleep 1s
|
|
||||||
Escape
|
|
||||||
Sleep 0.5s
|
|
||||||
|
|
||||||
# Go to definition
|
|
||||||
Type "gg"
|
|
||||||
Sleep 0.3s
|
|
||||||
Type "/mapleader"
|
|
||||||
Enter
|
Enter
|
||||||
Sleep 1s
|
Sleep 1s
|
||||||
|
|
||||||
# Show cheat sheet
|
|
||||||
Type ",?"
|
Type ",?"
|
||||||
Sleep 2s
|
Sleep 5.5s
|
||||||
Type "q"
|
Type "q"
|
||||||
Sleep 0.5s
|
Sleep 0.5s
|
||||||
|
|
||||||
# Open file browser
|
# ── done ───────────────────────────────────────────────────────────────────
|
||||||
Type ",e"
|
|
||||||
Sleep 1.5s
|
|
||||||
Escape
|
|
||||||
Sleep 0.5s
|
|
||||||
|
|
||||||
# Quit
|
|
||||||
Type ":qa!"
|
Type ":qa!"
|
||||||
Enter
|
Enter
|
||||||
Sleep 0.5s
|
Sleep 0.5s
|
||||||
|
|
|
||||||
49
.github/workflows/test.yml
vendored
49
.github/workflows/test.yml
vendored
|
|
@ -36,31 +36,40 @@ jobs:
|
||||||
cp modules/*.vim ~/.vim/modules/
|
cp modules/*.vim ~/.vim/modules/
|
||||||
|
|
||||||
- name: Install plugins
|
- name: Install plugins
|
||||||
run: vim -es -u .vimrc -c 'PlugInstall --sync' -c 'qa!' 2>&1 || true
|
|
||||||
|
|
||||||
- name: Test startup
|
|
||||||
run: |
|
run: |
|
||||||
vim -u .vimrc -es -N -c 'qa!' 2>&1
|
set +e
|
||||||
echo "Vim exited cleanly"
|
vim -i NONE -es -u .vimrc -N -c 'PlugInstall --sync' -c 'qa!' 2>&1
|
||||||
|
plug_status=$?
|
||||||
- name: Verify modules load
|
set -e
|
||||||
run: |
|
if [ "$plug_status" -ne 0 ]; then
|
||||||
vim -u .vimrc -N -c 'redir! > /tmp/test.txt | echo len(g:plugs) | redir END | qa!' 2>/dev/null
|
echo "PlugInstall exited with $plug_status; validating plugin directories"
|
||||||
PLUGS=$(cat /tmp/test.txt | tr -d '[:space:]')
|
|
||||||
echo "Plugins registered: $PLUGS"
|
|
||||||
if [ "$PLUGS" -lt 25 ]; then
|
|
||||||
echo "FAIL: expected 25+ plugins, got $PLUGS"
|
|
||||||
exit 1
|
|
||||||
fi
|
fi
|
||||||
|
for plugin in fzf fzf.vim vim-fugitive vim-gitgutter ale vim-lsp vim-lsp-settings asyncomplete.vim asyncomplete-lsp.vim vim-markdown; do
|
||||||
|
test -d "$HOME/.vim/plugged/$plugin" || {
|
||||||
|
echo "FAIL: missing plugin $plugin"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
done
|
||||||
|
|
||||||
- name: Measure startup time
|
- name: Run Vim smoke tests
|
||||||
run: |
|
run: scripts/test.sh vim
|
||||||
vim -u .vimrc --startuptime /tmp/startup.log -c 'qa!' 2>/dev/null
|
|
||||||
tail -1 /tmp/startup.log
|
|
||||||
|
|
||||||
shellcheck:
|
shellcheck:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Shellcheck install.sh
|
- name: Check test runner CLI
|
||||||
run: shellcheck install.sh get.sh
|
run: |
|
||||||
|
scripts/test.sh --help
|
||||||
|
scripts/test.sh list
|
||||||
|
- name: Run shell and installer tests
|
||||||
|
run: scripts/test.sh shell installer bootstrap
|
||||||
|
|
||||||
|
docs:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install markdownlint
|
||||||
|
run: npm install -g markdownlint-cli
|
||||||
|
- name: Lint Markdown
|
||||||
|
run: scripts/test.sh docs
|
||||||
|
|
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -2,3 +2,7 @@
|
||||||
*.swo
|
*.swo
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Session.vim
|
Session.vim
|
||||||
|
autoload/
|
||||||
|
plugged/
|
||||||
|
.swap/
|
||||||
|
.undo/
|
||||||
|
|
|
||||||
13
.markdownlint.json
Normal file
13
.markdownlint.json
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"default": true,
|
||||||
|
"MD013": false,
|
||||||
|
"MD022": false,
|
||||||
|
"MD024": {
|
||||||
|
"siblings_only": true
|
||||||
|
},
|
||||||
|
"MD032": false,
|
||||||
|
"MD033": false,
|
||||||
|
"MD040": false,
|
||||||
|
"MD041": false,
|
||||||
|
"MD060": false
|
||||||
|
}
|
||||||
15
.vimrc
15
.vimrc
|
|
@ -1,10 +1,18 @@
|
||||||
|
let g:chopsticks_dir = fnamemodify(resolve(expand('<sfile>')), ':h')
|
||||||
|
let s:xdg_config_home = !empty($XDG_CONFIG_HOME) && $XDG_CONFIG_HOME =~# '^/'
|
||||||
|
\ ? $XDG_CONFIG_HOME
|
||||||
|
\ : '~/.config'
|
||||||
|
let s:local_config = expand(get(g:, 'chopsticks_local_config',
|
||||||
|
\ s:xdg_config_home . '/chopsticks.vim'))
|
||||||
|
if filereadable(s:local_config)
|
||||||
|
execute 'source ' . fnameescape(s:local_config)
|
||||||
|
endif
|
||||||
|
|
||||||
if exists('g:chopsticks_loaded') | finish | endif
|
if exists('g:chopsticks_loaded') | finish | endif
|
||||||
let g:chopsticks_loaded = 1
|
let g:chopsticks_loaded = 1
|
||||||
|
|
||||||
let g:chopsticks_dir = fnamemodify(resolve(expand('<sfile>')), ':h')
|
|
||||||
|
|
||||||
function! s:load(mod) abort
|
function! s:load(mod) abort
|
||||||
execute 'source ' . g:chopsticks_dir . '/modules/' . a:mod . '.vim'
|
execute 'source ' . fnameescape(g:chopsticks_dir . '/modules/' . a:mod . '.vim')
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
call s:load('env')
|
call s:load('env')
|
||||||
|
|
@ -16,6 +24,5 @@ call s:load('navigation')
|
||||||
call s:load('lsp')
|
call s:load('lsp')
|
||||||
call s:load('lint')
|
call s:load('lint')
|
||||||
call s:load('git')
|
call s:load('git')
|
||||||
call s:load('writing')
|
|
||||||
call s:load('languages')
|
call s:load('languages')
|
||||||
call s:load('tools')
|
call s:load('tools')
|
||||||
|
|
|
||||||
159
CHANGELOG.md
Normal file
159
CHANGELOG.md
Normal file
|
|
@ -0,0 +1,159 @@
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- `~/.config/chopsticks.vim` local pre-load config for profile and user choices
|
||||||
|
- `g:chopsticks_enable_markdown_preview` to control Previm independently
|
||||||
|
- `g:chopsticks_profile` with `minimal`, `engineer`, and `full` profiles
|
||||||
|
- `.markdownlint.json` aligned with the project's README/changelog style
|
||||||
|
- `:ChopsticksStatus` diagnostic command — checks system tools, LSP servers, linters, formatters
|
||||||
|
- `,af` toggle format-on-save (ALE `fix_on_save`)
|
||||||
|
- `,gL` git log graph (last 20 commits)
|
||||||
|
- `,gC` FZF git commits search, `,gB` buffer commits
|
||||||
|
- Interactive installer profile selection for `minimal`, `engineer`, and `full`
|
||||||
|
- `install.sh --profile=minimal|engineer|full` for scripted profile selection
|
||||||
|
- `install.sh --dry-run` to show the resolved profile/config path without writes
|
||||||
|
- `install.sh --configure-only` to update local profile config without reinstalling
|
||||||
|
- `get.sh --dry-run` for safe bootstrap previews before clone/update/install
|
||||||
|
- `CHOPSTICKS_DEST=/absolute/path` to test or install the bootstrap target elsewhere
|
||||||
|
- `scripts/test.sh` local test runner reused by GitHub Actions
|
||||||
|
- `scripts/test.sh quick`, `--help`, and `list` for easier local test discovery
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- `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
|
||||||
|
origin is chopsticks
|
||||||
|
- Large file protection now stays active after filetype and syntax autocommands
|
||||||
|
- `g:ale_fix_on_save = 0` in local config is now respected
|
||||||
|
- Local config now respects absolute `XDG_CONFIG_HOME` instead of hardcoding
|
||||||
|
`~/.config`
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- `,?` cheat sheet is now profile-aware and hides LSP/ALE/preview/UndoTree keys
|
||||||
|
when those features are disabled
|
||||||
|
- Module reload/source paths now use `fnameescape()` so installs in paths with
|
||||||
|
spaces are handled correctly
|
||||||
|
- CI now verifies path-safe module loading, the local config hook, and
|
||||||
|
minimal-profile cheat sheet output
|
||||||
|
- Markdown now opens in quiet writing mode by default: no real-time markdownlint,
|
||||||
|
no Marksman LSP, no spell noise, no conceal, no sign column, and no realtime preview
|
||||||
|
- Native `s` is no longer shadowed by EasyMotion; use `,S` for the two-character jump
|
||||||
|
- `,w` now uses a normal `:write` instead of forced `:write!`
|
||||||
|
- Swap files are enabled again and stored under `~/.vim/.swap` for crash recovery
|
||||||
|
- Installer defaults are slimmer: only core search tools stay selected by default;
|
||||||
|
language and lint suites are opt-in
|
||||||
|
- `:ChopsticksStatus` now respects disabled LSP/lint profiles instead of reporting
|
||||||
|
intentionally disabled tools as missing
|
||||||
|
- `,sv` now clears the load guard before sourcing `$MYVIMRC`
|
||||||
|
- CI now verifies key plugin directories, Markdown quiet defaults, markdownlint,
|
||||||
|
and an explicit startup-time threshold
|
||||||
|
- Installer plugin validation now checks every plugin required by the active profile
|
||||||
|
- The optional tool menu now hides LSP/lint suites in `minimal` and selects
|
||||||
|
Marksman by default in `full`
|
||||||
|
- tmux integration is written as a managed block so future installer runs can
|
||||||
|
update it without appending duplicate bindings
|
||||||
|
- Installer cleanup now restores the cursor after interrupted checkbox menus
|
||||||
|
- Bootstrap dry-run now refuses unrelated existing git repos before any writes
|
||||||
|
- CI now shares shell, installer, bootstrap, docs, and Vim smoke checks with
|
||||||
|
the local test runner
|
||||||
|
- CI now checks the test runner help and group-list commands
|
||||||
|
- Skip 2 more built-in plugins: openPlugin, manpager (10 → 12 total)
|
||||||
|
- Remove deprecated `set ttyfast` (no-op since Vim 8)
|
||||||
|
- Add `grepprg=rg --vimgrep` — `:grep` now uses ripgrep + quickfix
|
||||||
|
- Add `diffopt` with histogram algorithm and indent-heuristic
|
||||||
|
- Consolidate FZF Rg/RgWord/GFiles commands (DRY refactor)
|
||||||
|
- vim-tmux-navigator: conditional load (only inside tmux), fallback `Ctrl+hjkl` mappings outside
|
||||||
|
- Add `Ctrl+hjkl` window navigation fallback when tmux-navigator not loaded
|
||||||
|
|
||||||
|
## 2.1.0 — 2025-04-22
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Cheat sheet (`,?`) — vertical sidebar, one key per line, section headers
|
||||||
|
- Previm markdown preview restored (lazy-loaded, `,mp`)
|
||||||
|
- `:LspInstallServer` added to cheat sheet
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Plugin count: 25 (restored previm, dropped 5 bloat plugins)
|
||||||
|
- QUICKSTART updated — removed stale references, improved first-launch guidance
|
||||||
|
|
||||||
|
## 2.0.0 — 2025-04-21
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Sidebar toggle (`,e` / `,E`) — left-side netrw with `topleft vertical`, winfixwidth, proper toggle
|
||||||
|
- Enriched statusline — SLMode, SLGit, SLAle, SLFlags
|
||||||
|
- Toggle feedback — F2/F3/F4/F6/`,ss` echo current state
|
||||||
|
- `vim .` layout — netrw left + Startify right (removed in later refactor)
|
||||||
|
- Interactive tutorial (`:ChopsticksLearn` — removed in later release)
|
||||||
|
|
||||||
|
### Removed (Unix minimalism refactor)
|
||||||
|
|
||||||
|
- **565 lines** of dead code and bloat
|
||||||
|
- 5 plugins: Goyo, Limelight, vim-obsession, indentLine, vim-unimpaired
|
||||||
|
- `modules/writing.vim` — folded into `languages.vim`
|
||||||
|
- `tutor/chopsticks.tutor` — removed (269 lines)
|
||||||
|
- Tab management keybindings (8 mappings), spell nav bindings (4 mappings)
|
||||||
|
- Dead functions: HasPaste(), CleanExtraSpaces(), ToggleNumber(), SynStack
|
||||||
|
- TTY welcome message, `,so`, `,ms`, `,sh` mappings
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- CI plugin threshold lowered to 20
|
||||||
|
- README: hero layout with demo GIF, badges, architecture diagram
|
||||||
|
- vim-markdown settings absorbed from writing.vim into languages.vim
|
||||||
|
|
||||||
|
## 1.3.0 — 2025-04-20
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Startup: 39ms → 19ms (51% faster)
|
||||||
|
- Dropped vim-unimpaired for performance
|
||||||
|
- Runtime tuning across modules
|
||||||
|
|
||||||
|
## 1.2.0 — 2025-04-19
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Hero README with demo GIF, CI badges
|
||||||
|
- GitHub Actions CI (startup test on macOS + Ubuntu, shellcheck)
|
||||||
|
- Issue/PR templates
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Documentation rewrite — clean, short, for engineers
|
||||||
|
|
||||||
|
## 1.1.0 — 2025-04-18
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- 12-module architecture (env → plugins → core → ui → editing → navigation → lsp → lint → git → writing → languages → tools)
|
||||||
|
- Zen mode (Goyo + Limelight)
|
||||||
|
- Run file (`,cr`) with auto filetype detection
|
||||||
|
- Smart search (SmartFiles, Rg, RgWord)
|
||||||
|
- EasyMotion, yank highlight, undo tree
|
||||||
|
- Robust installer (`get.sh`) with preflight checks
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- `.vimrc` split into 12 self-contained modules
|
||||||
|
- Comprehensive bug audit (14 fixes)
|
||||||
|
|
||||||
|
## 1.0.0 — 2025-04-16
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Initial Vim configuration — migrated from Neovim
|
||||||
|
- vim-plug plugin manager
|
||||||
|
- vim-lsp + asyncomplete (pure VimScript LSP)
|
||||||
|
- ALE linting + format-on-save
|
||||||
|
- FZF fuzzy finder
|
||||||
|
- Fugitive + GitGutter
|
||||||
|
- Solarized colorscheme
|
||||||
|
- TTY detection and graceful degradation
|
||||||
|
- Platform installer (macOS, Debian, Arch, Fedora)
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
## Rules
|
## Rules
|
||||||
|
|
||||||
1. **No Node.js dependencies.** The LSP engine is pure VimScript. Some language servers need Node — that's fine. The config itself must not.
|
1. **No Node.js dependencies.** The LSP engine is pure VimScript. Some language servers need Node — that's fine. The config itself must not.
|
||||||
2. **Startup matters.** Run `vim --startuptime /tmp/s.log -c qa!` before and after. If your change adds >1ms, it needs a good reason.
|
2. **Startup matters.** Run `vim -u .vimrc -i NONE --startuptime /tmp/s.log -es -N -c qa!` before and after. If your change adds >1ms, it needs a good reason.
|
||||||
3. **Works on TTY.** Test over SSH. If it breaks in a terminal without true color, fix it or gate it behind `g:is_tty`.
|
3. **Works on TTY.** Test over SSH. If it breaks in a terminal without true color, fix it or gate it behind `g:is_tty`.
|
||||||
4. **One module, one concern.** Don't put git config in lsp.vim.
|
4. **One module, one concern.** Don't put git config in lsp.vim.
|
||||||
|
|
||||||
|
|
@ -13,11 +13,25 @@
|
||||||
2. If it's not needed at startup, lazy-load it: `Plug 'foo/bar', { 'on': 'FooCommand' }`
|
2. If it's not needed at startup, lazy-load it: `Plug 'foo/bar', { 'on': 'FooCommand' }`
|
||||||
3. Put config in the appropriate module
|
3. Put config in the appropriate module
|
||||||
4. Update the cheat sheet in `modules/tools.vim` if you add keybindings
|
4. Update the cheat sheet in `modules/tools.vim` if you add keybindings
|
||||||
5. Test on both macOS and Linux
|
5. Run `scripts/test.sh vim` locally after installing plugins
|
||||||
|
6. Test on both macOS and Linux when changing terminal or package-manager behavior
|
||||||
|
|
||||||
|
## Local tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scripts/test.sh --help
|
||||||
|
scripts/test.sh quick
|
||||||
|
scripts/test.sh vim
|
||||||
|
```
|
||||||
|
|
||||||
|
`scripts/test.sh quick` runs shell, docs, installer, and bootstrap checks without requiring Vim plugins.
|
||||||
|
`scripts/test.sh vim` expects plugins to be installed under `~/.vim/plugged`.
|
||||||
|
Use `STARTUP_LIMIT_MS=150 scripts/test.sh vim` to match CI's startup threshold.
|
||||||
|
|
||||||
## Reporting bugs
|
## Reporting bugs
|
||||||
|
|
||||||
Open an issue. Include:
|
Open an issue. Include:
|
||||||
|
|
||||||
- OS and Vim version
|
- OS and Vim version
|
||||||
- Whether you're on SSH/TTY
|
- Whether you're on SSH/TTY
|
||||||
- Steps to reproduce
|
- Steps to reproduce
|
||||||
|
|
@ -25,6 +39,6 @@ Open an issue. Include:
|
||||||
## Code style
|
## Code style
|
||||||
|
|
||||||
- Named augroups with `autocmd!`
|
- Named augroups with `autocmd!`
|
||||||
- No comments explaining *what* — only *why*
|
- No comments explaining _what_ — only _why_
|
||||||
- `exists('g:plugs["..."]')` guards for plugin-dependent config
|
- `exists('g:plugs["..."]')` guards for plugin-dependent config
|
||||||
- Test with `vim -u .vimrc --startuptime /tmp/s.log -c qa!`
|
- Test with `scripts/test.sh vim`
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,28 @@ Five minutes from zero to a working Vim setup.
|
||||||
|
|
||||||
```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
|
||||||
|
curl -fsSL https://raw.githubusercontent.com/m1ngsama/chopsticks/main/get.sh | bash -s -- --profile=minimal
|
||||||
|
curl -fsSL https://raw.githubusercontent.com/m1ngsama/chopsticks/main/get.sh | bash -s -- --dry-run --profile=full
|
||||||
```
|
```
|
||||||
|
|
||||||
Open vim. Plugins install automatically on first launch (30-60s). Restart vim.
|
Open vim. First launch auto-installs plugins — **wait 30-60s, don't close vim**. Restart when done.
|
||||||
|
|
||||||
|
Default profile is `engineer`. Interactive installs ask for a profile first;
|
||||||
|
`--profile=minimal`, `--profile=engineer`, or `--profile=full` selects it
|
||||||
|
without prompting. You can later put `let g:chopsticks_profile = 'minimal'` in
|
||||||
|
`${XDG_CONFIG_HOME:-~/.config}/chopsticks.vim` for a smaller core-only setup,
|
||||||
|
or use `full` for the heavier Markdown/LSP feedback.
|
||||||
|
|
||||||
|
To switch later without reinstalling anything:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/.vim && ./install.sh --configure-only --profile=full
|
||||||
|
```
|
||||||
|
|
||||||
## Modes
|
## Modes
|
||||||
|
|
||||||
| Mode | Enter | Leave |
|
| Mode | Enter | Leave |
|
||||||
|------|-------|-------|
|
| ------ | --------------- | ------------- |
|
||||||
| Normal | startup default | — |
|
| Normal | startup default | — |
|
||||||
| Insert | `i` / `a` / `o` | `Esc` or `jk` |
|
| Insert | `i` / `a` / `o` | `Esc` or `jk` |
|
||||||
| Visual | `v` / `V` | `Esc` |
|
| Visual | `v` / `V` | `Esc` |
|
||||||
|
|
@ -26,8 +40,7 @@ Esc / jk back to Normal
|
||||||
,x save + quit
|
,x save + quit
|
||||||
:q! force quit
|
:q! force quit
|
||||||
Ctrl+s save from any mode
|
Ctrl+s save from any mode
|
||||||
,? cheat sheet
|
,? cheat sheet (toggle sidebar)
|
||||||
:ChopsticksLearn interactive tutorial
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Find things
|
## Find things
|
||||||
|
|
@ -53,7 +66,7 @@ K hover docs
|
||||||
Tab / S-Tab cycle completions
|
Tab / S-Tab cycle completions
|
||||||
```
|
```
|
||||||
|
|
||||||
Install language servers with `:LspInstallServer` (auto-detects filetype).
|
**First time in a new language?** Run `:LspInstallServer` — it auto-detects filetype and installs the right server. Do this once per language.
|
||||||
|
|
||||||
## Git
|
## Git
|
||||||
|
|
||||||
|
|
@ -68,7 +81,7 @@ Install language servers with `:LspInstallServer` (auto-detects filetype).
|
||||||
## Edit
|
## Edit
|
||||||
|
|
||||||
```
|
```
|
||||||
s + 2 chars EasyMotion jump
|
,S + 2 chars EasyMotion jump
|
||||||
gc toggle comment
|
gc toggle comment
|
||||||
cs"' change surrounding " to '
|
cs"' change surrounding " to '
|
||||||
Alt+j / Alt+k move line
|
Alt+j / Alt+k move line
|
||||||
|
|
@ -85,12 +98,23 @@ Ctrl+h/j/k/l splits + tmux panes
|
||||||
,tv / ,th terminal
|
,tv / ,th terminal
|
||||||
```
|
```
|
||||||
|
|
||||||
## Write prose
|
## Markdown
|
||||||
|
|
||||||
```
|
```
|
||||||
,zen zen mode (Goyo + Limelight)
|
,mp preview in browser
|
||||||
,mp markdown preview in browser
|
|
||||||
,mt table of contents
|
,mt table of contents
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Markdown is quiet by default: no real-time lint, no spell noise, no concealed
|
||||||
|
syntax. Enable the heavier Markdown tools only when you want them.
|
||||||
|
|
||||||
|
## Health check
|
||||||
|
|
||||||
|
```
|
||||||
|
:ChopsticksStatus see what's installed and what's missing
|
||||||
|
```
|
||||||
|
|
||||||
|
The `,?` cheat sheet follows your active profile, so `minimal` users only see
|
||||||
|
keys for features that are actually loaded.
|
||||||
|
|
||||||
See [README](README.md) for the full reference. See the [wiki](https://github.com/m1ngsama/chopsticks/wiki) for deep dives.
|
See [README](README.md) for the full reference. See the [wiki](https://github.com/m1ngsama/chopsticks/wiki) for deep dives.
|
||||||
|
|
|
||||||
141
README.md
141
README.md
|
|
@ -5,7 +5,7 @@
|
||||||
<h1 align="center">chopsticks</h1>
|
<h1 align="center">chopsticks</h1>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<strong>Vim for engineers. 29 plugins, 19ms startup, works over SSH.</strong>
|
<strong>Vim for engineers. ~25 plugins, works over SSH.</strong>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
|
|
@ -30,47 +30,76 @@ You SSH into a server. You need to edit code. You want LSP, fuzzy find, git inte
|
||||||
|
|
||||||
chopsticks gives you a production-ready Vim config in one command. Pure VimScript — no Node.js for the core. Degrades gracefully on TTY. Works the same on your MacBook and your headless Arch box.
|
chopsticks gives you a production-ready Vim config in one command. Pure VimScript — no Node.js for the core. Degrades gracefully on TTY. Works the same on your MacBook and your headless Arch box.
|
||||||
|
|
||||||
**19ms startup** with 29 plugins, LSP, linting, and a hand-built statusline. Faster than most people's empty vimrc.
|
**24–25 plugins** (tmux-navigator loads only inside tmux), LSP, linting, and a hand-built statusline. No bloat, no decorations, just tools.
|
||||||
|
|
||||||
## What's in the box
|
## What's in the box
|
||||||
|
|
||||||
| | |
|
| Feature | Description |
|
||||||
|-|-|
|
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| **LSP** | completion, go-to-def, hover, rename, code actions — pure VimScript ([vim-lsp](https://github.com/prabirshrestha/vim-lsp)) |
|
| **LSP** | completion, go-to-def, hover, rename, code actions — pure VimScript ([vim-lsp](https://github.com/prabirshrestha/vim-lsp)) |
|
||||||
| **Lint + format** | [ALE](https://github.com/dense-analysis/ale) runs black, prettier, gofmt, rustfmt on save |
|
| **Lint + format** | [ALE](https://github.com/dense-analysis/ale) runs black, prettier, goimports, rustfmt on save |
|
||||||
| **Fuzzy find** | files, buffers, grep, tags, marks, commands — [FZF](https://github.com/junegunn/fzf.vim) |
|
| **Fuzzy find** | files, buffers, grep, tags, marks, commands — [FZF](https://github.com/junegunn/fzf.vim) |
|
||||||
| **Git** | status, diff, blame, push, pull, conflict markers — [fugitive](https://github.com/tpope/vim-fugitive) + [gitgutter](https://github.com/airblade/vim-gitgutter) |
|
| **Git** | status, diff, blame, push, pull, conflict markers — [fugitive](https://github.com/tpope/vim-fugitive) + [gitgutter](https://github.com/airblade/vim-gitgutter) |
|
||||||
| **Zen mode** | `,zen` — [Goyo](https://github.com/junegunn/goyo.vim) + [Limelight](https://github.com/junegunn/limelight.vim) |
|
|
||||||
| **Run file** | `,cr` — auto-detects Python, Go, Rust, JS, C, Shell, and more |
|
| **Run file** | `,cr` — auto-detects Python, Go, Rust, JS, C, Shell, and more |
|
||||||
|
| **Markdown** | quiet writing defaults, browser preview (`,mp`), table of contents (`,mt`) |
|
||||||
|
| **Diagnostics** | `:ChopsticksStatus` — see what's installed, what's missing, how to fix it |
|
||||||
| **TTY-aware** | degrades gracefully on SSH, console, slow links — never breaks |
|
| **TTY-aware** | degrades gracefully on SSH, console, slow links — never breaks |
|
||||||
| **19ms startup** | lazy-loaded plugins, deferred LSP init, zero redundant work |
|
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
```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
|
||||||
|
curl -fsSL https://raw.githubusercontent.com/m1ngsama/chopsticks/main/get.sh | bash -s -- --profile=minimal
|
||||||
|
curl -fsSL https://raw.githubusercontent.com/m1ngsama/chopsticks/main/get.sh | bash -s -- --dry-run --profile=full
|
||||||
```
|
```
|
||||||
|
|
||||||
Or manually:
|
Or manually:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/m1ngsama/chopsticks.git ~/.vim
|
git clone https://github.com/m1ngsama/chopsticks.git ~/.vim
|
||||||
cd ~/.vim && ./install.sh
|
cd ~/.vim && ./install.sh --profile=engineer
|
||||||
```
|
```
|
||||||
|
|
||||||
Supports macOS (brew), Debian/Ubuntu (apt), Arch (pacman), Fedora (dnf).
|
Supports macOS (brew), Debian/Ubuntu (apt), Arch (pacman), Fedora (dnf).
|
||||||
|
Set `CHOPSTICKS_DEST=/absolute/path` before running `get.sh` to install
|
||||||
|
somewhere other than `~/.vim`.
|
||||||
|
|
||||||
First launch installs plugins automatically (30-60s). Restart vim when done.
|
First launch installs plugins automatically (30-60s). Restart vim when done.
|
||||||
|
Use `./install.sh --dry-run --profile=full` to inspect the resolved profile and
|
||||||
|
config path without changing files. Use `./install.sh --configure-only
|
||||||
|
--profile=minimal` to switch profiles without reinstalling plugins or tools.
|
||||||
|
|
||||||
|
## Profiles
|
||||||
|
|
||||||
|
Default profile: `engineer`. Interactive installs ask for this profile before
|
||||||
|
plugins are installed; `--profile=minimal`, `--profile=engineer`, or
|
||||||
|
`--profile=full` selects it without prompting. `--yes` keeps the existing local
|
||||||
|
profile or uses `engineer`.
|
||||||
|
|
||||||
|
```vim
|
||||||
|
" Put this in ${XDG_CONFIG_HOME:-~/.config}/chopsticks.vim.
|
||||||
|
let g:chopsticks_profile = 'minimal' " core navigation/editing/git/markdown
|
||||||
|
let g:chopsticks_profile = 'engineer' " default: LSP, ALE, syntax extras
|
||||||
|
let g:chopsticks_profile = 'full' " engineer + heavier Markdown feedback
|
||||||
|
```
|
||||||
|
|
||||||
|
`minimal` avoids LSP, ALE, completion plugins, extra language syntax plugins,
|
||||||
|
Startify, UndoTree, and browser Markdown preview. `full` keeps those and opts
|
||||||
|
into Markdown lint, format, spell, conceal, Marksman, and LSP virtual text.
|
||||||
|
|
||||||
|
Project updates leave `~/.config/chopsticks.vim` alone, so put local choices
|
||||||
|
there instead of editing the managed `.vimrc`. The `,?` cheat sheet follows the
|
||||||
|
active profile and only shows keys for enabled features.
|
||||||
|
|
||||||
## Keys
|
## Keys
|
||||||
|
|
||||||
Leader: `,` — press `,?` for the full cheat sheet inside vim.
|
Leader: `,`
|
||||||
|
|
||||||
```
|
```
|
||||||
Ctrl+p fuzzy find file gd go to definition
|
Ctrl+p fuzzy find file gd go to definition
|
||||||
,rg ripgrep project K hover docs
|
,rg ripgrep project K hover docs
|
||||||
,gs git status ,cr run current file
|
,e toggle file sidebar ,cr run current file
|
||||||
,zen zen mode ,f format
|
,gs git status ,f format
|
||||||
,w save ,q quit
|
,w save ,q quit
|
||||||
jk exit insert mode ,? cheat sheet
|
jk exit insert mode ,? cheat sheet
|
||||||
```
|
```
|
||||||
|
|
@ -80,27 +109,35 @@ jk exit insert mode ,? cheat sheet
|
||||||
|
|
||||||
### Files
|
### Files
|
||||||
|
|
||||||
`Ctrl+p` find | `,b` buffers | `,rg` grep | `,rG` grep word | `,fh` recent | `,e` browser | `,,` last file
|
`Ctrl+p` find | `,b` buffers | `,rg` grep | `,rG` grep word | `,fh` recent | `,fl` lines | `,e` browser | `,E` browser (file dir) | `,,` last file
|
||||||
|
|
||||||
### Code
|
### Code
|
||||||
|
|
||||||
`gd` def | `gy` type | `gi` impl | `gr` refs | `K` docs | `[g` `]g` diagnostics | `,rn` rename | `,ca` action | `,o` outline | `,cr` run
|
`gd` def | `gy` type | `gi` impl | `gr` refs | `K` docs | `[g` `]g` diagnostics | `[e` `]e` ALE errors | `,rn` rename | `,ca` action | `,o` outline | `,cr` run
|
||||||
|
|
||||||
### Edit
|
### Edit
|
||||||
|
|
||||||
`s`+2ch jump | `gc` comment | `cs"'` surround | `Alt+j/k` move line | `,u` undo tree | `,y` clipboard | `,*` replace word
|
`,S`+2ch jump | `gc` comment | `cs"'` surround | `Alt+j/k` move line | `,u` undo tree | `,y` clipboard | `,*` replace word | `,F` re-indent | `,W` strip whitespace | `[<Space>` `]<Space>` blank lines
|
||||||
|
|
||||||
### Git
|
### Git
|
||||||
|
|
||||||
`,gs` status | `,gd` diff | `,gb` blame | `,gc` commit | `,gp` push | `]x` `[x` conflict
|
`,gs` status | `,gd` diff | `,gb` blame | `,gc` commit | `,gp` push | `,gl` pull | `,gL` log graph | `,gC` FZF commits | `,gB` buffer commits | `]x` `[x` conflict
|
||||||
|
|
||||||
### Windows
|
### Windows
|
||||||
|
|
||||||
`Ctrl+hjkl` navigate (+ tmux) | `,z` maximize | `,h` `,l` buffers | `,tv` terminal | `Esc Esc` exit terminal
|
`Ctrl+hjkl` navigate (+ tmux) | `,z` maximize | `,h` `,l` buffers | `,bd` close buffer | `,=` `,−` resize | `,tv` `,th` terminal | `Esc Esc` exit terminal
|
||||||
|
|
||||||
### Writing
|
### Markdown
|
||||||
|
|
||||||
`,zen` zen mode | `,mp` markdown preview | `,mt` table of contents
|
`,mp` preview in browser | `,mt` table of contents
|
||||||
|
|
||||||
|
### Toggle
|
||||||
|
|
||||||
|
`F2` paste | `F3` line numbers | `F4` relative numbers | `F6` invisible chars | `,ss` spell check | `,af` format on save
|
||||||
|
|
||||||
|
### Utilities
|
||||||
|
|
||||||
|
`,cp` copy full path | `,cf` copy filename | `,ev` edit vimrc | `,sv` reload vimrc | `,wa` save all | `:ChopsticksStatus` diagnostics
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
|
@ -109,65 +146,79 @@ jk exit insert mode ,? cheat sheet
|
||||||
```vim
|
```vim
|
||||||
:LspInstallServer " auto-detects filetype
|
:LspInstallServer " auto-detects filetype
|
||||||
:LspStatus " check what's running
|
:LspStatus " check what's running
|
||||||
|
:ChopsticksStatus " see all tools + LSP + linters at a glance
|
||||||
```
|
```
|
||||||
|
|
||||||
pylsp, gopls, rust-analyzer, clangd, marksman, sqls — no Node.js. JS/TS servers need Node.
|
pylsp, gopls, rust-analyzer, clangd, sqls — no Node.js. JS/TS servers need Node.
|
||||||
|
Markdown LSP (`marksman`) is opt-in so prose buffers stay quiet by default.
|
||||||
|
|
||||||
ALE and vim-lsp coexist cleanly (`ale_disable_lsp=1`). ALE handles linting + formatting. vim-lsp handles everything else.
|
ALE and vim-lsp coexist cleanly (`ale_disable_lsp=1`). ALE handles linting + formatting. vim-lsp handles everything else.
|
||||||
|
|
||||||
|
## Markdown
|
||||||
|
|
||||||
|
Markdown opens in writing mode: wrapped text, no spell noise, no concealed
|
||||||
|
syntax, no sign column, no real-time markdownlint, and no Marksman diagnostics.
|
||||||
|
The explicit commands still work:
|
||||||
|
|
||||||
|
```vim
|
||||||
|
,mp " preview in browser
|
||||||
|
,mt " table of contents
|
||||||
|
```
|
||||||
|
|
||||||
|
Opt into heavier Markdown tooling from your own vimrc before loading
|
||||||
|
chopsticks:
|
||||||
|
|
||||||
|
```vim
|
||||||
|
let g:chopsticks_markdown_lint = 1
|
||||||
|
let g:chopsticks_markdown_format_on_save = 1
|
||||||
|
let g:chopsticks_markdown_lsp = 1
|
||||||
|
let g:chopsticks_markdown_spell = 1
|
||||||
|
let g:chopsticks_markdown_conceal = 1
|
||||||
|
let g:previm_enable_realtime = 1
|
||||||
|
```
|
||||||
|
|
||||||
|
For Markdown LSP, install or select `marksman` first.
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
```
|
```
|
||||||
~/.vim/
|
~/.vim/
|
||||||
├── .vimrc thin loader (12 lines)
|
├── .vimrc thin loader
|
||||||
├── modules/
|
├── modules/
|
||||||
│ ├── env.vim TTY detection, truecolor
|
│ ├── env.vim TTY detection, truecolor, skip built-in plugins
|
||||||
│ ├── plugins.vim vim-plug + 29 plugins
|
│ ├── plugins.vim vim-plug + 24–25 plugins
|
||||||
│ ├── core.vim settings, keymaps, performance
|
│ ├── core.vim settings, keymaps, performance
|
||||||
│ ├── ui.vim solarized, statusline, startify
|
│ ├── ui.vim solarized, statusline, startify
|
||||||
│ ├── editing.vim easymotion, yank highlight
|
│ ├── editing.vim easymotion, yank highlight, blank lines
|
||||||
│ ├── navigation.vim fzf, netrw, windows, terminal
|
│ ├── navigation.vim fzf, netrw sidebar, windows, terminal
|
||||||
│ ├── lsp.vim vim-lsp, asyncomplete
|
│ ├── lsp.vim vim-lsp, asyncomplete
|
||||||
│ ├── lint.vim ale, format-on-save
|
│ ├── lint.vim ale, format-on-save
|
||||||
│ ├── git.vim fugitive, gitgutter
|
│ ├── git.vim fugitive, gitgutter, conflict nav
|
||||||
│ ├── writing.vim markdown, previm, zen mode
|
│ ├── languages.vim vim-go, markdown, filetype settings
|
||||||
│ ├── languages.vim vim-go, filetype settings
|
│ └── tools.vim run file, quickfix, cheat sheet, diagnostics
|
||||||
│ └── tools.vim cheat sheet, run file, helpers
|
|
||||||
└── tutor/
|
|
||||||
└── chopsticks.tutor
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Each module is self-contained. Comment out one line in `.vimrc` to disable it. Add your own with `call s:load('mine')`.
|
Each module is self-contained. Comment out one line in `.vimrc` to disable it. Add your own with `call s:load('mine')`.
|
||||||
|
|
||||||
## Learn
|
|
||||||
|
|
||||||
```vim
|
|
||||||
:ChopsticksLearn " interactive tutorial — 10 lessons
|
|
||||||
,? " cheat sheet (every binding)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Performance
|
## Performance
|
||||||
|
|
||||||
| Metric | Value |
|
| Metric | Value |
|
||||||
|--------|-------|
|
| ------------------------ | ------------------------------------------- |
|
||||||
| Startup time | **19ms** (29 plugins loaded) |
|
| Lazy-loaded | 7 plugins (on command or filetype) |
|
||||||
| Lazy-loaded | 8 plugins (on command or filetype) |
|
| Built-in plugins skipped | 12 (gzip, tar, zip, vimball, logiPat, etc.) |
|
||||||
| Built-in plugins skipped | 10 (gzip, tar, zip, vimball, etc.) |
|
|
||||||
| Runtime lint delay | 200ms (no thrashing during edits) |
|
|
||||||
| Large file threshold | 10MB (auto-disables syntax + undo) |
|
| Large file threshold | 10MB (auto-disables syntax + undo) |
|
||||||
| TTY large file | 500KB (syntax disabled) |
|
| TTY large file | 500KB (syntax disabled) |
|
||||||
|
|
||||||
Measured with `vim --startuptime`. We benchmark every change.
|
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
| Problem | Fix |
|
| Problem | Fix |
|
||||||
|---------|-----|
|
| ------------------- | --------------------------------------------- |
|
||||||
| Plugins not loading | `:PlugInstall` then `:PlugUpdate` |
|
| Plugins not loading | `:PlugInstall` then `:PlugUpdate` |
|
||||||
| LSP not starting | `:LspInstallServer` for current filetype |
|
| LSP not starting | `:LspInstallServer` for current filetype |
|
||||||
| Colors wrong | `export COLORTERM=truecolor` in shell rc |
|
| Colors wrong | `export COLORTERM=truecolor` in shell rc |
|
||||||
| `Ctrl+s` freezes | `stty -ixon` in shell rc |
|
| `Ctrl+s` freezes | `stty -ixon` in shell rc |
|
||||||
| Everything slow | Large file? Auto-disabled >10MB |
|
| Everything slow | Large file? Auto-disabled >10MB |
|
||||||
|
| What's installed? | `:ChopsticksStatus` shows tools, LSP, linters |
|
||||||
|
|
||||||
More in the [wiki](https://github.com/m1ngsama/chopsticks/wiki).
|
More in the [wiki](https://github.com/m1ngsama/chopsticks/wiki).
|
||||||
|
|
||||||
|
|
|
||||||
82
get.sh
Normal file → Executable file
82
get.sh
Normal file → Executable file
|
|
@ -4,11 +4,37 @@
|
||||||
# Usage:
|
# Usage:
|
||||||
# curl -fsSL https://raw.githubusercontent.com/m1ngsama/chopsticks/main/get.sh | 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 -s -- --yes
|
# curl -fsSL https://raw.githubusercontent.com/m1ngsama/chopsticks/main/get.sh | bash -s -- --yes
|
||||||
|
# CHOPSTICKS_DEST=/absolute/path bash get.sh --dry-run
|
||||||
|
|
||||||
set -eo pipefail
|
set -eo pipefail
|
||||||
|
|
||||||
REPO="https://github.com/m1ngsama/chopsticks.git"
|
REPO="https://github.com/m1ngsama/chopsticks.git"
|
||||||
DEST="$HOME/.vim"
|
DEST="${CHOPSTICKS_DEST:-$HOME/.vim}"
|
||||||
|
DRY_RUN=0
|
||||||
|
INSTALLER_ARGS=()
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'EOF'
|
||||||
|
Usage: get.sh [OPTIONS] [INSTALLER_OPTIONS]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--dry-run Show what would happen without cloning, pulling, or installing
|
||||||
|
--help, -h Show this help and exit
|
||||||
|
|
||||||
|
Environment:
|
||||||
|
CHOPSTICKS_DEST Absolute install path (default: ~/.vim)
|
||||||
|
|
||||||
|
All other options are passed to install.sh after clone/update.
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
for arg in "$@"; do
|
||||||
|
case "$arg" in
|
||||||
|
--dry-run) DRY_RUN=1 ;;
|
||||||
|
--help|-h) usage; exit 0 ;;
|
||||||
|
*) INSTALLER_ARGS+=("$arg") ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
if [[ -t 1 ]] && [[ -z "${NO_COLOR:-}" ]]; then
|
if [[ -t 1 ]] && [[ -z "${NO_COLOR:-}" ]]; then
|
||||||
GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m'; BOLD='\033[1m'; NC='\033[0m'
|
GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m'; BOLD='\033[1m'; NC='\033[0m'
|
||||||
|
|
@ -19,11 +45,36 @@ ok() { echo -e "${GREEN}[OK]${NC} $1"; }
|
||||||
warn() { echo -e "${YELLOW}[!]${NC} $1"; }
|
warn() { echo -e "${YELLOW}[!]${NC} $1"; }
|
||||||
die() { echo -e "${RED}[FATAL]${NC} $1" >&2; exit 1; }
|
die() { echo -e "${RED}[FATAL]${NC} $1" >&2; exit 1; }
|
||||||
step() { echo -e "\n${BOLD}==> $1${NC}"; }
|
step() { echo -e "\n${BOLD}==> $1${NC}"; }
|
||||||
|
info() { echo " $1"; }
|
||||||
|
|
||||||
|
case "$DEST" in
|
||||||
|
/*) ;;
|
||||||
|
*) die "CHOPSTICKS_DEST must be an absolute path: $DEST" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
repo_origin() {
|
||||||
|
git -C "$DEST" config --get remote.origin.url 2>/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
|
is_chopsticks_repo() {
|
||||||
|
local origin="$1"
|
||||||
|
origin="${origin%/}"
|
||||||
|
origin="${origin%.git}"
|
||||||
|
case "$origin" in
|
||||||
|
https://github.com/m1ngsama/chopsticks|\
|
||||||
|
git@github.com:m1ngsama/chopsticks|\
|
||||||
|
ssh://git@github.com/m1ngsama/chopsticks)
|
||||||
|
return 0 ;;
|
||||||
|
*)
|
||||||
|
return 1 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
echo -e "${BOLD}chopsticks — One-command installer${NC}"
|
echo -e "${BOLD}chopsticks — One-command installer${NC}"
|
||||||
echo "----------------------------------"
|
echo "----------------------------------"
|
||||||
echo " Repo: $REPO"
|
echo " Repo: $REPO"
|
||||||
echo " Dest: $DEST"
|
echo " Dest: $DEST"
|
||||||
|
[[ $DRY_RUN -eq 1 ]] && echo " Mode: dry-run"
|
||||||
|
|
||||||
# ── git ───────────────────────────────────────────────────────────────────────
|
# ── git ───────────────────────────────────────────────────────────────────────
|
||||||
step "Checking for git"
|
step "Checking for git"
|
||||||
|
|
@ -43,15 +94,36 @@ ok "git $(git --version | awk '{print $3}')"
|
||||||
step "Setting up $DEST"
|
step "Setting up $DEST"
|
||||||
|
|
||||||
if [[ -d "$DEST/.git" ]]; then
|
if [[ -d "$DEST/.git" ]]; then
|
||||||
|
ORIGIN="$(repo_origin)"
|
||||||
|
if ! is_chopsticks_repo "$ORIGIN"; then
|
||||||
|
die "$DEST is a git repo, but it does not look like chopsticks.
|
||||||
|
origin: ${ORIGIN:-none}
|
||||||
|
Back it up first: mv \"$DEST\" \"$DEST.bak\"
|
||||||
|
Then re-run: curl -fsSL https://raw.githubusercontent.com/m1ngsama/chopsticks/main/get.sh | bash"
|
||||||
|
fi
|
||||||
|
[[ -f "$DEST/install.sh" && -f "$DEST/.vimrc" ]] || \
|
||||||
|
die "$DEST looks incomplete. Expected install.sh and .vimrc.
|
||||||
|
Back it up first: mv \"$DEST\" \"$DEST.bak\"
|
||||||
|
Then re-run: curl -fsSL https://raw.githubusercontent.com/m1ngsama/chopsticks/main/get.sh | bash"
|
||||||
|
if [[ $DRY_RUN -eq 1 ]]; then
|
||||||
|
info "Would update existing chopsticks repo at $DEST"
|
||||||
|
info "Would run: bash install.sh ${INSTALLER_ARGS[*]:-(no installer options)}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
warn "$DEST already exists — pulling latest changes"
|
warn "$DEST already exists — pulling latest changes"
|
||||||
git -C "$DEST" pull --ff-only origin main 2>/dev/null || \
|
git -C "$DEST" pull --ff-only origin main 2>/dev/null || \
|
||||||
warn "Could not pull latest — using existing version (run: git -C ~/.vim pull)"
|
warn "Could not pull latest — using existing version (run: git -C ~/.vim pull)"
|
||||||
ok "Repository updated ($(git -C "$DEST" describe --tags 2>/dev/null || git -C "$DEST" rev-parse --short HEAD))"
|
ok "Repository updated ($(git -C "$DEST" describe --tags 2>/dev/null || git -C "$DEST" rev-parse --short HEAD))"
|
||||||
elif [[ -d "$DEST" ]]; then
|
elif [[ -d "$DEST" ]]; then
|
||||||
die "$HOME/.vim exists but is not a chopsticks git repo.
|
die "$DEST exists but is not a chopsticks git repo.
|
||||||
Back it up first: mv ~/.vim ~/.vim.bak
|
Back it up first: mv \"$DEST\" \"$DEST.bak\"
|
||||||
Then re-run: curl -fsSL https://raw.githubusercontent.com/m1ngsama/chopsticks/main/get.sh | bash"
|
Then re-run: curl -fsSL https://raw.githubusercontent.com/m1ngsama/chopsticks/main/get.sh | bash"
|
||||||
else
|
else
|
||||||
|
if [[ $DRY_RUN -eq 1 ]]; then
|
||||||
|
info "Would clone $REPO to $DEST"
|
||||||
|
info "Would run: bash install.sh ${INSTALLER_ARGS[*]:-(no installer options)}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
git clone --depth=1 "$REPO" "$DEST" || \
|
git clone --depth=1 "$REPO" "$DEST" || \
|
||||||
die "Clone failed — check your network connection"
|
die "Clone failed — check your network connection"
|
||||||
ok "Cloned to $DEST ($(git -C "$DEST" describe --tags 2>/dev/null || git -C "$DEST" rev-parse --short HEAD))"
|
ok "Cloned to $DEST ($(git -C "$DEST" describe --tags 2>/dev/null || git -C "$DEST" rev-parse --short HEAD))"
|
||||||
|
|
@ -67,7 +139,7 @@ cd "$DEST"
|
||||||
# Use a test-open to check /dev/tty is actually accessible (it may exist but be
|
# Use a test-open to check /dev/tty is actually accessible (it may exist but be
|
||||||
# unusable in non-interactive SSH sessions or container environments).
|
# unusable in non-interactive SSH sessions or container environments).
|
||||||
if { true </dev/tty; } 2>/dev/null; then
|
if { true </dev/tty; } 2>/dev/null; then
|
||||||
exec bash install.sh "$@" </dev/tty
|
exec bash install.sh "${INSTALLER_ARGS[@]}" </dev/tty
|
||||||
else
|
else
|
||||||
exec bash install.sh "$@"
|
exec bash install.sh "${INSTALLER_ARGS[@]}"
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
309
install.sh
309
install.sh
|
|
@ -1,28 +1,53 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# install.sh - chopsticks vim configuration installer
|
# install.sh - chopsticks vim configuration installer
|
||||||
# Usage: cd /path/to/chopsticks && ./install.sh [--yes] [--help]
|
# Usage: cd /path/to/chopsticks && ./install.sh [--yes] [--profile=engineer] [--help]
|
||||||
#
|
#
|
||||||
# --yes non-interactive: install all optional components automatically
|
# --yes non-interactive: use default profile/component selections
|
||||||
|
# --profile=PROFILE choose minimal, engineer, or full without prompting
|
||||||
|
# --configure-only update local chopsticks profile config and exit
|
||||||
|
# --dry-run show resolved profile/config path without writing files
|
||||||
# --help show this help and exit
|
# --help show this help and exit
|
||||||
|
|
||||||
set -eo pipefail
|
set -eo pipefail
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
AUTO_YES=0
|
AUTO_YES=0
|
||||||
|
REQUESTED_PROFILE=""
|
||||||
|
CONFIGURE_ONLY=0
|
||||||
|
DRY_RUN=0
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
case "$arg" in
|
case "$arg" in
|
||||||
--yes) AUTO_YES=1 ;;
|
--yes) AUTO_YES=1 ;;
|
||||||
|
--profile=*) REQUESTED_PROFILE="${arg#*=}" ;;
|
||||||
|
--profile)
|
||||||
|
echo "Use --profile=minimal, --profile=engineer, or --profile=full" >&2
|
||||||
|
exit 1 ;;
|
||||||
|
--configure-only) CONFIGURE_ONLY=1 ;;
|
||||||
|
--dry-run) DRY_RUN=1 ;;
|
||||||
--help|-h)
|
--help|-h)
|
||||||
echo "Usage: ./install.sh [OPTIONS]"
|
echo "Usage: ./install.sh [OPTIONS]"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Options:"
|
echo "Options:"
|
||||||
echo " --yes Non-interactive mode: select all defaults automatically"
|
echo " --yes Non-interactive mode: use default profile/component selections"
|
||||||
|
echo " --profile=PROFILE"
|
||||||
|
echo " Select minimal, engineer, or full without prompting"
|
||||||
|
echo " --configure-only"
|
||||||
|
echo " Update local profile config and exit; no plugins or tools installed"
|
||||||
|
echo " --dry-run"
|
||||||
|
echo " Show resolved profile/config path without writing files"
|
||||||
echo " --help Show this help and exit"
|
echo " --help Show this help and exit"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Supported platforms: macOS (brew), Debian/Ubuntu (apt), Arch (pacman), Fedora (dnf)"
|
echo "Supported platforms: macOS (brew), Debian/Ubuntu (apt), Arch (pacman), Fedora (dnf)"
|
||||||
exit 0 ;;
|
exit 0 ;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
case "$REQUESTED_PROFILE" in
|
||||||
|
""|minimal|engineer|full) ;;
|
||||||
|
*)
|
||||||
|
echo "Invalid profile: $REQUESTED_PROFILE" >&2
|
||||||
|
echo "Use: minimal, engineer, or full" >&2
|
||||||
|
exit 1 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
# ── Colours (respect NO_COLOR and non-TTY) ───────────────────────────────────
|
# ── Colours (respect NO_COLOR and non-TTY) ───────────────────────────────────
|
||||||
if [[ -t 1 ]] && [[ -z "${NO_COLOR:-}" ]]; then
|
if [[ -t 1 ]] && [[ -z "${NO_COLOR:-}" ]]; then
|
||||||
|
|
@ -51,6 +76,13 @@ info() { echo " $1"; }
|
||||||
INSTALLED=()
|
INSTALLED=()
|
||||||
SKIPPED=()
|
SKIPPED=()
|
||||||
FAILED=()
|
FAILED=()
|
||||||
|
if [[ -n "${XDG_CONFIG_HOME:-}" && "$XDG_CONFIG_HOME" == /* ]]; then
|
||||||
|
CONFIG_HOME="$XDG_CONFIG_HOME"
|
||||||
|
else
|
||||||
|
CONFIG_HOME="$HOME/.config"
|
||||||
|
fi
|
||||||
|
LOCAL_CONFIG="$CONFIG_HOME/chopsticks.vim"
|
||||||
|
CONFIG_PROFILE="${REQUESTED_PROFILE:-engineer}"
|
||||||
|
|
||||||
# Ask yes/no; reads from /dev/tty so it works under: curl | bash
|
# Ask yes/no; reads from /dev/tty so it works under: curl | bash
|
||||||
ask() {
|
ask() {
|
||||||
|
|
@ -66,16 +98,160 @@ ask() {
|
||||||
[[ "$reply" =~ ^[Yy]$ ]]
|
[[ "$reply" =~ ^[Yy]$ ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Error trap ────────────────────────────────────────────────────────────────
|
profile_from_config() {
|
||||||
|
[[ -f "$LOCAL_CONFIG" ]] || return 0
|
||||||
|
sed -nE "s/^[[:space:]]*let[[:space:]]+g:chopsticks_profile[[:space:]]*=[[:space:]]*['\"](minimal|engineer|full)['\"].*/\1/p" \
|
||||||
|
"$LOCAL_CONFIG" | tail -n1
|
||||||
|
}
|
||||||
|
|
||||||
|
choose_profile() {
|
||||||
|
local default="${1:-engineer}" reply
|
||||||
|
while true; do
|
||||||
|
echo "Choose Vim profile:"
|
||||||
|
echo " 1) minimal core navigation/editing/git/markdown; no LSP/ALE/completion extras"
|
||||||
|
echo " 2) engineer default; LSP, ALE, completion, syntax extras"
|
||||||
|
echo " 3) full engineer plus heavier Markdown lint/spell/conceal/LSP feedback"
|
||||||
|
if [[ -t 0 ]]; then
|
||||||
|
read -r -p "Profile [$default]: " reply
|
||||||
|
elif { true </dev/tty; } 2>/dev/null; then
|
||||||
|
read -r -p "Profile [$default]: " reply </dev/tty
|
||||||
|
else
|
||||||
|
CONFIG_PROFILE="$default"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
reply="${reply:-$default}"
|
||||||
|
case "$reply" in
|
||||||
|
1|minimal) CONFIG_PROFILE="minimal"; return ;;
|
||||||
|
2|engineer) CONFIG_PROFILE="engineer"; return ;;
|
||||||
|
3|full) CONFIG_PROFILE="full"; return ;;
|
||||||
|
*) warn "Choose 1, 2, 3, minimal, engineer, or full." ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
write_profile_config() {
|
||||||
|
local profile="$1" config_dir tmp
|
||||||
|
config_dir="$(dirname "$LOCAL_CONFIG")"
|
||||||
|
mkdir -p "$config_dir"
|
||||||
|
|
||||||
|
if [[ -f "$LOCAL_CONFIG" ]] && \
|
||||||
|
grep -Eq '^[[:space:]]*let[[:space:]]+g:chopsticks_profile[[:space:]]*=' "$LOCAL_CONFIG"; then
|
||||||
|
tmp="$_TMPDIR/chopsticks-local-config"
|
||||||
|
awk -v profile="$profile" '
|
||||||
|
/^[[:space:]]*let[[:space:]]+g:chopsticks_profile[[:space:]]*=/ && !done {
|
||||||
|
printf "let g:chopsticks_profile = \047%s\047\n", profile
|
||||||
|
done = 1
|
||||||
|
next
|
||||||
|
}
|
||||||
|
{ print }
|
||||||
|
' "$LOCAL_CONFIG" > "$tmp"
|
||||||
|
mv "$tmp" "$LOCAL_CONFIG"
|
||||||
|
else
|
||||||
|
[[ -s "$LOCAL_CONFIG" ]] && printf '\n' >> "$LOCAL_CONFIG"
|
||||||
|
{
|
||||||
|
echo '" chopsticks local preferences'
|
||||||
|
echo "let g:chopsticks_profile = '$profile'"
|
||||||
|
} >> "$LOCAL_CONFIG"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
configure_profile() {
|
||||||
|
local existing
|
||||||
|
existing="$(profile_from_config)"
|
||||||
|
|
||||||
|
step "Configuring Vim profile"
|
||||||
|
if [[ $DRY_RUN -eq 1 ]]; then
|
||||||
|
if [[ -n "$REQUESTED_PROFILE" ]]; then
|
||||||
|
CONFIG_PROFILE="$REQUESTED_PROFILE"
|
||||||
|
elif [[ -n "$existing" ]]; then
|
||||||
|
CONFIG_PROFILE="$existing"
|
||||||
|
else
|
||||||
|
CONFIG_PROFILE="engineer"
|
||||||
|
fi
|
||||||
|
info "Dry run: no files will be changed"
|
||||||
|
info "Profile: $CONFIG_PROFILE"
|
||||||
|
info "Local config: $LOCAL_CONFIG"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$REQUESTED_PROFILE" ]]; then
|
||||||
|
CONFIG_PROFILE="$REQUESTED_PROFILE"
|
||||||
|
write_profile_config "$CONFIG_PROFILE"
|
||||||
|
ok "Profile saved: $CONFIG_PROFILE ($LOCAL_CONFIG)"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$existing" ]]; then
|
||||||
|
CONFIG_PROFILE="$existing"
|
||||||
|
ok "Profile: $CONFIG_PROFILE ($LOCAL_CONFIG)"
|
||||||
|
if [[ $AUTO_YES -eq 1 ]] || ! { true </dev/tty; } 2>/dev/null; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
if ask "Change profile now?"; then
|
||||||
|
choose_profile "$existing"
|
||||||
|
if [[ "$CONFIG_PROFILE" != "$existing" ]]; then
|
||||||
|
write_profile_config "$CONFIG_PROFILE"
|
||||||
|
ok "Profile saved: $CONFIG_PROFILE ($LOCAL_CONFIG)"
|
||||||
|
else
|
||||||
|
skip "profile unchanged"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [[ $AUTO_YES -eq 1 ]] || ! { true </dev/tty; } 2>/dev/null; then
|
||||||
|
CONFIG_PROFILE="engineer"
|
||||||
|
if [[ $CONFIGURE_ONLY -eq 1 ]]; then
|
||||||
|
write_profile_config "$CONFIG_PROFILE"
|
||||||
|
ok "Profile saved: $CONFIG_PROFILE ($LOCAL_CONFIG)"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
info "Profile: engineer (default; create $LOCAL_CONFIG to change later)"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
choose_profile engineer
|
||||||
|
if [[ "$CONFIG_PROFILE" == "engineer" && $CONFIGURE_ONLY -eq 0 ]]; then
|
||||||
|
info "Profile: engineer (default; no local override written)"
|
||||||
|
else
|
||||||
|
write_profile_config "$CONFIG_PROFILE"
|
||||||
|
ok "Profile saved: $CONFIG_PROFILE ($LOCAL_CONFIG)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Error trap / cleanup ──────────────────────────────────────────────────────
|
||||||
on_error() {
|
on_error() {
|
||||||
echo -e "\n${RED}[FATAL]${NC} Command '${BASH_COMMAND}' failed at line ${BASH_LINENO[0]}." >&2
|
echo -e "\n${RED}[FATAL]${NC} Command '${BASH_COMMAND}' failed at line ${BASH_LINENO[0]}." >&2
|
||||||
echo " To get a full debug log:" >&2
|
echo " To get a full debug log:" >&2
|
||||||
echo " ./install.sh 2>&1 | tee /tmp/chopsticks-install.log" >&2
|
echo " ./install.sh 2>&1 | tee /tmp/chopsticks-install.log" >&2
|
||||||
echo " Report issues: https://github.com/m1ngsama/chopsticks/issues" >&2
|
echo " Report issues: https://github.com/m1ngsama/chopsticks/issues" >&2
|
||||||
}
|
}
|
||||||
|
cleanup() {
|
||||||
|
if [[ ${_MENU_CURSOR_HIDDEN:-0} -eq 1 ]]; then
|
||||||
|
tput cnorm 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
[[ -n "${_TMPDIR:-}" ]] && rm -rf "$_TMPDIR" 2>/dev/null
|
||||||
|
}
|
||||||
|
on_interrupt() {
|
||||||
|
echo "" >&2
|
||||||
|
die "Interrupted."
|
||||||
|
}
|
||||||
trap on_error ERR
|
trap on_error ERR
|
||||||
_TMPDIR=$(mktemp -d "${TMPDIR:-/tmp}/chopsticks-XXXXXX")
|
_TMPDIR=$(mktemp -d "${TMPDIR:-/tmp}/chopsticks-XXXXXX")
|
||||||
trap 'rm -rf "$_TMPDIR" 2>/dev/null' EXIT
|
trap cleanup EXIT
|
||||||
|
trap on_interrupt INT TERM
|
||||||
|
|
||||||
|
if [[ $DRY_RUN -eq 1 || $CONFIGURE_ONLY -eq 1 ]]; then
|
||||||
|
echo -e "${BOLD}chopsticks — Vim Configuration Installer${NC}"
|
||||||
|
echo "----------------------------------------"
|
||||||
|
configure_profile
|
||||||
|
if [[ $DRY_RUN -eq 1 ]]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}Configuration complete.${NC}"
|
||||||
|
echo " Profile: $CONFIG_PROFILE"
|
||||||
|
echo " Local config: $LOCAL_CONFIG"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
# ── Safe download helper ──────────────────────────────────────────────────────
|
# ── Safe download helper ──────────────────────────────────────────────────────
|
||||||
safe_download() {
|
safe_download() {
|
||||||
|
|
@ -183,7 +359,9 @@ _menu_checkbox() {
|
||||||
local _lines=$(( 3 + 2 * _MENU_N ))
|
local _lines=$(( 3 + 2 * _MENU_N ))
|
||||||
local _key _esc _i
|
local _key _esc _i
|
||||||
|
|
||||||
tput civis 2>/dev/null # hide cursor
|
if tput civis 2>/dev/null; then
|
||||||
|
_MENU_CURSOR_HIDDEN=1
|
||||||
|
fi
|
||||||
local _first=1
|
local _first=1
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
|
|
@ -211,7 +389,10 @@ _menu_checkbox() {
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
tput cnorm 2>/dev/null # restore cursor
|
if [[ ${_MENU_CURSOR_HIDDEN:-0} -eq 1 ]]; then
|
||||||
|
tput cnorm 2>/dev/null || true
|
||||||
|
_MENU_CURSOR_HIDDEN=0
|
||||||
|
fi
|
||||||
echo ""
|
echo ""
|
||||||
MENU_SEL=("${_MENU_SELS[@]}")
|
MENU_SEL=("${_MENU_SELS[@]}")
|
||||||
}
|
}
|
||||||
|
|
@ -381,6 +562,8 @@ fi
|
||||||
|
|
||||||
mkdir -p "$HOME/.vim"
|
mkdir -p "$HOME/.vim"
|
||||||
|
|
||||||
|
configure_profile
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# 3. vim-plug + Plugins
|
# 3. vim-plug + Plugins
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
@ -423,13 +606,52 @@ _vim_run() {
|
||||||
vim --not-a-term "$@" 2>/dev/null
|
vim --not-a-term "$@" 2>/dev/null
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
if [[ -d "$HOME/.vim/plugged" ]] && [[ -n "$(ls -A "$HOME/.vim/plugged" 2>/dev/null)" ]]; then
|
|
||||||
|
verify_plugins() {
|
||||||
|
local required_file="$_TMPDIR/required-plugins.txt"
|
||||||
|
local verify_script="$_TMPDIR/required-plugins.vim"
|
||||||
|
local missing=() dir
|
||||||
|
|
||||||
|
cat > "$verify_script" <<'VIMEOF'
|
||||||
|
if !exists('g:plugs')
|
||||||
|
cquit
|
||||||
|
endif
|
||||||
|
let s:dirs = []
|
||||||
|
for s:plug in values(g:plugs)
|
||||||
|
let s:dir = get(s:plug, 'dir', '')
|
||||||
|
if !empty(s:dir)
|
||||||
|
call add(s:dirs, fnamemodify(s:dir, ':p'))
|
||||||
|
endif
|
||||||
|
endfor
|
||||||
|
call writefile(s:dirs, $CHOPSTICKS_REQUIRED_PLUGINS)
|
||||||
|
qa!
|
||||||
|
VIMEOF
|
||||||
|
|
||||||
|
CHOPSTICKS_REQUIRED_PLUGINS="$required_file" \
|
||||||
|
vim -u "$SCRIPT_DIR/.vimrc" -i NONE -es -N -S "$verify_script" >/dev/null 2>&1 || return 1
|
||||||
|
|
||||||
|
[[ -s "$required_file" ]] || return 1
|
||||||
|
while IFS= read -r dir; do
|
||||||
|
[[ -z "$dir" ]] && continue
|
||||||
|
[[ -d "$dir" ]] || missing+=("$dir")
|
||||||
|
done < "$required_file"
|
||||||
|
|
||||||
|
if [[ ${#missing[@]} -gt 0 ]]; then
|
||||||
|
fail "Plugin installation incomplete — missing:"
|
||||||
|
for dir in "${missing[@]}"; do echo " ! $dir"; done
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ -d "$HOME/.vim/plugged" ]] && [[ -n "$(find "$HOME/.vim/plugged" -mindepth 1 -maxdepth 1 2>/dev/null)" ]]; then
|
||||||
warn "PlugClean: removing plugins not listed in .vimrc from ~/.vim/plugged"
|
warn "PlugClean: removing plugins not listed in .vimrc from ~/.vim/plugged"
|
||||||
fi
|
fi
|
||||||
_vim_run +'PlugClean!' +qall || true # remove plugins no longer in vimrc; ignore exit code (none expected)
|
_vim_run +'PlugClean!' +qall || true # remove plugins no longer in vimrc; ignore exit code (none expected)
|
||||||
_vim_run +'PlugInstall --sync' +qall || true # fzf post-install hook may exit non-zero; harmless
|
_vim_run +'PlugInstall --sync' +qall || true # fzf post-install hook may exit non-zero; harmless
|
||||||
|
|
||||||
_plug_count=$(ls -1 "$HOME/.vim/plugged" 2>/dev/null | wc -l | tr -d ' ')
|
verify_plugins || die "Plugin installation failed — retry with a stable network connection."
|
||||||
|
|
||||||
|
_plug_count=$(find "$HOME/.vim/plugged" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l | tr -d ' ')
|
||||||
if [[ $_plug_count -eq 0 ]]; then
|
if [[ $_plug_count -eq 0 ]]; then
|
||||||
die "Plugin installation failed — ~/.vim/plugged is empty. Check network and retry."
|
die "Plugin installation failed — ~/.vim/plugged is empty. Check network and retry."
|
||||||
fi
|
fi
|
||||||
|
|
@ -440,9 +662,14 @@ ok "Plugins installed ($_plug_count)"
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
step "Select optional components"
|
step "Select optional components"
|
||||||
|
info "Vim profile: $CONFIG_PROFILE"
|
||||||
|
|
||||||
_ITEMS=()
|
_ITEMS=()
|
||||||
_idx=0
|
_idx=0
|
||||||
|
_PROFILE_TOOLING=1
|
||||||
|
[[ $CONFIG_PROFILE == "minimal" ]] && _PROFILE_TOOLING=0
|
||||||
|
_MARKSMAN_DEFAULT=0
|
||||||
|
[[ $CONFIG_PROFILE == "full" ]] && _MARKSMAN_DEFAULT=1
|
||||||
|
|
||||||
# Index map (-1 = not in menu / unavailable)
|
# Index map (-1 = not in menu / unavailable)
|
||||||
_I_RIPGREP=-1; _I_FZF=-1; _I_CTAGS=-1; _I_SHELLCHECK=-1
|
_I_RIPGREP=-1; _I_FZF=-1; _I_CTAGS=-1; _I_SHELLCHECK=-1
|
||||||
|
|
@ -467,42 +694,46 @@ if [[ $HAS_PKG_MGR -eq 1 ]]; then
|
||||||
: $(( _idx++ ))
|
: $(( _idx++ ))
|
||||||
|
|
||||||
_I_CTAGS=$_idx
|
_I_CTAGS=$_idx
|
||||||
_ITEMS+=("universal-ctags|code symbol index (backing engine for ,rt tag jumps)|1")
|
_ITEMS+=("universal-ctags|Optional symbol index for ,rt tag jumps|0")
|
||||||
: $(( _idx++ ))
|
: $(( _idx++ ))
|
||||||
|
|
||||||
|
if [[ $_PROFILE_TOOLING -eq 1 ]]; then
|
||||||
_I_SHELLCHECK=$_idx
|
_I_SHELLCHECK=$_idx
|
||||||
_ITEMS+=("shellcheck|shell script static analysis (ALE integration, on-save)|1")
|
_ITEMS+=("shellcheck|Optional shell script static analysis via ALE|0")
|
||||||
: $(( _idx++ ))
|
: $(( _idx++ ))
|
||||||
|
|
||||||
_I_HADOLINT=$_idx
|
_I_HADOLINT=$_idx
|
||||||
_ITEMS+=("hadolint|Dockerfile linting (ALE integration, on-save)|1")
|
_ITEMS+=("hadolint|Optional Dockerfile linting via ALE|0")
|
||||||
: $(( _idx++ ))
|
: $(( _idx++ ))
|
||||||
|
|
||||||
_I_MARKSMAN=$_idx
|
_I_MARKSMAN=$_idx
|
||||||
_ITEMS+=("marksman|Markdown LSP — completion · go-to-definition · live diagnostics|1")
|
_ITEMS+=("marksman|Markdown LSP for full profile or explicit Markdown LSP|$_MARKSMAN_DEFAULT")
|
||||||
: $(( _idx++ ))
|
: $(( _idx++ ))
|
||||||
|
else
|
||||||
|
skip "lint/LSP system tools hidden by minimal profile"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
warn "No package manager available — system tools skipped"
|
warn "No package manager available — system tools skipped"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── npm tools ────────────────────────────────────────────────────────────────
|
# ── npm tools ────────────────────────────────────────────────────────────────
|
||||||
if [[ $HAS_NODE -eq 1 ]]; then
|
if [[ $HAS_NODE -eq 1 && $_PROFILE_TOOLING -eq 1 ]]; then
|
||||||
_I_NPM=$_idx
|
_I_NPM=$_idx
|
||||||
_ITEMS+=("npm formatter suite|prettier / eslint / markdownlint / stylelint / tsc — ALE fix-on-save|1")
|
_ITEMS+=("npm formatter suite|Optional prettier / eslint / markdownlint / stylelint / tsc|0")
|
||||||
: $(( _idx++ ))
|
: $(( _idx++ ))
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── Python tools ─────────────────────────────────────────────────────────────
|
# ── Python tools ─────────────────────────────────────────────────────────────
|
||||||
if [[ $HAS_PIP -eq 1 ]]; then
|
if [[ $HAS_PIP -eq 1 && $_PROFILE_TOOLING -eq 1 ]]; then
|
||||||
_I_PYTHON=$_idx
|
_I_PYTHON=$_idx
|
||||||
_ITEMS+=("Python tool suite|black / isort / flake8 / pylint / yamllint / sqlfluff — ALE fix-on-save|1")
|
_ITEMS+=("Python tool suite|Optional black / isort / flake8 / pylint / yamllint / sqlfluff|0")
|
||||||
: $(( _idx++ ))
|
: $(( _idx++ ))
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── Go tools ─────────────────────────────────────────────────────────────────
|
# ── Go tools ─────────────────────────────────────────────────────────────────
|
||||||
if [[ $HAS_GO -eq 1 ]]; then
|
if [[ $HAS_GO -eq 1 && $_PROFILE_TOOLING -eq 1 ]]; then
|
||||||
_I_GO=$_idx
|
_I_GO=$_idx
|
||||||
_ITEMS+=("Go tool suite|gopls (LSP) / goimports / staticcheck — completion · format · analysis|1")
|
_ITEMS+=("Go tool suite|Optional gopls / goimports / staticcheck|0")
|
||||||
: $(( _idx++ ))
|
: $(( _idx++ ))
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
@ -510,7 +741,7 @@ fi
|
||||||
if command -v tmux >/dev/null 2>&1; then
|
if command -v tmux >/dev/null 2>&1; then
|
||||||
if ! grep -q 'vim-tmux-navigator' "$HOME/.tmux.conf" 2>/dev/null; then
|
if ! grep -q 'vim-tmux-navigator' "$HOME/.tmux.conf" 2>/dev/null; then
|
||||||
_I_TMUX=$_idx
|
_I_TMUX=$_idx
|
||||||
_ITEMS+=("tmux integration|seamless Ctrl+h/j/k/l navigation between vim and tmux panes|1")
|
_ITEMS+=("tmux integration|Optional Ctrl+h/j/k/l navigation between vim and tmux panes|0")
|
||||||
: $(( _idx++ ))
|
: $(( _idx++ ))
|
||||||
else
|
else
|
||||||
ok "tmux integration (vim-tmux-navigator already configured)"
|
ok "tmux integration (vim-tmux-navigator already configured)"
|
||||||
|
|
@ -680,6 +911,9 @@ step "npm tools (formatters + linters)"
|
||||||
if [[ $HAS_NODE -eq 0 ]]; then
|
if [[ $HAS_NODE -eq 0 ]]; then
|
||||||
skip "npm tools (Node.js not installed)"
|
skip "npm tools (Node.js not installed)"
|
||||||
SKIPPED+=("prettier" "markdownlint-cli" "stylelint" "eslint" "typescript")
|
SKIPPED+=("prettier" "markdownlint-cli" "stylelint" "eslint" "typescript")
|
||||||
|
elif [[ $_PROFILE_TOOLING -eq 0 ]]; then
|
||||||
|
skip "npm tools (minimal profile)"
|
||||||
|
SKIPPED+=("prettier" "markdownlint-cli" "stylelint" "eslint" "typescript")
|
||||||
elif [[ $_I_NPM -lt 0 ]] || ! _selected "$_I_NPM"; then
|
elif [[ $_I_NPM -lt 0 ]] || ! _selected "$_I_NPM"; then
|
||||||
skip "npm formatter suite (skipped by user)"
|
skip "npm formatter suite (skipped by user)"
|
||||||
SKIPPED+=("prettier" "markdownlint-cli" "stylelint" "eslint" "typescript")
|
SKIPPED+=("prettier" "markdownlint-cli" "stylelint" "eslint" "typescript")
|
||||||
|
|
@ -712,6 +946,9 @@ step "Python tools (formatters + linters)"
|
||||||
if [[ $HAS_PIP -eq 0 ]]; then
|
if [[ $HAS_PIP -eq 0 ]]; then
|
||||||
skip "Python tools (pip3 not installed)"
|
skip "Python tools (pip3 not installed)"
|
||||||
SKIPPED+=("black" "isort" "flake8" "pylint" "yamllint" "sqlfluff")
|
SKIPPED+=("black" "isort" "flake8" "pylint" "yamllint" "sqlfluff")
|
||||||
|
elif [[ $_PROFILE_TOOLING -eq 0 ]]; then
|
||||||
|
skip "Python tools (minimal profile)"
|
||||||
|
SKIPPED+=("black" "isort" "flake8" "pylint" "yamllint" "sqlfluff")
|
||||||
elif [[ $_I_PYTHON -lt 0 ]] || ! _selected "$_I_PYTHON"; then
|
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")
|
||||||
|
|
@ -747,6 +984,9 @@ step "Go tools"
|
||||||
if [[ $HAS_GO -eq 0 ]]; then
|
if [[ $HAS_GO -eq 0 ]]; then
|
||||||
skip "Go tools (go not installed — see https://go.dev/dl/)"
|
skip "Go tools (go not installed — see https://go.dev/dl/)"
|
||||||
SKIPPED+=("gopls" "goimports" "staticcheck")
|
SKIPPED+=("gopls" "goimports" "staticcheck")
|
||||||
|
elif [[ $_PROFILE_TOOLING -eq 0 ]]; then
|
||||||
|
skip "Go tools (minimal profile)"
|
||||||
|
SKIPPED+=("gopls" "goimports" "staticcheck")
|
||||||
elif [[ $_I_GO -lt 0 ]] || ! _selected "$_I_GO"; then
|
elif [[ $_I_GO -lt 0 ]] || ! _selected "$_I_GO"; then
|
||||||
skip "Go tool suite (skipped by user)"
|
skip "Go tool suite (skipped by user)"
|
||||||
SKIPPED+=("gopls" "goimports" "staticcheck")
|
SKIPPED+=("gopls" "goimports" "staticcheck")
|
||||||
|
|
@ -789,14 +1029,26 @@ elif ! _selected "$_I_TMUX"; then
|
||||||
SKIPPED+=("tmux-navigator-config")
|
SKIPPED+=("tmux-navigator-config")
|
||||||
else
|
else
|
||||||
TMUX_CONF="$HOME/.tmux.conf"
|
TMUX_CONF="$HOME/.tmux.conf"
|
||||||
cat >> "$TMUX_CONF" << 'TMUXEOF'
|
TMUX_BEGIN="# >>> chopsticks vim-tmux-navigator >>>"
|
||||||
|
TMUX_END="# <<< chopsticks vim-tmux-navigator <<<"
|
||||||
|
if [[ -f "$TMUX_CONF" ]] && grep -Fq "$TMUX_BEGIN" "$TMUX_CONF"; then
|
||||||
|
tmp="$_TMPDIR/tmux.conf"
|
||||||
|
awk -v begin="$TMUX_BEGIN" -v end="$TMUX_END" '
|
||||||
|
$0 == begin { skip = 1; next }
|
||||||
|
$0 == end { skip = 0; next }
|
||||||
|
!skip { print }
|
||||||
|
' "$TMUX_CONF" > "$tmp"
|
||||||
|
mv "$tmp" "$TMUX_CONF"
|
||||||
|
fi
|
||||||
|
cat >> "$TMUX_CONF" << TMUXEOF
|
||||||
|
|
||||||
# vim-tmux-navigator: seamless Ctrl+h/j/k/l navigation between vim and tmux
|
$TMUX_BEGIN
|
||||||
is_vim="ps -o state= -o comm= -t '#{pane_tty}' | grep -iqE '^[^TXZ ]+ +(\S+\/)?g?(view|n?vim?x?)(diff)?$'"
|
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-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-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-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'
|
bind-key -n 'C-l' if-shell "\$is_vim" 'send-keys C-l' 'select-pane -R'
|
||||||
|
$TMUX_END
|
||||||
TMUXEOF
|
TMUXEOF
|
||||||
ok "vim-tmux-navigator bindings appended to ~/.tmux.conf"
|
ok "vim-tmux-navigator bindings appended to ~/.tmux.conf"
|
||||||
warn "Reload tmux config now: tmux source-file ~/.tmux.conf"
|
warn "Reload tmux config now: tmux source-file ~/.tmux.conf"
|
||||||
|
|
@ -828,6 +1080,11 @@ echo -e "${BOLD}=======================================${NC}"
|
||||||
echo -e "${GREEN}Installation complete.${NC}"
|
echo -e "${GREEN}Installation complete.${NC}"
|
||||||
echo -e "${BOLD}=======================================${NC}"
|
echo -e "${BOLD}=======================================${NC}"
|
||||||
|
|
||||||
|
echo -e "\n${BOLD}Profile:${NC} $CONFIG_PROFILE"
|
||||||
|
if [[ -f "$LOCAL_CONFIG" ]]; then
|
||||||
|
echo " Local config: $LOCAL_CONFIG"
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ ${#INSTALLED[@]} -gt 0 ]]; then
|
if [[ ${#INSTALLED[@]} -gt 0 ]]; then
|
||||||
echo -e "\n${GREEN}Installed:${NC}"
|
echo -e "\n${GREEN}Installed:${NC}"
|
||||||
for t in "${INSTALLED[@]}"; do echo " + $t"; done
|
for t in "${INSTALLED[@]}"; do echo " + $t"; done
|
||||||
|
|
|
||||||
|
|
@ -57,8 +57,14 @@ endif
|
||||||
|
|
||||||
set display+=lastline
|
set display+=lastline
|
||||||
set ffs=unix,dos,mac
|
set ffs=unix,dos,mac
|
||||||
set nowb
|
set writebackup
|
||||||
set noswapfile
|
|
||||||
|
if has('unix')
|
||||||
|
let s:swap_dir = expand(get(g:, 'chopsticks_swap_dir', '~/.vim/.swap'))
|
||||||
|
let &directory = s:swap_dir . '//,/tmp//'
|
||||||
|
silent! call mkdir(s:swap_dir, 'p', 0700)
|
||||||
|
endif
|
||||||
|
set swapfile
|
||||||
|
|
||||||
if has('persistent_undo')
|
if has('persistent_undo')
|
||||||
set undofile
|
set undofile
|
||||||
|
|
@ -89,7 +95,7 @@ let mapleader = ","
|
||||||
|
|
||||||
" ── Basic Keymaps ───────────────────────────────────────────────────────────
|
" ── Basic Keymaps ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
nnoremap <leader>w :w!<cr>
|
nnoremap <leader>w :w<cr>
|
||||||
nnoremap <leader>q :q<cr>
|
nnoremap <leader>q :q<cr>
|
||||||
nnoremap <leader>x :x<cr>
|
nnoremap <leader>x :x<cr>
|
||||||
|
|
||||||
|
|
@ -100,23 +106,10 @@ nnoremap <leader>ba :bufdo bd<cr>
|
||||||
nnoremap <leader>l :bnext<cr>
|
nnoremap <leader>l :bnext<cr>
|
||||||
nnoremap <leader>h :bprevious<cr>
|
nnoremap <leader>h :bprevious<cr>
|
||||||
|
|
||||||
nnoremap <leader>tn :tabnew<cr>
|
|
||||||
nnoremap <leader>to :tabonly<cr>
|
|
||||||
nnoremap <leader>tc :tabclose<cr>
|
|
||||||
nnoremap <leader>tm :tabmove
|
|
||||||
nnoremap <leader>t<leader> :tabnext<cr>
|
|
||||||
|
|
||||||
let g:lasttab = 1
|
|
||||||
nnoremap <Leader>tl :exe "tabn ".g:lasttab<CR>
|
|
||||||
augroup ChopstickTabHistory
|
|
||||||
autocmd!
|
|
||||||
autocmd TabLeave * let g:lasttab = tabpagenr()
|
|
||||||
augroup END
|
|
||||||
|
|
||||||
nnoremap <leader>te :tabedit <C-r>=expand("%:p:h")<cr>/
|
|
||||||
nnoremap <leader>cd :lcd %:p:h<cr>:pwd<cr>
|
nnoremap <leader>cd :lcd %:p:h<cr>:pwd<cr>
|
||||||
|
|
||||||
nnoremap 0 ^
|
nnoremap 0 ^
|
||||||
|
vnoremap 0 ^
|
||||||
nnoremap gV `[v`]
|
nnoremap gV `[v`]
|
||||||
|
|
||||||
cnoremap <C-p> <Up>
|
cnoremap <C-p> <Up>
|
||||||
|
|
@ -127,16 +120,12 @@ nnoremap <M-k> :m .-2<CR>==
|
||||||
vnoremap <M-j> :m '>+1<CR>gv=gv
|
vnoremap <M-j> :m '>+1<CR>gv=gv
|
||||||
vnoremap <M-k> :m '<-2<CR>gv=gv
|
vnoremap <M-k> :m '<-2<CR>gv=gv
|
||||||
|
|
||||||
nnoremap <leader>ss :setlocal spell!<cr>
|
nnoremap <silent> <leader>ss :setlocal spell!<CR>:echo 'Spell: ' . (&spell ? 'ON' : 'OFF')<CR>
|
||||||
nnoremap <leader>sn ]s
|
|
||||||
nnoremap <leader>sp [s
|
|
||||||
nnoremap <leader>sa zg
|
|
||||||
nnoremap <leader>s? z=
|
|
||||||
|
|
||||||
set pastetoggle=<F2>
|
nnoremap <silent> <F2> :set paste!<CR>:echo 'Paste: ' . (&paste ? 'ON' : 'OFF')<CR>
|
||||||
nnoremap <F3> :set invnumber<CR>
|
nnoremap <silent> <F3> :set invnumber<CR>:echo 'Line numbers: ' . (&number ? 'ON' : 'OFF')<CR>
|
||||||
nnoremap <F4> :set invrelativenumber<CR>
|
nnoremap <silent> <F4> :set invrelativenumber<CR>:echo 'Relative numbers: ' . (&relativenumber ? 'ON' : 'OFF')<CR>
|
||||||
nnoremap <F6> :set list!<CR>
|
nnoremap <silent> <F6> :set list!<CR>:echo 'List chars: ' . (&list ? 'ON' : 'OFF')<CR>
|
||||||
|
|
||||||
nnoremap <space> za
|
nnoremap <space> za
|
||||||
|
|
||||||
|
|
@ -157,14 +146,18 @@ nnoremap <silent> <C-s> :w<CR>
|
||||||
inoremap <silent> <C-s> <C-o>:w<CR>
|
inoremap <silent> <C-s> <C-o>:w<CR>
|
||||||
|
|
||||||
nnoremap <C-d> <C-d>zz
|
nnoremap <C-d> <C-d>zz
|
||||||
|
vnoremap <C-d> <C-d>zz
|
||||||
nnoremap <C-u> <C-u>zz
|
nnoremap <C-u> <C-u>zz
|
||||||
|
vnoremap <C-u> <C-u>zz
|
||||||
|
|
||||||
if has('clipboard')
|
if has('clipboard')
|
||||||
nnoremap <leader>y "+y
|
nnoremap <leader>y "+y
|
||||||
vnoremap <leader>y "+y
|
vnoremap <leader>y "+y
|
||||||
nnoremap <leader>Y "+Y
|
nnoremap <leader>Y "+Y
|
||||||
nnoremap <leader>p "+p
|
nnoremap <leader>p "+p
|
||||||
|
vnoremap <leader>p "+p
|
||||||
nnoremap <leader>P "+P
|
nnoremap <leader>P "+P
|
||||||
|
vnoremap <leader>P "+P
|
||||||
endif
|
endif
|
||||||
|
|
||||||
nnoremap <leader>qo :copen<CR>
|
nnoremap <leader>qo :copen<CR>
|
||||||
|
|
@ -178,22 +171,21 @@ augroup END
|
||||||
" ── Performance ─────────────────────────────────────────────────────────────
|
" ── Performance ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
set synmaxcol=200
|
set synmaxcol=200
|
||||||
set ttyfast
|
|
||||||
set lazyredraw
|
set lazyredraw
|
||||||
set complete-=i
|
set complete-=i
|
||||||
|
|
||||||
|
if executable('rg')
|
||||||
|
set grepprg=rg\ --vimgrep\ --smart-case
|
||||||
|
set grepformat=%f:%l:%c:%m
|
||||||
|
endif
|
||||||
set updatetime=300
|
set updatetime=300
|
||||||
set shortmess+=cI
|
set shortmess+=cI
|
||||||
|
|
||||||
if g:is_tty
|
if g:is_tty
|
||||||
set signcolumn=auto
|
set signcolumn=auto
|
||||||
set synmaxcol=120
|
set synmaxcol=120
|
||||||
else
|
|
||||||
if has("patch-8.1.1564")
|
|
||||||
set signcolumn=number
|
|
||||||
else
|
|
||||||
set signcolumn=yes
|
|
||||||
endif
|
|
||||||
endif
|
endif
|
||||||
|
" non-TTY signcolumn is set in ui.vim (=yes, fixed-width to prevent jitter)
|
||||||
|
|
||||||
" ── Project-Local Config ────────────────────────────────────────────────────
|
" ── Project-Local Config ────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
@ -201,6 +193,10 @@ set exrc
|
||||||
set secure
|
set secure
|
||||||
set sessionoptions=blank,buffers,curdir,folds,help,tabpages,winsize,winpos,terminal
|
set sessionoptions=blank,buffers,curdir,folds,help,tabpages,winsize,winpos,terminal
|
||||||
|
|
||||||
|
if has("patch-8.1.0360")
|
||||||
|
set diffopt=filler,internal,context:3,algorithm:histogram,indent-heuristic
|
||||||
|
endif
|
||||||
|
|
||||||
" ── Format Options ──────────────────────────────────────────────────────────
|
" ── Format Options ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
augroup ChopstickFormatOptions
|
augroup ChopstickFormatOptions
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ let g:EasyMotion_do_mapping = 0
|
||||||
let g:EasyMotion_smartcase = 1
|
let g:EasyMotion_smartcase = 1
|
||||||
|
|
||||||
if exists('g:plugs["vim-easymotion"]')
|
if exists('g:plugs["vim-easymotion"]')
|
||||||
nmap s <Plug>(easymotion-overwin-f2)
|
nmap <Leader>S <Plug>(easymotion-overwin-f2)
|
||||||
nmap <Leader>j <Plug>(easymotion-j)
|
nmap <Leader>j <Plug>(easymotion-j)
|
||||||
nmap <Leader>k <Plug>(easymotion-k)
|
nmap <Leader>k <Plug>(easymotion-k)
|
||||||
endif
|
endif
|
||||||
|
|
@ -25,7 +25,7 @@ if exists('##TextYankPost') && has('timers')
|
||||||
if v:event.operator !=# 'y' | return | endif
|
if v:event.operator !=# 'y' | return | endif
|
||||||
let l:m = matchadd('IncSearch',
|
let l:m = matchadd('IncSearch',
|
||||||
\ printf('\%%>%dl\%%<%dl', line("'[") - 1, line("']") + 1))
|
\ printf('\%%>%dl\%%<%dl', line("'[") - 1, line("']") + 1))
|
||||||
call timer_start(150, {-> matchdelete(l:m)})
|
call timer_start(150, {-> execute('silent! call matchdelete(' . l:m . ')')})
|
||||||
endfunction
|
endfunction
|
||||||
augroup ChopstickYankHL
|
augroup ChopstickYankHL
|
||||||
autocmd!
|
autocmd!
|
||||||
|
|
@ -44,3 +44,26 @@ augroup ChopstickSearchHL
|
||||||
autocmd!
|
autocmd!
|
||||||
autocmd CursorHold * if get(v:, 'hlsearch', 0) | let v:hlsearch = 0 | endif
|
autocmd CursorHold * if get(v:, 'hlsearch', 0) | let v:hlsearch = 0 | endif
|
||||||
augroup END
|
augroup END
|
||||||
|
|
||||||
|
" ── Overlength Highlight (only chars past textwidth, not the whole column) ─
|
||||||
|
|
||||||
|
function! s:OverLengthApply() abort
|
||||||
|
if exists('w:overlength_match')
|
||||||
|
silent! call matchdelete(w:overlength_match)
|
||||||
|
unlet w:overlength_match
|
||||||
|
endif
|
||||||
|
if &textwidth <= 0 || &buftype !=# '' | return | endif
|
||||||
|
let w:overlength_match = matchadd('OverLength', '\%>' . &textwidth . 'v.\+', -1)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:OverLengthDefineHL() abort
|
||||||
|
hi default OverLength ctermbg=52 ctermfg=NONE guibg=#3a1f1f guifg=NONE
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
augroup ChopstickOverLength
|
||||||
|
autocmd!
|
||||||
|
autocmd ColorScheme * call s:OverLengthDefineHL()
|
||||||
|
autocmd OptionSet textwidth call s:OverLengthApply()
|
||||||
|
autocmd BufWinEnter,WinEnter,FileType * call s:OverLengthApply()
|
||||||
|
augroup END
|
||||||
|
call s:OverLengthDefineHL()
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,48 @@ let g:is_tty = empty($TERM) || $TERM ==# 'dumb' || $TERM =~# 'linux'
|
||||||
\ || $TERM =~# 'screen' || &term =~# 'builtin'
|
\ || $TERM =~# 'screen' || &term =~# 'builtin'
|
||||||
let g:has_true_color = ($COLORTERM ==# 'truecolor' || $COLORTERM ==# '24bit')
|
let g:has_true_color = ($COLORTERM ==# 'truecolor' || $COLORTERM ==# '24bit')
|
||||||
|
|
||||||
|
let g:chopsticks_profile = get(g:, 'chopsticks_profile', 'engineer')
|
||||||
|
if index(['minimal', 'engineer', 'full'], g:chopsticks_profile) < 0
|
||||||
|
let g:chopsticks_profile = 'engineer'
|
||||||
|
endif
|
||||||
|
|
||||||
|
let s:profile_full = g:chopsticks_profile ==# 'full'
|
||||||
|
let s:profile_minimal = g:chopsticks_profile ==# 'minimal'
|
||||||
|
|
||||||
|
let g:chopsticks_enable_lsp = get(g:, 'chopsticks_enable_lsp',
|
||||||
|
\ !s:profile_minimal)
|
||||||
|
let g:chopsticks_enable_lint = get(g:, 'chopsticks_enable_lint',
|
||||||
|
\ !s:profile_minimal)
|
||||||
|
let g:chopsticks_enable_extra_languages = get(g:,
|
||||||
|
\ 'chopsticks_enable_extra_languages', !s:profile_minimal)
|
||||||
|
let g:chopsticks_enable_ui_extras = get(g:, 'chopsticks_enable_ui_extras',
|
||||||
|
\ !s:profile_minimal)
|
||||||
|
let g:chopsticks_enable_markdown_preview = get(g:,
|
||||||
|
\ 'chopsticks_enable_markdown_preview', !s:profile_minimal)
|
||||||
|
|
||||||
|
let g:chopsticks_markdown_lint = get(g:, 'chopsticks_markdown_lint',
|
||||||
|
\ s:profile_full)
|
||||||
|
let g:chopsticks_markdown_format_on_save = get(g:,
|
||||||
|
\ 'chopsticks_markdown_format_on_save', s:profile_full)
|
||||||
|
let g:chopsticks_markdown_lsp = get(g:, 'chopsticks_markdown_lsp',
|
||||||
|
\ s:profile_full)
|
||||||
|
let g:chopsticks_markdown_spell = get(g:, 'chopsticks_markdown_spell',
|
||||||
|
\ s:profile_full)
|
||||||
|
let g:chopsticks_markdown_conceal = get(g:, 'chopsticks_markdown_conceal',
|
||||||
|
\ s:profile_full)
|
||||||
|
let g:chopsticks_lsp_virtual_text = get(g:, 'chopsticks_lsp_virtual_text',
|
||||||
|
\ s:profile_full && !g:is_tty)
|
||||||
|
|
||||||
" Skip built-in plugins we never use
|
" Skip built-in plugins we never use
|
||||||
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 g:loaded_gzip = 1
|
||||||
let g:loaded_logipat = 1
|
let g:loaded_logiPat = 1
|
||||||
let g: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 g:loaded_spellfile_plugin = 1
|
||||||
|
let g:loaded_openPlugin = 1
|
||||||
|
let g:loaded_manpager_plugin = 1
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ if exists('g:plugs["vim-fugitive"]')
|
||||||
nnoremap <leader>gl :Git pull<CR>
|
nnoremap <leader>gl :Git pull<CR>
|
||||||
nnoremap <leader>gd :Gdiffsplit<CR>
|
nnoremap <leader>gd :Gdiffsplit<CR>
|
||||||
nnoremap <leader>gb :Git blame<CR>
|
nnoremap <leader>gb :Git blame<CR>
|
||||||
|
nnoremap <leader>gL :Git log --oneline --graph -20<CR>
|
||||||
endif
|
endif
|
||||||
|
|
||||||
" ── Conflict Navigation ────────────────────────────────────────────────────
|
" ── Conflict Navigation ────────────────────────────────────────────────────
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,32 @@
|
||||||
" languages.vim — vim-go config, per-filetype autocmds
|
" languages.vim — vim-go, vim-markdown, per-filetype autocmds
|
||||||
|
|
||||||
|
" ── vim-markdown ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
let g:vim_markdown_conceal = get(g:, 'vim_markdown_conceal',
|
||||||
|
\ g:chopsticks_markdown_conceal)
|
||||||
|
let g:vim_markdown_conceal_code_blocks = 0
|
||||||
|
let g:vim_markdown_folding_disabled = get(g:, 'vim_markdown_folding_disabled', 1)
|
||||||
|
let g:vim_markdown_folding_level = 2
|
||||||
|
let g:vim_markdown_frontmatter = 1
|
||||||
|
let g:vim_markdown_toml_frontmatter = 1
|
||||||
|
let g:vim_markdown_json_frontmatter = 1
|
||||||
|
let g:vim_markdown_follow_anchor = 1
|
||||||
|
let g:vim_markdown_new_list_item_indent = 2
|
||||||
|
let g:vim_markdown_strikethrough = 1
|
||||||
|
|
||||||
|
if exists('g:plugs["vim-markdown"]')
|
||||||
|
nnoremap <leader>mt :Toc<CR>
|
||||||
|
endif
|
||||||
|
|
||||||
|
if has('macunix')
|
||||||
|
let g:previm_open_cmd = '/usr/bin/open'
|
||||||
|
elseif executable('xdg-open')
|
||||||
|
let g:previm_open_cmd = 'xdg-open'
|
||||||
|
endif
|
||||||
|
let g:previm_enable_realtime = get(g:, 'previm_enable_realtime', 0)
|
||||||
|
if exists('g:plugs["previm"]')
|
||||||
|
nnoremap <leader>mp :PrevimOpen<CR>
|
||||||
|
endif
|
||||||
|
|
||||||
" ── vim-go (syntax only — vim-lsp handles intelligence) ─────────────────────
|
" ── vim-go (syntax only — vim-lsp handles intelligence) ─────────────────────
|
||||||
|
|
||||||
|
|
@ -13,6 +41,22 @@ let g:go_highlight_function_calls = 1
|
||||||
|
|
||||||
" ── Filetype Detection ──────────────────────────────────────────────────────
|
" ── Filetype Detection ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function! s:MarkdownDefaults() abort
|
||||||
|
setlocal wrap linebreak textwidth=0 colorcolumn=0 signcolumn=no
|
||||||
|
let &l:conceallevel = get(g:, 'chopsticks_markdown_conceal', 0) ? 2 : 0
|
||||||
|
|
||||||
|
if get(g:, 'chopsticks_markdown_spell', 0)
|
||||||
|
setlocal spell
|
||||||
|
else
|
||||||
|
setlocal nospell
|
||||||
|
endif
|
||||||
|
|
||||||
|
if !get(g:, 'chopsticks_markdown_lint', 0)
|
||||||
|
\ && !get(g:, 'chopsticks_markdown_format_on_save', 0)
|
||||||
|
let b:ale_enabled = 0
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
augroup ChopstickFiletype
|
augroup ChopstickFiletype
|
||||||
autocmd!
|
autocmd!
|
||||||
|
|
||||||
|
|
@ -20,23 +64,22 @@ augroup ChopstickFiletype
|
||||||
\ if line("'\"") > 1 && line("'\"") <= line("$") | exe "normal! g'\"" | endif
|
\ if line("'\"") > 1 && line("'\"") <= line("$") | exe "normal! g'\"" | endif
|
||||||
|
|
||||||
autocmd FileType python
|
autocmd FileType python
|
||||||
\ setlocal expandtab shiftwidth=4 tabstop=4 textwidth=88 colorcolumn=+1
|
\ setlocal expandtab shiftwidth=4 tabstop=4 textwidth=88
|
||||||
autocmd FileType javascript,typescript
|
autocmd FileType javascript,typescript
|
||||||
\ setlocal expandtab shiftwidth=2 tabstop=2 textwidth=100 colorcolumn=+1
|
\ setlocal expandtab shiftwidth=2 tabstop=2 textwidth=100
|
||||||
autocmd FileType go
|
autocmd FileType go
|
||||||
\ setlocal noexpandtab shiftwidth=4 tabstop=4 textwidth=120 colorcolumn=+1
|
\ setlocal noexpandtab shiftwidth=4 tabstop=4 textwidth=120
|
||||||
autocmd FileType rust
|
autocmd FileType rust
|
||||||
\ setlocal expandtab shiftwidth=4 tabstop=4 textwidth=100 colorcolumn=+1
|
\ setlocal expandtab shiftwidth=4 tabstop=4 textwidth=100
|
||||||
autocmd FileType c,cpp
|
autocmd FileType c,cpp
|
||||||
\ setlocal expandtab shiftwidth=4 tabstop=4 textwidth=80 colorcolumn=+1
|
\ setlocal expandtab shiftwidth=4 tabstop=4 textwidth=80
|
||||||
autocmd FileType html,css
|
autocmd FileType html,css
|
||||||
\ setlocal expandtab shiftwidth=2 tabstop=2
|
\ setlocal expandtab shiftwidth=2 tabstop=2
|
||||||
autocmd FileType yaml
|
autocmd FileType yaml
|
||||||
\ setlocal expandtab shiftwidth=2 tabstop=2
|
\ setlocal expandtab shiftwidth=2 tabstop=2
|
||||||
autocmd FileType markdown
|
autocmd FileType markdown call s:MarkdownDefaults()
|
||||||
\ setlocal wrap linebreak spell textwidth=0 colorcolumn=0 conceallevel=2
|
|
||||||
autocmd FileType sh
|
autocmd FileType sh
|
||||||
\ setlocal expandtab shiftwidth=2 tabstop=2 textwidth=80 colorcolumn=+1
|
\ setlocal expandtab shiftwidth=2 tabstop=2 textwidth=80
|
||||||
autocmd FileType make
|
autocmd FileType make
|
||||||
\ setlocal noexpandtab shiftwidth=8 tabstop=8
|
\ setlocal noexpandtab shiftwidth=8 tabstop=8
|
||||||
autocmd FileType json
|
autocmd FileType json
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
" lint.vim — ALE async linting and format-on-save
|
" lint.vim — ALE async linting and format-on-save
|
||||||
|
|
||||||
|
if !g:chopsticks_enable_lint
|
||||||
|
finish
|
||||||
|
endif
|
||||||
|
|
||||||
let g:ale_disable_lsp = 1
|
let g:ale_disable_lsp = 1
|
||||||
|
|
||||||
let g:ale_linters = {
|
let s:ale_linters = {
|
||||||
\ 'python': ['flake8', 'pylint'],
|
\ 'python': ['flake8', 'pylint'],
|
||||||
\ 'javascript': ['eslint'],
|
\ 'javascript': ['eslint'],
|
||||||
\ 'typescript': ['eslint'],
|
\ 'typescript': ['eslint'],
|
||||||
|
|
@ -14,11 +18,16 @@ let g:ale_linters = {
|
||||||
\ 'dockerfile': ['hadolint'],
|
\ 'dockerfile': ['hadolint'],
|
||||||
\ 'css': ['stylelint'],
|
\ 'css': ['stylelint'],
|
||||||
\ 'scss': ['stylelint'],
|
\ 'scss': ['stylelint'],
|
||||||
\ 'markdown': ['markdownlint'],
|
|
||||||
\ 'sql': ['sqlfluff'],
|
\ 'sql': ['sqlfluff'],
|
||||||
\}
|
\}
|
||||||
|
|
||||||
let g:ale_fixers = {
|
if g:chopsticks_markdown_lint
|
||||||
|
let s:ale_linters.markdown = ['markdownlint']
|
||||||
|
endif
|
||||||
|
|
||||||
|
let g:ale_linters = s:ale_linters
|
||||||
|
|
||||||
|
let s:ale_fixers = {
|
||||||
\ '*': ['remove_trailing_lines', 'trim_whitespace'],
|
\ '*': ['remove_trailing_lines', 'trim_whitespace'],
|
||||||
\ 'python': ['black', 'isort'],
|
\ 'python': ['black', 'isort'],
|
||||||
\ 'javascript': ['prettier', 'eslint'],
|
\ 'javascript': ['prettier', 'eslint'],
|
||||||
|
|
@ -32,11 +41,16 @@ let g:ale_fixers = {
|
||||||
\ 'css': ['prettier'],
|
\ 'css': ['prettier'],
|
||||||
\ 'scss': ['prettier'],
|
\ 'scss': ['prettier'],
|
||||||
\ 'less': ['prettier'],
|
\ 'less': ['prettier'],
|
||||||
\ 'markdown': ['prettier'],
|
|
||||||
\ 'sql': ['sqlfluff'],
|
\ 'sql': ['sqlfluff'],
|
||||||
\}
|
\}
|
||||||
|
|
||||||
let g:ale_fix_on_save = 1
|
if g:chopsticks_markdown_format_on_save
|
||||||
|
let s:ale_fixers.markdown = ['prettier']
|
||||||
|
endif
|
||||||
|
|
||||||
|
let g:ale_fixers = s:ale_fixers
|
||||||
|
|
||||||
|
let g:ale_fix_on_save = get(g:, 'ale_fix_on_save', 1)
|
||||||
let g:ale_python_isort_options = '--profile black'
|
let g:ale_python_isort_options = '--profile black'
|
||||||
let g:ale_sign_error = 'X'
|
let g:ale_sign_error = 'X'
|
||||||
let g:ale_sign_warning = '!'
|
let g:ale_sign_warning = '!'
|
||||||
|
|
@ -45,9 +59,12 @@ let g:ale_lint_on_insert_leave = 1
|
||||||
let g:ale_lint_on_enter = 1
|
let g:ale_lint_on_enter = 1
|
||||||
let g:ale_lint_delay = 200
|
let g:ale_lint_delay = 200
|
||||||
let g:ale_echo_delay = 100
|
let g:ale_echo_delay = 100
|
||||||
|
let g:ale_virtualtext_cursor = get(g:, 'ale_virtualtext_cursor', 'disabled')
|
||||||
|
|
||||||
if exists('g:plugs["ale"]')
|
if exists('g:plugs["ale"]')
|
||||||
nnoremap <silent> [e :ALEPrevious<cr>
|
nnoremap <silent> [e :ALEPrevious<cr>
|
||||||
nnoremap <silent> ]e :ALENext<cr>
|
nnoremap <silent> ]e :ALENext<cr>
|
||||||
nnoremap <silent> <leader>aD :ALEDetail<cr>
|
nnoremap <silent> <leader>aD :ALEDetail<cr>
|
||||||
|
nnoremap <silent> <leader>af :let g:ale_fix_on_save = !g:ale_fix_on_save
|
||||||
|
\ <bar> echo 'Format on save: ' . (g:ale_fix_on_save ? 'ON' : 'OFF')<cr>
|
||||||
endif
|
endif
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
" lsp.vim — vim-lsp settings, asyncomplete, LSP buffer keymaps
|
" lsp.vim — vim-lsp settings, asyncomplete, LSP buffer keymaps
|
||||||
|
|
||||||
|
if !g:chopsticks_enable_lsp
|
||||||
|
finish
|
||||||
|
endif
|
||||||
|
|
||||||
let g:lsp_settings_lazyload = 1
|
let g:lsp_settings_lazyload = 1
|
||||||
|
|
||||||
let g:lsp_settings_filetype_python = ['pylsp']
|
let g:lsp_settings_filetype_python = ['pylsp']
|
||||||
|
|
@ -14,10 +18,13 @@ let g:lsp_settings_filetype_css = ['vscode-css-language-server']
|
||||||
let g:lsp_settings_filetype_scss = ['vscode-css-language-server']
|
let g:lsp_settings_filetype_scss = ['vscode-css-language-server']
|
||||||
let g:lsp_settings_filetype_json = ['vscode-json-language-server']
|
let g:lsp_settings_filetype_json = ['vscode-json-language-server']
|
||||||
let g:lsp_settings_filetype_yaml = ['yaml-language-server']
|
let g:lsp_settings_filetype_yaml = ['yaml-language-server']
|
||||||
let g:lsp_settings_filetype_markdown = ['marksman']
|
|
||||||
let g:lsp_settings_filetype_sql = ['sqls']
|
let g:lsp_settings_filetype_sql = ['sqls']
|
||||||
|
|
||||||
let g:lsp_diagnostics_virtual_text_enabled = !g:is_tty
|
if g:chopsticks_markdown_lsp
|
||||||
|
let g:lsp_settings_filetype_markdown = ['marksman']
|
||||||
|
endif
|
||||||
|
|
||||||
|
let g:lsp_diagnostics_virtual_text_enabled = g:chopsticks_lsp_virtual_text
|
||||||
let g:lsp_diagnostics_virtual_text_delay = 200
|
let g:lsp_diagnostics_virtual_text_delay = 200
|
||||||
let g:lsp_diagnostics_highlights_enabled = !g:is_tty
|
let g:lsp_diagnostics_highlights_enabled = !g:is_tty
|
||||||
let g:lsp_document_highlight_enabled = !g:is_tty
|
let g:lsp_document_highlight_enabled = !g:is_tty
|
||||||
|
|
@ -52,7 +59,9 @@ inoremap <expr> <CR> pumvisible() ? asyncomplete#close_popup() : "\<CR>"
|
||||||
|
|
||||||
function! s:on_lsp_buffer_enabled() abort
|
function! s:on_lsp_buffer_enabled() abort
|
||||||
setlocal omnifunc=lsp#complete
|
setlocal omnifunc=lsp#complete
|
||||||
|
if !g:is_tty && &filetype !=# 'markdown'
|
||||||
setlocal signcolumn=yes
|
setlocal signcolumn=yes
|
||||||
|
endif
|
||||||
|
|
||||||
nmap <buffer> gd <plug>(lsp-definition)
|
nmap <buffer> gd <plug>(lsp-definition)
|
||||||
nmap <buffer> gy <plug>(lsp-type-definition)
|
nmap <buffer> gy <plug>(lsp-type-definition)
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,37 @@
|
||||||
|
|
||||||
let g:netrw_liststyle = 3
|
let g:netrw_liststyle = 3
|
||||||
let g:netrw_banner = 0
|
let g:netrw_banner = 0
|
||||||
let g:netrw_browse_split = 0
|
let g:netrw_browse_split = 4
|
||||||
let g:netrw_winsize = 25
|
let g:netrw_winsize = 25
|
||||||
|
let g:netrw_altv = 1
|
||||||
let g:netrw_list_hide = '\(^\|\s\s\)\zs\.\S\+'
|
let g:netrw_list_hide = '\(^\|\s\s\)\zs\.\S\+'
|
||||||
let g:netrw_list_hide .= ',\.pyc$,node_modules,\.git,__pycache__,\.DS_Store'
|
let g:netrw_list_hide .= ',\.pyc$,node_modules,\.git,__pycache__,\.DS_Store'
|
||||||
|
|
||||||
nnoremap <leader>e :Explore<CR>
|
function! s:ToggleSidebar(...) abort
|
||||||
nnoremap <leader>E :Vexplore<CR>
|
let l:dir = a:0 ? a:1 : getcwd()
|
||||||
|
if getbufvar(winbufnr(1), '&filetype') ==# 'netrw' && getwinvar(1, '&winfixwidth')
|
||||||
|
let l:cur = winnr()
|
||||||
|
1wincmd w
|
||||||
|
close
|
||||||
|
if l:cur > 1
|
||||||
|
execute (l:cur - 1) . 'wincmd w'
|
||||||
|
endif
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
execute 'topleft vertical 30new'
|
||||||
|
execute 'Explore ' . fnameescape(l:dir)
|
||||||
|
setlocal winfixwidth
|
||||||
|
setlocal bufhidden=wipe
|
||||||
|
wincmd p
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
nnoremap <silent> <leader>e :call <SID>ToggleSidebar()<CR>
|
||||||
|
nnoremap <silent> <leader>E :call <SID>ToggleSidebar(expand('%:p:h'))<CR>
|
||||||
|
|
||||||
|
augroup ChopstickNetrw
|
||||||
|
autocmd!
|
||||||
|
autocmd FileType netrw setlocal bufhidden=wipe
|
||||||
|
augroup END
|
||||||
|
|
||||||
" ── FZF ─────────────────────────────────────────────────────────────────────
|
" ── FZF ─────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
@ -36,6 +60,8 @@ if exists('g:plugs["fzf.vim"]')
|
||||||
nnoremap <leader>fL :Lines<CR>
|
nnoremap <leader>fL :Lines<CR>
|
||||||
nnoremap <leader>f/ :History/<CR>
|
nnoremap <leader>f/ :History/<CR>
|
||||||
nnoremap <leader>f: :History:<CR>
|
nnoremap <leader>f: :History:<CR>
|
||||||
|
nnoremap <leader>gC :Commits<CR>
|
||||||
|
nnoremap <leader>gB :BCommits<CR>
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let g:fzf_layout = { 'down': '40%' }
|
let g:fzf_layout = { 'down': '40%' }
|
||||||
|
|
@ -46,30 +72,27 @@ else
|
||||||
let g:fzf_preview_window = ['right:50%', 'ctrl-/']
|
let g:fzf_preview_window = ['right:50%', 'ctrl-/']
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if g:is_tty
|
function! s:Preview() abort
|
||||||
command! -bang -nargs=* Rg
|
return g:is_tty ? {} : fzf#vim#with_preview()
|
||||||
\ call fzf#vim#grep(
|
endfunction
|
||||||
\ 'rg --column --line-number --no-heading --color=always --smart-case -- '
|
|
||||||
\ .shellescape(<q-args>), 1, <bang>0)
|
|
||||||
command! -bang GFiles call fzf#vim#gitfiles('', <bang>0)
|
|
||||||
else
|
|
||||||
command! -bang -nargs=* Rg
|
|
||||||
\ call fzf#vim#grep(
|
|
||||||
\ 'rg --column --line-number --no-heading --color=always --smart-case -- '
|
|
||||||
\ .shellescape(<q-args>), 1, fzf#vim#with_preview(), <bang>0)
|
|
||||||
command! -bang GFiles call fzf#vim#gitfiles('', fzf#vim#with_preview(), <bang>0)
|
|
||||||
endif
|
|
||||||
|
|
||||||
if g:is_tty
|
command! -bang -nargs=* Rg
|
||||||
command! -bang -nargs=* RgWord
|
\ call fzf#vim#grep(
|
||||||
|
\ 'rg --column --line-number --no-heading --color=always --smart-case -- '
|
||||||
|
\ .shellescape(<q-args>), 1, s:Preview(), <bang>0)
|
||||||
|
command! -bang -nargs=* RgWord
|
||||||
\ call fzf#vim#grep(
|
\ call fzf#vim#grep(
|
||||||
\ 'rg --column --line-number --no-heading --color=always --smart-case -F -- '
|
\ 'rg --column --line-number --no-heading --color=always --smart-case -F -- '
|
||||||
\ .shellescape(expand('<cword>')), 1, <bang>0)
|
\ .shellescape(expand('<cword>')), 1, s:Preview(), <bang>0)
|
||||||
else
|
command! -bang -nargs=? GFiles call fzf#vim#gitfiles(<q-args>, s:Preview(), <bang>0)
|
||||||
command! -bang -nargs=* RgWord
|
|
||||||
\ call fzf#vim#grep(
|
" ── Window Navigation ───────────────────────────────────────────────────────
|
||||||
\ 'rg --column --line-number --no-heading --color=always --smart-case -F -- '
|
|
||||||
\ .shellescape(expand('<cword>')), 1, fzf#vim#with_preview(), <bang>0)
|
if empty($TMUX)
|
||||||
|
nnoremap <C-h> <C-w>h
|
||||||
|
nnoremap <C-j> <C-w>j
|
||||||
|
nnoremap <C-k> <C-w>k
|
||||||
|
nnoremap <C-l> <C-w>l
|
||||||
endif
|
endif
|
||||||
|
|
||||||
" ── Window Maximize Toggle ──────────────────────────────────────────────────
|
" ── Window Maximize Toggle ──────────────────────────────────────────────────
|
||||||
|
|
@ -78,9 +101,11 @@ function! s:ToggleMaximize() abort
|
||||||
if exists('t:maximize_session')
|
if exists('t:maximize_session')
|
||||||
execute t:maximize_session
|
execute t:maximize_session
|
||||||
unlet t:maximize_session
|
unlet t:maximize_session
|
||||||
|
echo 'Window: restored'
|
||||||
else
|
else
|
||||||
let t:maximize_session = winrestcmd()
|
let t:maximize_session = winrestcmd()
|
||||||
resize | vertical resize
|
resize | vertical resize
|
||||||
|
echo 'Window: MAXIMIZED'
|
||||||
endif
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
nnoremap <silent> <leader>z :call <SID>ToggleMaximize()<CR>
|
nnoremap <silent> <leader>z :call <SID>ToggleMaximize()<CR>
|
||||||
|
|
|
||||||
|
|
@ -24,45 +24,43 @@ Plug 'airblade/vim-gitgutter'
|
||||||
Plug 'tpope/vim-surround'
|
Plug 'tpope/vim-surround'
|
||||||
Plug 'tpope/vim-commentary'
|
Plug 'tpope/vim-commentary'
|
||||||
Plug 'tpope/vim-repeat'
|
Plug 'tpope/vim-repeat'
|
||||||
" tpope/vim-unimpaired removed: 2.5ms startup cost, we define our own
|
|
||||||
" [q/]q (quickfix), [e/]e (ALE), [x/]x (conflict) — unimpaired's [b/]b
|
|
||||||
" is covered by ,h/,l. Blank line insertion ([<Space>) added below.
|
|
||||||
|
|
||||||
Plug 'tpope/vim-sleuth'
|
Plug 'tpope/vim-sleuth'
|
||||||
Plug 'wellle/targets.vim'
|
Plug 'wellle/targets.vim'
|
||||||
Plug 'jiangmiao/auto-pairs'
|
Plug 'jiangmiao/auto-pairs'
|
||||||
Plug 'easymotion/vim-easymotion', { 'on': '<Plug>(easymotion' }
|
Plug 'easymotion/vim-easymotion', { 'on': '<Plug>(easymotion' }
|
||||||
|
|
||||||
" ── Linting & Formatting ────────────────────────────────────────────────────
|
if g:chopsticks_enable_lint
|
||||||
Plug 'dense-analysis/ale'
|
" ── Linting & Formatting ────────────────────────────────────────────────
|
||||||
|
Plug 'dense-analysis/ale'
|
||||||
" ── LSP + Completion (no Node.js required) ──────────────────────────────────
|
|
||||||
Plug 'prabirshrestha/vim-lsp'
|
|
||||||
Plug 'mattn/vim-lsp-settings'
|
|
||||||
Plug 'prabirshrestha/asyncomplete.vim'
|
|
||||||
Plug 'prabirshrestha/asyncomplete-lsp.vim'
|
|
||||||
|
|
||||||
" ── Language Syntax ──────────────────────────────────────────────────────────
|
|
||||||
Plug 'pangloss/vim-javascript', { 'for': ['javascript', 'javascript.jsx'] }
|
|
||||||
Plug 'HerringtonDarkholme/yats.vim', { 'for': ['typescript', 'typescript.tsx'] }
|
|
||||||
Plug 'preservim/vim-markdown', { 'for': 'markdown' }
|
|
||||||
Plug 'fatih/vim-go', { 'for': 'go' }
|
|
||||||
|
|
||||||
" ── Markdown Preview & Writing ───────────────────────────────────────────────
|
|
||||||
Plug 'previm/previm', { 'on': 'PrevimOpen' }
|
|
||||||
Plug 'junegunn/goyo.vim', { 'on': 'Goyo' }
|
|
||||||
Plug 'junegunn/limelight.vim', { 'on': ['Limelight', 'Limelight!'] }
|
|
||||||
|
|
||||||
" ── UI ───────────────────────────────────────────────────────────────────────
|
|
||||||
Plug 'mbbill/undotree', { 'on': 'UndotreeToggle' }
|
|
||||||
Plug 'mhinz/vim-startify'
|
|
||||||
Plug 'lifepillar/vim-solarized8'
|
|
||||||
if !g:is_tty
|
|
||||||
Plug 'Yggdroot/indentLine'
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
" ── Session & Navigation ────────────────────────────────────────────────────
|
if g:chopsticks_enable_lsp
|
||||||
Plug 'tpope/vim-obsession'
|
" ── LSP + Completion ─────────────────────────────────────────────────────
|
||||||
Plug 'christoomey/vim-tmux-navigator'
|
Plug 'prabirshrestha/vim-lsp'
|
||||||
|
Plug 'mattn/vim-lsp-settings'
|
||||||
|
Plug 'prabirshrestha/asyncomplete.vim'
|
||||||
|
Plug 'prabirshrestha/asyncomplete-lsp.vim'
|
||||||
|
endif
|
||||||
|
|
||||||
|
" ── Language Syntax ──────────────────────────────────────────────────────────
|
||||||
|
Plug 'preservim/vim-markdown', { 'for': 'markdown' }
|
||||||
|
if g:chopsticks_enable_markdown_preview
|
||||||
|
Plug 'previm/previm', { 'on': 'PrevimOpen' }
|
||||||
|
endif
|
||||||
|
if g:chopsticks_enable_extra_languages
|
||||||
|
Plug 'pangloss/vim-javascript', { 'for': ['javascript', 'javascript.jsx'] }
|
||||||
|
Plug 'HerringtonDarkholme/yats.vim', { 'for': ['typescript', 'typescript.tsx'] }
|
||||||
|
Plug 'fatih/vim-go', { 'for': 'go' }
|
||||||
|
endif
|
||||||
|
|
||||||
|
" ── UI ───────────────────────────────────────────────────────────────────────
|
||||||
|
if g:chopsticks_enable_ui_extras
|
||||||
|
Plug 'mbbill/undotree', { 'on': 'UndotreeToggle' }
|
||||||
|
Plug 'mhinz/vim-startify'
|
||||||
|
endif
|
||||||
|
Plug 'lifepillar/vim-solarized8'
|
||||||
|
if !empty($TMUX)
|
||||||
|
Plug 'christoomey/vim-tmux-navigator'
|
||||||
|
endif
|
||||||
|
|
||||||
call plug#end()
|
call plug#end()
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,6 @@
|
||||||
" tools.vim — cheat sheet, run file, sudo save, quickfix, helpers
|
" tools.vim — run file, sudo save, quickfix, helpers
|
||||||
|
|
||||||
" ── Helper Functions ────────────────────────────────────────────────────────
|
" ── Buffer Close ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
function! HasPaste()
|
|
||||||
if &paste | return 'PASTE MODE ' | endif
|
|
||||||
return ''
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
command! Bclose call <SID>BufcloseCloseIt()
|
command! Bclose call <SID>BufcloseCloseIt()
|
||||||
function! <SID>BufcloseCloseIt()
|
function! <SID>BufcloseCloseIt()
|
||||||
|
|
@ -24,50 +19,31 @@ function! <SID>BufcloseCloseIt()
|
||||||
endif
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
fun! CleanExtraSpaces()
|
" ── Utilities ──────────────────────────────────────────────────────────────
|
||||||
let save_cursor = getpos(".")
|
|
||||||
let old_query = getreg('/')
|
|
||||||
silent! %s/\s\+$//e
|
|
||||||
call setpos('.', save_cursor)
|
|
||||||
call setreg('/', old_query)
|
|
||||||
endfun
|
|
||||||
|
|
||||||
function! ToggleNumber()
|
|
||||||
if(&relativenumber == 1)
|
|
||||||
set norelativenumber
|
|
||||||
set number
|
|
||||||
else
|
|
||||||
set relativenumber
|
|
||||||
endif
|
|
||||||
endfunc
|
|
||||||
|
|
||||||
" ── Additional Utilities ────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
nnoremap <leader>F gg=G``
|
nnoremap <leader>F gg=G``
|
||||||
|
vnoremap <leader>F =
|
||||||
nnoremap <leader>wa :wa<CR>
|
nnoremap <leader>wa :wa<CR>
|
||||||
|
|
||||||
nnoremap <silent> <Leader>= :exe "resize " . (winheight(0) * 3/2)<CR>
|
nnoremap <silent> <Leader>= :exe "resize " . (winheight(0) * 3/2)<CR>
|
||||||
nnoremap <silent> <Leader>- :exe "resize " . (winheight(0) * 2/3)<CR>
|
nnoremap <silent> <Leader>- :exe "resize " . (winheight(0) * 2/3)<CR>
|
||||||
nnoremap <silent> <Leader>+ :exe "vertical resize " . (winwidth(0) * 3/2)<CR>
|
|
||||||
nnoremap <silent> <Leader>_ :exe "vertical resize " . (winwidth(0) * 2/3)<CR>
|
|
||||||
|
|
||||||
nnoremap <leader><leader> <c-^>
|
nnoremap <leader><leader> <c-^>
|
||||||
|
|
||||||
nnoremap <leader>W :%s/\s\+$//<CR>:let @/=''<CR>
|
nnoremap <leader>W :%s/\s\+$//<CR>:let @/=''<CR>
|
||||||
|
vnoremap <leader>W :s/\s\+$//<CR>:let @/=''<CR>gv
|
||||||
|
|
||||||
nnoremap <leader>so :if &filetype ==# 'vim' <Bar> source % <Bar> echo "Sourced " . expand('%') <Bar> else <Bar> echo "Not a vim file" <Bar> endif<CR>
|
|
||||||
nnoremap <leader>ev :edit $MYVIMRC<CR>
|
nnoremap <leader>ev :edit $MYVIMRC<CR>
|
||||||
nnoremap <leader>sv :source $MYVIMRC<CR>:echo "vimrc reloaded"<CR>
|
nnoremap <leader>sv :unlet! g:chopsticks_loaded<CR>:execute 'source ' . fnameescape($MYVIMRC)<CR>:echo "vimrc reloaded"<CR>
|
||||||
|
|
||||||
nnoremap <leader>* :%s/\<<C-r><C-w>\>//g<Left><Left>
|
nnoremap <leader>* :%s/\<<C-r><C-w>\>//g<Left><Left>
|
||||||
|
vnoremap <leader>* :s///g<Left><Left><Left>
|
||||||
|
|
||||||
if has('clipboard')
|
if has('clipboard')
|
||||||
nnoremap <leader>cp :let @+ = expand("%:p")<CR>:echo "Copied: " . expand("%:p")<CR>
|
nnoremap <leader>cp :let @+ = expand("%:p")<CR>:echo "Copied: " . expand("%:p")<CR>
|
||||||
nnoremap <leader>cf :let @+ = expand("%:t")<CR>:echo "Copied: " . expand("%:t")<CR>
|
nnoremap <leader>cf :let @+ = expand("%:t")<CR>:echo "Copied: " . expand("%:t")<CR>
|
||||||
endif
|
endif
|
||||||
|
|
||||||
nnoremap <leader>ms :e ~/buffer.md<cr>
|
|
||||||
|
|
||||||
" ── Auto-Create Directories ─────────────────────────────────────────────────
|
" ── Auto-Create Directories ─────────────────────────────────────────────────
|
||||||
|
|
||||||
function! s:MkNonExDir(file, buf)
|
function! s:MkNonExDir(file, buf)
|
||||||
|
|
@ -88,31 +64,43 @@ augroup END
|
||||||
|
|
||||||
" ── Large File Handling ──────────────────────────────────────────────────────
|
" ── Large File Handling ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
let g:LargeFile = 1024 * 1024 * 10
|
let g:LargeFile = get(g:, 'LargeFile', 1024 * 1024 * 10)
|
||||||
let s:tty_large = g:is_tty ? 512000 : g:LargeFile
|
let s:tty_large = g:is_tty ? 512000 : g:LargeFile
|
||||||
|
|
||||||
|
function! s:ApplyLargeFileSettings() abort
|
||||||
|
if get(b:, 'chopsticks_large_file', 0)
|
||||||
|
setlocal bufhidden=unload undolevels=-1 noswapfile
|
||||||
|
let b:ale_enabled = 0
|
||||||
|
if &l:syntax !=# ''
|
||||||
|
setlocal syntax=
|
||||||
|
endif
|
||||||
|
elseif get(b:, 'chopsticks_tty_large_file', 0)
|
||||||
|
if &l:syntax !=# ''
|
||||||
|
setlocal syntax=
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:MarkLargeFile(file) abort
|
||||||
|
if empty(a:file)
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:fsize = getfsize(a:file)
|
||||||
|
if l:fsize > g:LargeFile || l:fsize == -2
|
||||||
|
let b:chopsticks_large_file = 1
|
||||||
|
elseif g:is_tty && l:fsize > s:tty_large
|
||||||
|
let b:chopsticks_tty_large_file = 1
|
||||||
|
endif
|
||||||
|
call s:ApplyLargeFileSettings()
|
||||||
|
endfunction
|
||||||
|
|
||||||
augroup ChopstickLargeFile
|
augroup ChopstickLargeFile
|
||||||
autocmd!
|
autocmd!
|
||||||
autocmd BufReadPre *
|
autocmd BufReadPre * call s:MarkLargeFile(expand('<afile>'))
|
||||||
\ if !empty(expand('<afile>')) |
|
autocmd BufReadPost,FileType,Syntax * call s:ApplyLargeFileSettings()
|
||||||
\ let s:fsize = getfsize(expand('<afile>')) |
|
|
||||||
\ if s:fsize > g:LargeFile || s:fsize == -2 |
|
|
||||||
\ setlocal bufhidden=unload undolevels=-1 noswapfile syntax= |
|
|
||||||
\ let b:ale_enabled = 0 |
|
|
||||||
\ elseif g:is_tty && s:fsize > s:tty_large |
|
|
||||||
\ setlocal syntax= |
|
|
||||||
\ endif |
|
|
||||||
\ endif
|
|
||||||
augroup END
|
augroup END
|
||||||
|
|
||||||
if g:is_tty && !exists("g:tty_message_shown")
|
|
||||||
augroup TTYMessage
|
|
||||||
autocmd!
|
|
||||||
autocmd VimEnter * echom "TTY mode — visual features disabled"
|
|
||||||
augroup END
|
|
||||||
let g:tty_message_shown = 1
|
|
||||||
endif
|
|
||||||
|
|
||||||
" ── Run Current File (,cr) ──────────────────────────────────────────────────
|
" ── Run Current File (,cr) ──────────────────────────────────────────────────
|
||||||
|
|
||||||
function! s:RunFile() abort
|
function! s:RunFile() abort
|
||||||
|
|
@ -138,7 +126,7 @@ nnoremap <leader>cr :call <SID>RunFile()<CR>
|
||||||
|
|
||||||
cnoremap w!! w !sudo tee > /dev/null %
|
cnoremap w!! w !sudo tee > /dev/null %
|
||||||
|
|
||||||
" ── QuickFix Improvements ───────────────────────────────────────────────────
|
" ── QuickFix ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
augroup ChopstickQF
|
augroup ChopstickQF
|
||||||
autocmd!
|
autocmd!
|
||||||
|
|
@ -149,112 +137,251 @@ augroup END
|
||||||
nnoremap <silent> ]q :cnext<CR>
|
nnoremap <silent> ]q :cnext<CR>
|
||||||
nnoremap <silent> [q :cprev<CR>
|
nnoremap <silent> [q :cprev<CR>
|
||||||
|
|
||||||
" ── Debug Helpers ───────────────────────────────────────────────────────────
|
" ── Status Diagnostic (:ChopsticksStatus) ───────────────────────────────────
|
||||||
|
|
||||||
nnoremap <leader>sh :call <SID>SynStack()<CR>
|
function! s:Check(name, cmd) abort
|
||||||
function! <SID>SynStack()
|
return executable(a:cmd) ? ' OK ' . a:name : ' -- ' . a:name . ' (missing: ' . a:cmd . ')'
|
||||||
if !exists("*synstack") | return | endif
|
endfunction
|
||||||
echo map(synstack(line('.'), col('.')), 'synIDattr(v:val, "name")')
|
|
||||||
endfunc
|
function! s:Off(name, reason) abort
|
||||||
|
return ' off ' . a:name . ' (' . a:reason . ')'
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:LspCheck(ft, server) abort
|
||||||
|
if !get(g:, 'chopsticks_enable_lsp', 1)
|
||||||
|
return s:Off(a:ft, 'LSP disabled by profile')
|
||||||
|
endif
|
||||||
|
if !exists('*lsp#get_server_names')
|
||||||
|
return ' -- ' . a:ft . ' (vim-lsp not loaded)'
|
||||||
|
endif
|
||||||
|
let l:dir = expand('~/.local/share/vim-lsp-settings/servers/' . a:server)
|
||||||
|
if isdirectory(l:dir)
|
||||||
|
return ' OK ' . a:ft . ' (' . a:server . ')'
|
||||||
|
endif
|
||||||
|
return ' -- ' . a:ft . ' (:LspInstallServer in a ' . a:ft . ' file)'
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:ChopsticksStatus() abort
|
||||||
|
let l:lines = []
|
||||||
|
call add(l:lines, 'chopsticks status')
|
||||||
|
call add(l:lines, repeat('─', 50))
|
||||||
|
call add(l:lines, '')
|
||||||
|
|
||||||
|
call add(l:lines, '── system tools ──')
|
||||||
|
call add(l:lines, s:Check('fzf', 'fzf'))
|
||||||
|
call add(l:lines, s:Check('ripgrep', 'rg'))
|
||||||
|
call add(l:lines, s:Check('git', 'git'))
|
||||||
|
call add(l:lines, s:Check('curl', 'curl'))
|
||||||
|
call add(l:lines, s:Check('node', 'node'))
|
||||||
|
call add(l:lines, s:Check('python3', 'python3'))
|
||||||
|
call add(l:lines, s:Check('go', 'go'))
|
||||||
|
call add(l:lines, '')
|
||||||
|
|
||||||
|
call add(l:lines, '── lsp servers ── (:LspInstallServer to install)')
|
||||||
|
call add(l:lines, s:LspCheck('python', 'pylsp'))
|
||||||
|
call add(l:lines, s:LspCheck('go', 'gopls'))
|
||||||
|
call add(l:lines, s:LspCheck('rust', 'rust-analyzer'))
|
||||||
|
call add(l:lines, s:LspCheck('typescript', 'typescript-language-server'))
|
||||||
|
call add(l:lines, s:LspCheck('c/c++', 'clangd'))
|
||||||
|
call add(l:lines, s:LspCheck('bash', 'bash-language-server'))
|
||||||
|
call add(l:lines, s:LspCheck('html', 'vscode-html-language-server'))
|
||||||
|
call add(l:lines, s:LspCheck('json', 'vscode-json-language-server'))
|
||||||
|
call add(l:lines, s:LspCheck('yaml', 'yaml-language-server'))
|
||||||
|
call add(l:lines, s:LspCheck('markdown', 'marksman'))
|
||||||
|
call add(l:lines, s:LspCheck('sql', 'sqls'))
|
||||||
|
call add(l:lines, '')
|
||||||
|
|
||||||
|
call add(l:lines, '── linters ──')
|
||||||
|
if get(g:, 'chopsticks_enable_lint', 1)
|
||||||
|
call add(l:lines, s:Check('flake8 (python)', 'flake8'))
|
||||||
|
call add(l:lines, s:Check('pylint (python)', 'pylint'))
|
||||||
|
call add(l:lines, s:Check('eslint (js/ts)', 'eslint'))
|
||||||
|
call add(l:lines, s:Check('staticcheck (go)', 'staticcheck'))
|
||||||
|
call add(l:lines, s:Check('shellcheck (sh)', 'shellcheck'))
|
||||||
|
call add(l:lines, s:Check('yamllint (yaml)', 'yamllint'))
|
||||||
|
call add(l:lines, s:Check('hadolint (docker)', 'hadolint'))
|
||||||
|
if get(g:, 'chopsticks_markdown_lint', 0)
|
||||||
|
call add(l:lines, s:Check('markdownlint (md)', 'markdownlint'))
|
||||||
|
else
|
||||||
|
call add(l:lines, s:Off('markdownlint (md)', 'disabled by default'))
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
call add(l:lines, s:Off('ALE linters', 'lint disabled by profile'))
|
||||||
|
endif
|
||||||
|
call add(l:lines, '')
|
||||||
|
|
||||||
|
call add(l:lines, '── formatters ── (format-on-save is ' . (get(g:, 'ale_fix_on_save', 0) ? 'ON' : 'OFF') . ')')
|
||||||
|
if get(g:, 'chopsticks_enable_lint', 1)
|
||||||
|
call add(l:lines, s:Check('black (python)', 'black'))
|
||||||
|
call add(l:lines, s:Check('isort (python)', 'isort'))
|
||||||
|
call add(l:lines, s:Check('prettier (js/ts/json)', 'prettier'))
|
||||||
|
if get(g:, 'chopsticks_markdown_format_on_save', 0)
|
||||||
|
call add(l:lines, s:Check('prettier (md)', 'prettier'))
|
||||||
|
else
|
||||||
|
call add(l:lines, s:Off('prettier (md)', 'disabled by default'))
|
||||||
|
endif
|
||||||
|
call add(l:lines, s:Check('goimports (go)', 'goimports'))
|
||||||
|
call add(l:lines, s:Check('rustfmt (rust)', 'rustfmt'))
|
||||||
|
call add(l:lines, s:Check('clang-format (c)', 'clang-format'))
|
||||||
|
else
|
||||||
|
call add(l:lines, s:Off('ALE formatters', 'lint disabled by profile'))
|
||||||
|
endif
|
||||||
|
call add(l:lines, '')
|
||||||
|
|
||||||
|
let l:ok = len(filter(copy(l:lines), 'v:val =~# " OK "'))
|
||||||
|
let l:miss = len(filter(copy(l:lines), 'v:val =~# " -- "'))
|
||||||
|
call add(l:lines, repeat('─', 50))
|
||||||
|
call add(l:lines, ' ' . l:ok . ' ready, ' . l:miss . ' missing')
|
||||||
|
call add(l:lines, '')
|
||||||
|
call add(l:lines, ' Install missing tools with ./install.sh')
|
||||||
|
if get(g:, 'chopsticks_enable_lsp', 1)
|
||||||
|
call add(l:lines, ' Install LSP servers with :LspInstallServer')
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:name = '__ChopsticksStatus__'
|
||||||
|
if bufwinnr(l:name) > 0
|
||||||
|
execute bufwinnr(l:name) . 'wincmd w | bd'
|
||||||
|
endif
|
||||||
|
execute 'botright new ' . l:name
|
||||||
|
resize 45
|
||||||
|
setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile
|
||||||
|
setlocal nowrap nonumber norelativenumber signcolumn=no
|
||||||
|
call setline(1, l:lines)
|
||||||
|
setlocal nomodifiable readonly
|
||||||
|
nnoremap <buffer> <silent> q :bd<CR>
|
||||||
|
endfunction
|
||||||
|
command! ChopsticksStatus call s:ChopsticksStatus()
|
||||||
|
|
||||||
" ── Cheat Sheet (,?) ────────────────────────────────────────────────────────
|
" ── Cheat Sheet (,?) ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
function! s:CheatSheet() abort
|
function! s:CheatSheet() abort
|
||||||
let l:name = '__ChopsticksCheatSheet__'
|
let l:name = '__ChopsticksCheatSheet__'
|
||||||
if bufwinnr(l:name) > 0
|
if bufwinnr(l:name) > 0
|
||||||
execute bufwinnr(l:name) . 'wincmd w'
|
execute bufwinnr(l:name) . 'wincmd w | bd'
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
execute 'botright new ' . l:name
|
|
||||||
setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile
|
let l:has_lsp = get(g:, 'chopsticks_enable_lsp', 1)
|
||||||
call setline(1, [
|
let l:has_lint = get(g:, 'chopsticks_enable_lint', 1)
|
||||||
\ '=== chopsticks — Quick Reference ===',
|
let l:has_undotree = exists('g:plugs["undotree"]')
|
||||||
|
let l:has_previm = exists('g:plugs["previm"]')
|
||||||
|
|
||||||
|
let l:lines = [
|
||||||
|
\ ' chopsticks ,? close',
|
||||||
|
\ ' ─────────────────────────────',
|
||||||
\ '',
|
\ '',
|
||||||
\ 'SURVIVAL',
|
\ ' ── files ──────────────────',
|
||||||
\ ' Esc / jk Exit insert or visual mode',
|
\ ' Ctrl+p find file',
|
||||||
\ ' :q! + Enter Quit without saving',
|
\ ' ,b buffers',
|
||||||
\ ' ,x Save+quit ,w Save Ctrl+s Save (any mode)',
|
\ ' ,rg grep project',
|
||||||
\ ' :w!! Sudo save (when you forgot to open as root)',
|
\ ' ,rG grep word',
|
||||||
|
\ ' ,e sidebar (cwd)',
|
||||||
|
\ ' ,E sidebar (file dir)',
|
||||||
|
\ ' ,, last file',
|
||||||
|
\ ' ,fh recent files',
|
||||||
|
\ ' ,fl lines in buffer',
|
||||||
|
\ ' ,fc commands',
|
||||||
|
\ ' ,fm marks',
|
||||||
\ '',
|
\ '',
|
||||||
\ 'FILES & SEARCH',
|
\ ' ── code ──────────────────',
|
||||||
\ ' Ctrl+p Fuzzy find file (git-aware)',
|
\ ]
|
||||||
\ ' ,e / ,E File browser / vertical split',
|
|
||||||
\ ' ,b Search open buffers',
|
if l:has_lsp
|
||||||
\ ' ,rg Search project contents (ripgrep)',
|
call extend(l:lines, [
|
||||||
\ ' ,rG Ripgrep word under cursor',
|
\ ' gd definition',
|
||||||
\ ' ,fh Recent files history',
|
\ ' gy type definition',
|
||||||
\ ' ,fl / ,fL Search lines in buffer / all buffers',
|
\ ' gi implementation',
|
||||||
\ ' ,fc Commands | ,fm Marks',
|
\ ' gr references',
|
||||||
\ ' ,f/ / ,f: Search / command history',
|
\ ' K hover docs',
|
||||||
\ ' ,, Switch to last file (Ctrl+^)',
|
\ ' ,rn rename',
|
||||||
\ '',
|
\ ' ,ca code action',
|
||||||
\ 'CODE INTELLIGENCE (vim-lsp)',
|
\ ' ,f format',
|
||||||
\ ' gd Definition gy Type def gi Impl gr Refs',
|
\ ' ,o outline',
|
||||||
\ ' K Hover documentation',
|
\ ' [g ]g LSP diagnostics',
|
||||||
\ ' [g / ]g Prev / next LSP diagnostic',
|
\ ' :LspInstallServer setup LSP',
|
||||||
\ ' [e / ]e Prev / next ALE error',
|
|
||||||
\ ' ,ca Code action ,rn Rename ,f Format',
|
|
||||||
\ ' ,o File outline ,ws Workspace symbols',
|
|
||||||
\ ' ,cr Run current file',
|
|
||||||
\ '',
|
|
||||||
\ 'MARKDOWN & WRITING',
|
|
||||||
\ ' ,mp Live browser preview (previm)',
|
|
||||||
\ ' ,mt Table of contents',
|
|
||||||
\ ' ,zen Zen mode (Goyo + Limelight)',
|
|
||||||
\ ' zr / zm Unfold / fold all headings',
|
|
||||||
\ '',
|
|
||||||
\ 'EDITING',
|
|
||||||
\ ' gc Toggle comment (visual mode too)',
|
|
||||||
\ ' s + 2 chars EasyMotion jump anywhere',
|
|
||||||
\ ' ,u / F5 Undo tree',
|
|
||||||
\ ' ,y / ,Y Yank to system clipboard',
|
|
||||||
\ ' Alt+j / Alt+k Move line down / up',
|
|
||||||
\ ' ,F Re-indent file ,W Strip trailing whitespace',
|
|
||||||
\ ' ,* Search and replace word under cursor',
|
|
||||||
\ '',
|
|
||||||
\ 'GIT',
|
|
||||||
\ ' ,gs Status ,gd Diff ,gb Blame',
|
|
||||||
\ ' ,gc Commit ,gp Push ,gl Pull',
|
|
||||||
\ ' [x / ]x Navigate git conflict markers',
|
|
||||||
\ '',
|
|
||||||
\ 'WINDOWS & PANES',
|
|
||||||
\ ' Ctrl+h/j/k/l Navigate splits and tmux panes',
|
|
||||||
\ ' ,h / ,l Prev / next buffer ,bd Close buffer',
|
|
||||||
\ ' ,z Maximize / restore current window',
|
|
||||||
\ ' ,tv / ,th Terminal (vertical / horizontal)',
|
|
||||||
\ ' Esc Esc Exit terminal mode',
|
|
||||||
\ ' ,= / ,- Resize height ,+ / ,_ Resize width',
|
|
||||||
\ '',
|
|
||||||
\ 'QUICKFIX',
|
|
||||||
\ ' ,qo / ,qc Open / close quickfix',
|
|
||||||
\ ' ]q / [q Next / prev quickfix entry',
|
|
||||||
\ '',
|
|
||||||
\ 'UTILITIES',
|
|
||||||
\ ' ,ev / ,sv Edit / reload ~/.vimrc',
|
|
||||||
\ ' ,cp / ,cf Copy file path / filename to clipboard',
|
|
||||||
\ ' ,ms Scratch buffer ,cd CD to file dir',
|
|
||||||
\ ' ,ss Toggle spell ,so Source current vim file',
|
|
||||||
\ ' F2 Paste F3 Line# F4 Relative# F6 Invisible',
|
|
||||||
\ '',
|
|
||||||
\ '(press q to close)',
|
|
||||||
\ ])
|
\ ])
|
||||||
|
endif
|
||||||
|
|
||||||
|
call add(l:lines, ' ,cr run file')
|
||||||
|
if l:has_previm
|
||||||
|
call add(l:lines, ' ,mp markdown preview')
|
||||||
|
endif
|
||||||
|
call add(l:lines, ' ,mt table of contents')
|
||||||
|
|
||||||
|
if l:has_lint
|
||||||
|
call extend(l:lines, [
|
||||||
|
\ ' [e ]e ALE errors',
|
||||||
|
\ ' ,af format on save',
|
||||||
|
\ ])
|
||||||
|
endif
|
||||||
|
|
||||||
|
call extend(l:lines, [
|
||||||
|
\ '',
|
||||||
|
\ ' ── edit ──────────────────',
|
||||||
|
\ ' gc comment',
|
||||||
|
\ ' ,S+2ch easymotion jump',
|
||||||
|
\ ' cs"'' surround',
|
||||||
|
\ ])
|
||||||
|
|
||||||
|
if l:has_undotree
|
||||||
|
call add(l:lines, ' ,u undo tree')
|
||||||
|
endif
|
||||||
|
|
||||||
|
call extend(l:lines, [
|
||||||
|
\ ' ,y ,p clipboard y/p (v)',
|
||||||
|
\ ' Alt+j/k move line (v)',
|
||||||
|
\ ' ,* replace word (v)',
|
||||||
|
\ ' ,F re-indent (v)',
|
||||||
|
\ ' ,W strip trailing (v)',
|
||||||
|
\ '',
|
||||||
|
\ ' ── git ───────────────────',
|
||||||
|
\ ' ,gs status',
|
||||||
|
\ ' ,gd diff',
|
||||||
|
\ ' ,gb blame',
|
||||||
|
\ ' ,gc commit',
|
||||||
|
\ ' ,gp push',
|
||||||
|
\ ' ,gl pull',
|
||||||
|
\ ' ,gL log graph',
|
||||||
|
\ ' ,gC FZF commits',
|
||||||
|
\ ' [x ]x conflict markers',
|
||||||
|
\ '',
|
||||||
|
\ ' ── windows ───────────────',
|
||||||
|
\ ' Ctrl+hjkl navigate splits',
|
||||||
|
\ ' ,h ,l prev / next buf',
|
||||||
|
\ ' ,bd close buffer',
|
||||||
|
\ ' ,z maximize toggle',
|
||||||
|
\ ' ,= ,- resize height',
|
||||||
|
\ ' ,tv ,th terminal v / h',
|
||||||
|
\ ' ]q [q next / prev qf',
|
||||||
|
\ ' ,qo ,qc open / close qf',
|
||||||
|
\ '',
|
||||||
|
\ ' ── toggle ────────────────',
|
||||||
|
\ ' F2 paste mode',
|
||||||
|
\ ' F3 line numbers',
|
||||||
|
\ ' F4 relative numbers',
|
||||||
|
\ ' F6 invisible chars',
|
||||||
|
\ ' ,ss spell check',
|
||||||
|
\ '',
|
||||||
|
\ ' ── survival ──────────────',
|
||||||
|
\ ' ,w save',
|
||||||
|
\ ' ,q quit',
|
||||||
|
\ ' ,x save + quit',
|
||||||
|
\ ' Ctrl+s save (any mode)',
|
||||||
|
\ ' jk exit insert',
|
||||||
|
\ ' :w!! sudo save',
|
||||||
|
\ ' ,ev edit vimrc',
|
||||||
|
\ ' ,sv reload vimrc',
|
||||||
|
\ ' :ChopsticksStatus health',
|
||||||
|
\ ])
|
||||||
|
|
||||||
|
execute 'vertical botright new ' . l:name
|
||||||
|
vertical resize 42
|
||||||
|
setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile
|
||||||
|
setlocal nowrap nonumber norelativenumber signcolumn=no
|
||||||
|
setlocal winfixwidth
|
||||||
|
call setline(1, l:lines)
|
||||||
setlocal nomodifiable readonly
|
setlocal nomodifiable readonly
|
||||||
nnoremap <buffer> <silent> q :bd<CR>
|
nnoremap <buffer> <silent> q :bd<CR>
|
||||||
|
nnoremap <buffer> <silent> <leader>? :bd<CR>
|
||||||
endfunction
|
endfunction
|
||||||
nnoremap <silent> <leader>? :call <SID>CheatSheet()<CR>
|
nnoremap <silent> <leader>? :call <SID>CheatSheet()<CR>
|
||||||
|
|
||||||
" ── Interactive Tutorial ────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function! s:ChopsticksLearn() abort
|
|
||||||
let l:tutor = g:chopsticks_dir . '/tutor/chopsticks.tutor'
|
|
||||||
if !filereadable(l:tutor)
|
|
||||||
echo "Tutorial not found: " . l:tutor
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
execute 'edit ' . fnameescape(l:tutor)
|
|
||||||
setlocal nomodifiable readonly
|
|
||||||
setlocal buftype=nofile bufhidden=wipe
|
|
||||||
setlocal filetype=text
|
|
||||||
setlocal wrap linebreak
|
|
||||||
endfunction
|
|
||||||
command! ChopsticksLearn call s:ChopsticksLearn()
|
|
||||||
|
|
|
||||||
142
modules/ui.vim
142
modules/ui.vim
|
|
@ -1,4 +1,4 @@
|
||||||
" ui.vim — colorscheme, statusline, startify, indentline
|
" ui.vim — colorscheme, statusline, startify
|
||||||
|
|
||||||
" ── Colorscheme (Solarized Dark — matches tmux palette) ────────────────────
|
" ── Colorscheme (Solarized Dark — matches tmux palette) ────────────────────
|
||||||
|
|
||||||
|
|
@ -10,16 +10,49 @@ endif
|
||||||
|
|
||||||
set background=dark
|
set background=dark
|
||||||
|
|
||||||
|
function! s:WarnSolarized8Missing(...) abort
|
||||||
|
echohl WarningMsg
|
||||||
|
echom 'chopsticks: solarized8 not installed — run :PlugInstall'
|
||||||
|
echohl None
|
||||||
|
endfunction
|
||||||
|
|
||||||
if !g:is_tty
|
if !g:is_tty
|
||||||
try
|
try
|
||||||
colorscheme solarized8
|
colorscheme solarized8
|
||||||
catch
|
catch /^Vim\%((\a\+)\)\=:E185/
|
||||||
colorscheme default
|
colorscheme default
|
||||||
|
if has('timers')
|
||||||
|
call timer_start(500, function('s:WarnSolarized8Missing'))
|
||||||
|
else
|
||||||
|
augroup ChopstickColorschemeWarn
|
||||||
|
autocmd!
|
||||||
|
autocmd VimEnter * call s:WarnSolarized8Missing()
|
||||||
|
augroup END
|
||||||
|
endif
|
||||||
endtry
|
endtry
|
||||||
else
|
else
|
||||||
colorscheme default
|
colorscheme default
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
" ── Window separators, fillchars, cursorline visibility ────────────────────
|
||||||
|
|
||||||
|
if !g:is_tty
|
||||||
|
set fillchars+=vert:│,eob:\
|
||||||
|
endif
|
||||||
|
|
||||||
|
function! s:UIPolish() abort
|
||||||
|
hi VertSplit ctermbg=234 ctermfg=240 guibg=#002b36 guifg=#586e75 cterm=NONE gui=NONE
|
||||||
|
hi CursorLine ctermbg=235 guibg=#0c4452 cterm=NONE gui=NONE
|
||||||
|
hi CursorLineNr ctermbg=235 ctermfg=136 guibg=#0c4452 guifg=#b58900 cterm=bold gui=bold
|
||||||
|
hi SignColumn ctermbg=234 guibg=#002b36
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
augroup ChopstickUIPolish
|
||||||
|
autocmd!
|
||||||
|
autocmd ColorScheme * call s:UIPolish()
|
||||||
|
augroup END
|
||||||
|
if !g:is_tty | call s:UIPolish() | endif
|
||||||
|
|
||||||
if has("gui_running")
|
if has("gui_running")
|
||||||
if has("gui_gtk2") || has("gui_gtk3")
|
if has("gui_gtk2") || has("gui_gtk3")
|
||||||
set guifont=Hack\ 12,Source\ Code\ Pro\ 12,Monospace\ 12
|
set guifont=Hack\ 12,Source\ Code\ Pro\ 12,Monospace\ 12
|
||||||
|
|
@ -28,49 +61,16 @@ if has("gui_running")
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
" ── IndentLine (non-TTY only) ───────────────────────────────────────────────
|
|
||||||
|
|
||||||
if !g:is_tty && exists('g:plugs["indentLine"]')
|
|
||||||
let g:indentLine_char = '|'
|
|
||||||
let g:indentLine_first_char = '|'
|
|
||||||
let g:indentLine_showFirstIndentLevel = 1
|
|
||||||
let g:indentLine_fileTypeExclude = ['text', 'help', 'startify', 'markdown']
|
|
||||||
let g:indentLine_bufTypeExclude = ['help', 'terminal', 'nofile']
|
|
||||||
let g:indentLine_setConceal = 2
|
|
||||||
let g:indentLine_concealcursor = ''
|
|
||||||
endif
|
|
||||||
|
|
||||||
" ── Startify ────────────────────────────────────────────────────────────────
|
" ── Startify ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
if exists('g:plugs["vim-startify"]')
|
if exists('g:plugs["vim-startify"]')
|
||||||
let g:startify_custom_header = [
|
|
||||||
\ ' ██████╗██╗ ██╗ ██████╗ ██████╗ ███████╗████████╗██╗ ██████╗██╗ ██╗███████╗',
|
|
||||||
\ ' ██╔════╝██║ ██║██╔═══██╗██╔══██╗██╔════╝╚══██╔══╝██║██╔════╝██║ ██╔╝██╔════╝',
|
|
||||||
\ ' ██║ ███████║██║ ██║██████╔╝███████╗ ██║ ██║██║ █████╔╝ ███████╗',
|
|
||||||
\ ' ██║ ██╔══██║██║ ██║██╔═══╝ ╚════██║ ██║ ██║██║ ██╔═██╗ ╚════██║',
|
|
||||||
\ ' ╚██████╗██║ ██║╚██████╔╝██║ ███████║ ██║ ██║╚██████╗██║ ██╗███████║',
|
|
||||||
\ ' ╚═════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚══════╝',
|
|
||||||
\ '',
|
|
||||||
\ ]
|
|
||||||
|
|
||||||
let g:startify_lists = [
|
let g:startify_lists = [
|
||||||
\ { 'type': 'sessions', 'header': [' Sessions'] },
|
\ { 'type': 'sessions', 'header': [' Sessions'] },
|
||||||
\ { 'type': 'files', 'header': [' Recent Files'] },
|
\ { 'type': 'files', 'header': [' Recent Files'] },
|
||||||
\ { 'type': 'dir', 'header': [' Current Dir'] },
|
\ { 'type': 'dir', 'header': [' Current Dir'] },
|
||||||
\ { 'type': 'bookmarks', 'header': [' Bookmarks'] },
|
|
||||||
\ ]
|
\ ]
|
||||||
|
|
||||||
let g:startify_bookmarks = [{'v': '~/.vimrc'}]
|
let g:startify_bookmarks = [{'v': '~/.vimrc'}]
|
||||||
if filereadable(expand('~/.zshrc'))
|
|
||||||
call add(g:startify_bookmarks, {'z': '~/.zshrc'})
|
|
||||||
endif
|
|
||||||
if filereadable(expand('~/.bashrc'))
|
|
||||||
call add(g:startify_bookmarks, {'b': '~/.bashrc'})
|
|
||||||
endif
|
|
||||||
if filereadable(expand('~/.config/fish/config.fish'))
|
|
||||||
call add(g:startify_bookmarks, {'f': '~/.config/fish/config.fish'})
|
|
||||||
endif
|
|
||||||
|
|
||||||
let g:startify_session_persistence = 1
|
let g:startify_session_persistence = 1
|
||||||
let g:startify_session_autoload = 1
|
let g:startify_session_autoload = 1
|
||||||
let g:startify_change_to_vcs_root = 1
|
let g:startify_change_to_vcs_root = 1
|
||||||
|
|
@ -79,15 +79,29 @@ if exists('g:plugs["vim-startify"]')
|
||||||
let g:startify_files_number = 8
|
let g:startify_files_number = 8
|
||||||
let g:startify_padding_left = 4
|
let g:startify_padding_left = 4
|
||||||
|
|
||||||
|
function! s:SetupDirView() abort
|
||||||
|
if argc() != 1 || !isdirectory(argv()[0]) || exists('s:std_in')
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
let l:dir = fnameescape(argv()[0])
|
||||||
|
execute 'cd ' . l:dir
|
||||||
|
vertical rightbelow vnew
|
||||||
|
if exists(':Startify') == 2
|
||||||
|
Startify
|
||||||
|
else
|
||||||
|
enew
|
||||||
|
endif
|
||||||
|
wincmd h
|
||||||
|
vertical resize 30
|
||||||
|
setlocal winfixwidth
|
||||||
|
wincmd l
|
||||||
|
endfunction
|
||||||
|
|
||||||
if !g:is_tty
|
if !g:is_tty
|
||||||
augroup ChopstickStartup
|
augroup ChopstickStartup
|
||||||
autocmd!
|
autocmd!
|
||||||
autocmd StdinReadPre * let s:std_in = 1
|
autocmd StdinReadPre * let s:std_in = 1
|
||||||
autocmd VimEnter *
|
autocmd VimEnter * nested call <SID>SetupDirView()
|
||||||
\ if argc() == 1 && isdirectory(argv()[0]) && !exists('s:std_in') |
|
|
||||||
\ exe 'cd ' . fnameescape(argv()[0]) |
|
|
||||||
\ if exists(':Startify') == 2 | Startify | else | enew | endif |
|
|
||||||
\ endif
|
|
||||||
augroup END
|
augroup END
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
@ -144,11 +158,19 @@ function! SLAle() abort
|
||||||
return printf(' E:%d W:%d ', l:e, l:w)
|
return printf(' E:%d W:%d ', l:e, l:w)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
function! SLFlags() abort
|
||||||
|
let l:f = ''
|
||||||
|
if &paste | let l:f .= ' PASTE' | endif
|
||||||
|
if &spell | let l:f .= ' SPELL' | endif
|
||||||
|
return empty(l:f) ? '' : l:f . ' '
|
||||||
|
endfunction
|
||||||
|
|
||||||
function! SLBuild() abort
|
function! SLBuild() abort
|
||||||
let [l:label, l:hl] = SLMode()
|
let [l:label, l:hl] = SLMode()
|
||||||
let l:s = '%#' . l:hl . '#' . l:label
|
let l:s = '%#' . l:hl . '#' . l:label
|
||||||
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 .= '%#SLFlag#' . SLFlags()
|
||||||
let l:s .= '%#SLBody#%='
|
let l:s .= '%#SLBody#%='
|
||||||
let l:s .= '%#SLFlag#' . SLAle()
|
let l:s .= '%#SLFlag#' . SLAle()
|
||||||
let l:s .= '%#SLGit#' . SLGit()
|
let l:s .= '%#SLGit#' . SLGit()
|
||||||
|
|
@ -162,3 +184,43 @@ set statusline=%!SLBuild()
|
||||||
if g:is_tty
|
if g:is_tty
|
||||||
set statusline=%f\ %h%w%m%r\ %=%(%l,%c%V\ %=\ %P%)
|
set statusline=%f\ %h%w%m%r\ %=%(%l,%c%V\ %=\ %P%)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
" ── Tabline (native — shows listed buffers when >1, else hidden) ───────────
|
||||||
|
|
||||||
|
function! s:TLDefineColors() abort
|
||||||
|
hi TabLine ctermbg=234 ctermfg=244 cterm=none guibg=#002b36 guifg=#839496 gui=none
|
||||||
|
hi TabLineSel ctermbg=235 ctermfg=136 cterm=bold guibg=#073642 guifg=#b58900 gui=bold
|
||||||
|
hi TabLineFill ctermbg=234 ctermfg=240 cterm=none guibg=#002b36 guifg=#586e75 gui=none
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
augroup TLColors
|
||||||
|
autocmd!
|
||||||
|
autocmd ColorScheme * call s:TLDefineColors()
|
||||||
|
augroup END
|
||||||
|
call s:TLDefineColors()
|
||||||
|
|
||||||
|
function! TLBuild() abort
|
||||||
|
let l:s = ''
|
||||||
|
let l:cur = bufnr('%')
|
||||||
|
for l:b in range(1, bufnr('$'))
|
||||||
|
if !buflisted(l:b) || getbufvar(l:b, '&buftype') !=# '' | continue | endif
|
||||||
|
let l:hl = (l:b == l:cur) ? '%#TabLineSel#' : '%#TabLine#'
|
||||||
|
let l:name = bufname(l:b)
|
||||||
|
let l:name = empty(l:name) ? '[No Name]' : fnamemodify(l:name, ':t')
|
||||||
|
let l:mod = getbufvar(l:b, '&modified') ? ' +' : ''
|
||||||
|
let l:s .= l:hl . ' ' . l:b . ' ' . l:name . l:mod . ' '
|
||||||
|
endfor
|
||||||
|
let l:s .= '%#TabLineFill#%='
|
||||||
|
return l:s
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
if !g:is_tty
|
||||||
|
set showtabline=1
|
||||||
|
set tabline=%!TLBuild()
|
||||||
|
endif
|
||||||
|
|
||||||
|
" ── SignColumn: always reserve a column so width never jitters ─────────────
|
||||||
|
|
||||||
|
if !g:is_tty
|
||||||
|
set signcolumn=yes
|
||||||
|
endif
|
||||||
|
|
|
||||||
|
|
@ -1,56 +0,0 @@
|
||||||
" writing.vim — vim-markdown, previm, goyo + limelight zen mode
|
|
||||||
|
|
||||||
" ── vim-markdown ────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
let g:vim_markdown_conceal = 1
|
|
||||||
let g:vim_markdown_conceal_code_blocks = 0
|
|
||||||
let g:vim_markdown_folding_disabled = 0
|
|
||||||
let g:vim_markdown_folding_level = 2
|
|
||||||
let g:vim_markdown_frontmatter = 1
|
|
||||||
let g:vim_markdown_toml_frontmatter = 1
|
|
||||||
let g:vim_markdown_json_frontmatter = 1
|
|
||||||
let g:vim_markdown_follow_anchor = 1
|
|
||||||
let g:vim_markdown_new_list_item_indent = 2
|
|
||||||
let g:vim_markdown_strikethrough = 1
|
|
||||||
|
|
||||||
if exists('g:plugs["vim-markdown"]')
|
|
||||||
nnoremap <leader>mt :Toc<CR>
|
|
||||||
endif
|
|
||||||
|
|
||||||
" ── previm (Markdown browser preview) ───────────────────────────────────────
|
|
||||||
|
|
||||||
if has('macunix')
|
|
||||||
let g:previm_open_cmd = '/usr/bin/open'
|
|
||||||
elseif executable('xdg-open')
|
|
||||||
let g:previm_open_cmd = 'xdg-open'
|
|
||||||
endif
|
|
||||||
if exists('g:plugs["previm"]')
|
|
||||||
nnoremap <leader>mp :PrevimOpen<CR>
|
|
||||||
endif
|
|
||||||
let g:previm_enable_realtime = 1
|
|
||||||
|
|
||||||
" ── Goyo + Limelight (zen mode) ────────────────────────────────────────────
|
|
||||||
|
|
||||||
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'
|
|
||||||
251
scripts/test.sh
Executable file
251
scripts/test.sh
Executable file
|
|
@ -0,0 +1,251 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Project test runner. CI calls the same groups that maintainers can run locally.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
TMP_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/chopsticks-test-XXXXXX")"
|
||||||
|
EMPTY_XDG="$TMP_ROOT/xdg-empty"
|
||||||
|
STARTUP_LIMIT_MS="${STARTUP_LIMIT_MS:-150}"
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
rm -rf "$TMP_ROOT"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
cd "$ROOT"
|
||||||
|
mkdir -p "$EMPTY_XDG"
|
||||||
|
|
||||||
|
step() {
|
||||||
|
printf '\n==> %s\n' "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
need() {
|
||||||
|
command -v "$1" >/dev/null 2>&1 || {
|
||||||
|
echo "Missing required command: $1" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'EOF'
|
||||||
|
Usage: scripts/test.sh [group...]
|
||||||
|
|
||||||
|
Groups:
|
||||||
|
quick shell, docs, installer, and bootstrap checks
|
||||||
|
shell shell syntax, executability, and shellcheck
|
||||||
|
docs markdownlint for project docs
|
||||||
|
installer install.sh dry-run/configure-only profile checks
|
||||||
|
bootstrap get.sh dry-run safety checks
|
||||||
|
vim Vim smoke tests; requires plugins in ~/.vim/plugged
|
||||||
|
all quick plus vim
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-h, --help show this help
|
||||||
|
list print group names, one per line
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
list_groups() {
|
||||||
|
printf '%s\n' quick shell docs installer bootstrap vim all
|
||||||
|
}
|
||||||
|
|
||||||
|
check_shell() {
|
||||||
|
step "Shell syntax and lint"
|
||||||
|
need bash
|
||||||
|
bash -n install.sh
|
||||||
|
bash -n get.sh
|
||||||
|
bash -n scripts/test.sh
|
||||||
|
test -x install.sh
|
||||||
|
test -x get.sh
|
||||||
|
test -x scripts/test.sh
|
||||||
|
|
||||||
|
need shellcheck
|
||||||
|
shellcheck install.sh get.sh scripts/test.sh
|
||||||
|
}
|
||||||
|
|
||||||
|
check_docs() {
|
||||||
|
step "Markdown lint"
|
||||||
|
need markdownlint
|
||||||
|
markdownlint README.md QUICKSTART.md CONTRIBUTING.md CHANGELOG.md
|
||||||
|
}
|
||||||
|
|
||||||
|
check_installer_modes() {
|
||||||
|
step "Installer profile-only modes"
|
||||||
|
XDG_CONFIG_HOME="$TMP_ROOT/dry" ./install.sh --dry-run --profile=full \
|
||||||
|
| tee "$TMP_ROOT/install-dry-run.txt"
|
||||||
|
grep -q 'Profile: full' "$TMP_ROOT/install-dry-run.txt"
|
||||||
|
test ! -e "$TMP_ROOT/dry/chopsticks.vim"
|
||||||
|
|
||||||
|
XDG_CONFIG_HOME="$TMP_ROOT/config" ./install.sh --configure-only --profile=minimal
|
||||||
|
grep -q "let g:chopsticks_profile = 'minimal'" "$TMP_ROOT/config/chopsticks.vim"
|
||||||
|
|
||||||
|
XDG_CONFIG_HOME="$TMP_ROOT/config" ./install.sh --configure-only --profile=full
|
||||||
|
grep -q "let g:chopsticks_profile = 'full'" "$TMP_ROOT/config/chopsticks.vim"
|
||||||
|
|
||||||
|
XDG_CONFIG_HOME="$TMP_ROOT/default" ./install.sh --configure-only --yes
|
||||||
|
grep -q "let g:chopsticks_profile = 'engineer'" "$TMP_ROOT/default/chopsticks.vim"
|
||||||
|
}
|
||||||
|
|
||||||
|
check_bootstrap() {
|
||||||
|
step "Bootstrap dry-run safety"
|
||||||
|
CHOPSTICKS_DEST="$TMP_ROOT/bootstrap" ./get.sh --dry-run --profile=minimal \
|
||||||
|
| tee "$TMP_ROOT/get-dry-run.txt"
|
||||||
|
grep -q 'Would clone' "$TMP_ROOT/get-dry-run.txt"
|
||||||
|
test ! -e "$TMP_ROOT/bootstrap"
|
||||||
|
|
||||||
|
mkdir -p "$TMP_ROOT/not-chopsticks"
|
||||||
|
git -c init.defaultBranch=main init "$TMP_ROOT/not-chopsticks" >/dev/null
|
||||||
|
git -C "$TMP_ROOT/not-chopsticks" remote add origin https://github.com/example/not-chopsticks.git
|
||||||
|
if CHOPSTICKS_DEST="$TMP_ROOT/not-chopsticks" ./get.sh --dry-run; then
|
||||||
|
echo "Expected get.sh to reject non-chopsticks repo" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$TMP_ROOT/chopsticks-existing"
|
||||||
|
git -c init.defaultBranch=main init "$TMP_ROOT/chopsticks-existing" >/dev/null
|
||||||
|
git -C "$TMP_ROOT/chopsticks-existing" remote add origin https://github.com/m1ngsama/chopsticks.git
|
||||||
|
touch "$TMP_ROOT/chopsticks-existing/install.sh" "$TMP_ROOT/chopsticks-existing/.vimrc"
|
||||||
|
CHOPSTICKS_DEST="$TMP_ROOT/chopsticks-existing" ./get.sh --dry-run --yes \
|
||||||
|
| tee "$TMP_ROOT/get-existing.txt"
|
||||||
|
grep -q 'Would update existing chopsticks repo' "$TMP_ROOT/get-existing.txt"
|
||||||
|
}
|
||||||
|
|
||||||
|
check_plugin_dirs() {
|
||||||
|
step "Plugin directories"
|
||||||
|
for plugin in \
|
||||||
|
fzf fzf.vim vim-fugitive vim-gitgutter ale vim-lsp vim-lsp-settings \
|
||||||
|
asyncomplete.vim asyncomplete-lsp.vim vim-markdown
|
||||||
|
do
|
||||||
|
test -d "$HOME/.vim/plugged/$plugin" || {
|
||||||
|
echo "Missing plugin directory: $plugin" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
check_vim() {
|
||||||
|
step "Vim smoke tests"
|
||||||
|
need vim
|
||||||
|
check_plugin_dirs
|
||||||
|
|
||||||
|
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N -c 'qa!' 2>&1
|
||||||
|
|
||||||
|
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
|
||||||
|
-c "redir! > $TMP_ROOT/plugs.txt" \
|
||||||
|
-c 'silent echo len(g:plugs)' \
|
||||||
|
-c 'redir END' \
|
||||||
|
-c 'qa!' 2>/dev/null
|
||||||
|
PLUGS="$(tr -d '[:space:]' < "$TMP_ROOT/plugs.txt")"
|
||||||
|
echo "Plugins registered: $PLUGS"
|
||||||
|
if [ "$PLUGS" -lt 20 ]; then
|
||||||
|
echo "Expected 20+ plugins, got $PLUGS" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$TMP_ROOT/chopsticks path/modules"
|
||||||
|
cp .vimrc "$TMP_ROOT/chopsticks path/.vimrc"
|
||||||
|
cp modules/*.vim "$TMP_ROOT/chopsticks path/modules/"
|
||||||
|
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u "$TMP_ROOT/chopsticks path/.vimrc" \
|
||||||
|
-i NONE -es -N -c 'qa!' 2>&1
|
||||||
|
|
||||||
|
vim -u NONE -i NONE -es -N \
|
||||||
|
-c 'let g:chopsticks_profile = "minimal"' \
|
||||||
|
-c 'source .vimrc' \
|
||||||
|
-c 'if has_key(g:plugs, "ale") || has_key(g:plugs, "vim-lsp") || has_key(g:plugs, "vim-lsp-settings") || has_key(g:plugs, "asyncomplete.vim") | cquit | endif' \
|
||||||
|
-c 'qa!' 2>&1
|
||||||
|
|
||||||
|
mkdir -p "$TMP_ROOT/local"
|
||||||
|
printf "%s\n" "let g:chopsticks_profile = 'minimal'" > "$TMP_ROOT/local/config.vim"
|
||||||
|
vim -u NONE -i NONE -es -N \
|
||||||
|
-c "let g:chopsticks_local_config = '$TMP_ROOT/local/config.vim'" \
|
||||||
|
-c 'source .vimrc' \
|
||||||
|
-c 'if g:chopsticks_profile !=# "minimal" || has_key(g:plugs, "ale") || has_key(g:plugs, "vim-lsp") | cquit | endif' \
|
||||||
|
-c 'qa!' 2>&1
|
||||||
|
|
||||||
|
mkdir -p "$TMP_ROOT/xdg"
|
||||||
|
printf "%s\n" "let g:chopsticks_profile = 'minimal'" > "$TMP_ROOT/xdg/chopsticks.vim"
|
||||||
|
XDG_CONFIG_HOME="$TMP_ROOT/xdg" vim -u NONE -i NONE -es -N \
|
||||||
|
-c 'source .vimrc' \
|
||||||
|
-c 'if g:chopsticks_profile !=# "minimal" || has_key(g:plugs, "ale") || has_key(g:plugs, "vim-lsp") | cquit | endif' \
|
||||||
|
-c 'qa!' 2>&1
|
||||||
|
|
||||||
|
vim -u NONE -i NONE -es -N \
|
||||||
|
-c 'let g:chopsticks_profile = "minimal"' \
|
||||||
|
-c 'source .vimrc' \
|
||||||
|
-c 'normal ,?' \
|
||||||
|
-c "redir! > $TMP_ROOT/cheat.txt" \
|
||||||
|
-c 'silent %print' \
|
||||||
|
-c 'redir END' \
|
||||||
|
-c 'qa!' 2>&1
|
||||||
|
if grep -Eq 'definition|LspInstallServer|ALE errors|undo tree|markdown preview' "$TMP_ROOT/cheat.txt"; then
|
||||||
|
cat "$TMP_ROOT/cheat.txt"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
grep -q ',cr run file' "$TMP_ROOT/cheat.txt"
|
||||||
|
|
||||||
|
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N README.md \
|
||||||
|
-c 'set filetype=markdown' \
|
||||||
|
-c 'if &l:spell || &l:conceallevel != 0 || &l:signcolumn !=# "no" || exists("g:lsp_settings_filetype_markdown") | cquit | endif' \
|
||||||
|
-c 'qa!' 2>&1
|
||||||
|
|
||||||
|
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N \
|
||||||
|
-c 'if maparg("s", "n") !=# "" | cquit | endif' \
|
||||||
|
-c 'if maparg(",w", "n") =~# "!" | cquit | endif' \
|
||||||
|
-c 'if !&swapfile || !&writebackup || &directory !~# "\.vim/.swap" | cquit | endif' \
|
||||||
|
-c 'qa!' 2>&1
|
||||||
|
|
||||||
|
vim -u NONE -i NONE -es -N \
|
||||||
|
-c 'let g:ale_fix_on_save = 0' \
|
||||||
|
-c 'source .vimrc' \
|
||||||
|
-c 'if g:ale_fix_on_save != 0 | cquit | endif' \
|
||||||
|
-c 'qa!' 2>&1
|
||||||
|
|
||||||
|
truncate -s 11000000 "$TMP_ROOT/large.py"
|
||||||
|
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE -es -N "$TMP_ROOT/large.py" \
|
||||||
|
-c 'set filetype=python' \
|
||||||
|
-c 'if &l:syntax !=# "" || &l:undolevels != -1 || &l:swapfile || get(b:, "ale_enabled", 1) != 0 | cquit | endif' \
|
||||||
|
-c 'qa!' 2>&1
|
||||||
|
|
||||||
|
XDG_CONFIG_HOME="$EMPTY_XDG" vim -u .vimrc -i NONE --startuptime "$TMP_ROOT/startup.log" \
|
||||||
|
-es -N -c 'qa!' 2>/dev/null
|
||||||
|
tail -1 "$TMP_ROOT/startup.log"
|
||||||
|
STARTUP_MS="$(awk 'END { print $1 }' "$TMP_ROOT/startup.log")"
|
||||||
|
awk -v ms="$STARTUP_MS" -v limit="$STARTUP_LIMIT_MS" \
|
||||||
|
'BEGIN { if (ms > limit) exit 1 }'
|
||||||
|
}
|
||||||
|
|
||||||
|
run_group() {
|
||||||
|
case "$1" in
|
||||||
|
quick)
|
||||||
|
check_shell
|
||||||
|
check_docs
|
||||||
|
check_installer_modes
|
||||||
|
check_bootstrap
|
||||||
|
;;
|
||||||
|
shell) check_shell ;;
|
||||||
|
docs) check_docs ;;
|
||||||
|
installer) check_installer_modes ;;
|
||||||
|
bootstrap) check_bootstrap ;;
|
||||||
|
vim) check_vim ;;
|
||||||
|
all)
|
||||||
|
run_group quick
|
||||||
|
check_vim
|
||||||
|
;;
|
||||||
|
list | --list) list_groups ;;
|
||||||
|
-h | --help) usage ;;
|
||||||
|
*)
|
||||||
|
echo "Unknown test group: $1" >&2
|
||||||
|
echo >&2
|
||||||
|
usage >&2
|
||||||
|
exit 1 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ $# -eq 0 ]]; then
|
||||||
|
set -- all
|
||||||
|
fi
|
||||||
|
|
||||||
|
for group in "$@"; do
|
||||||
|
run_group "$group"
|
||||||
|
done
|
||||||
|
|
@ -1,269 +0,0 @@
|
||||||
================================================================================
|
|
||||||
= C H O P S T I C K S I N T E R A C T I V E T U T O R =
|
|
||||||
================================================================================
|
|
||||||
|
|
||||||
This tutorial teaches the key features of the chopsticks Vim configuration.
|
|
||||||
It assumes you already know basic Vim (if not, run :Tutor first).
|
|
||||||
|
|
||||||
Leader key is , (comma) throughout this tutorial.
|
|
||||||
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Lesson 1: SURVIVAL BASICS
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
These four commands get you out of any situation:
|
|
||||||
|
|
||||||
1. Press Esc or type jk to return to Normal mode
|
|
||||||
2. Type ,w to save the current file
|
|
||||||
3. Type ,x to save and quit
|
|
||||||
4. Type ,q to quit (without saving)
|
|
||||||
|
|
||||||
TIP: You can also press Ctrl+s to save from ANY mode (normal, insert,
|
|
||||||
or visual). Try it now — press i to enter insert mode, then Ctrl+s
|
|
||||||
to save without leaving insert mode.
|
|
||||||
|
|
||||||
When completely lost, press ,? to open the cheat sheet with every
|
|
||||||
keybinding. Press q to close it.
|
|
||||||
|
|
||||||
>>> Try it now: press ,? then read through the sheet, then press q
|
|
||||||
|
|
||||||
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Lesson 2: FINDING FILES
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
chopsticks uses FZF (fuzzy finder) for fast navigation.
|
|
||||||
|
|
||||||
Ctrl+p Fuzzy find files (git-aware — ignores .gitignore'd files)
|
|
||||||
,b Search open buffers
|
|
||||||
,fh Recent file history
|
|
||||||
,rg Search file contents with ripgrep
|
|
||||||
,rG Search for the word under your cursor
|
|
||||||
|
|
||||||
>>> Try it now: press Ctrl+p and start typing a filename
|
|
||||||
|
|
||||||
You can also browse files with the built-in file browser:
|
|
||||||
|
|
||||||
,e Open netrw file browser in current window
|
|
||||||
,E Open netrw in a vertical split
|
|
||||||
|
|
||||||
Inside netrw:
|
|
||||||
Enter Open file
|
|
||||||
- Go up one directory
|
|
||||||
% Create new file
|
|
||||||
d Create new directory
|
|
||||||
gh Toggle hidden files
|
|
||||||
|
|
||||||
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Lesson 3: CODE INTELLIGENCE
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
chopsticks uses vim-lsp for code intelligence — no Node.js required.
|
|
||||||
|
|
||||||
First, install a language server for your filetype:
|
|
||||||
|
|
||||||
:LspInstallServer (auto-detects the language)
|
|
||||||
:LspStatus (check if it's running)
|
|
||||||
|
|
||||||
Once a server is running, these keys become available:
|
|
||||||
|
|
||||||
gd Go to definition
|
|
||||||
gy Go to type definition
|
|
||||||
gi Go to implementation
|
|
||||||
gr List all references
|
|
||||||
K Show hover documentation
|
|
||||||
|
|
||||||
[g / ]g Jump to previous / next diagnostic
|
|
||||||
,rn Rename symbol under cursor
|
|
||||||
,ca Code action (auto-fix)
|
|
||||||
,f Format buffer (or selection in visual mode)
|
|
||||||
,o File outline (symbols)
|
|
||||||
|
|
||||||
>>> Open a source file and try: gd on a function call, then Ctrl+o
|
|
||||||
to jump back
|
|
||||||
|
|
||||||
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Lesson 4: EDITING POWER
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
These editing features make chopsticks faster than vanilla Vim.
|
|
||||||
|
|
||||||
EASYMOTION — jump anywhere on screen:
|
|
||||||
Press s then type two characters from your target location.
|
|
||||||
Matching positions light up — press the highlighted letter to jump.
|
|
||||||
|
|
||||||
>>> Try it now: press s then type two letters you can see on screen
|
|
||||||
|
|
||||||
MOVE LINES — rearrange code without cut/paste:
|
|
||||||
Alt+j Move current line down
|
|
||||||
Alt+k Move current line up
|
|
||||||
(Works in visual mode too — select lines first with V )
|
|
||||||
|
|
||||||
SURROUND — change surrounding characters:
|
|
||||||
cs"' Change surrounding " to '
|
|
||||||
cs'<div> Change surrounding ' to <div>...</div>
|
|
||||||
ds( Delete surrounding parentheses
|
|
||||||
ysiw" Surround word with "
|
|
||||||
|
|
||||||
COMMENT — toggle comments:
|
|
||||||
gc Toggle comment (works in visual mode on selections)
|
|
||||||
gcc Toggle comment on current line
|
|
||||||
|
|
||||||
VISUAL FEEDBACK:
|
|
||||||
- Yanked text flashes briefly so you know what was copied
|
|
||||||
- Search highlights auto-clear after you stop moving
|
|
||||||
|
|
||||||
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Lesson 5: GIT WORKFLOW
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
All git operations through vim-fugitive:
|
|
||||||
|
|
||||||
,gs Git status (press s to stage, press cc to commit)
|
|
||||||
,gd Diff current file (side-by-side split)
|
|
||||||
,gb Git blame (who changed each line)
|
|
||||||
,gc Git commit
|
|
||||||
,gp Git push
|
|
||||||
,gl Git pull
|
|
||||||
|
|
||||||
GitGutter shows changes in the sign column (left margin):
|
|
||||||
+ Added line
|
|
||||||
~ Modified line
|
|
||||||
- Deleted line
|
|
||||||
|
|
||||||
Navigate git conflict markers:
|
|
||||||
]x Jump to next conflict marker
|
|
||||||
[x Jump to previous conflict marker
|
|
||||||
|
|
||||||
>>> Try ,gs in a git repository to see the status view
|
|
||||||
|
|
||||||
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Lesson 6: MARKDOWN & WRITING
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Markdown files get special treatment automatically:
|
|
||||||
- Word wrap enabled
|
|
||||||
- Spell checking enabled
|
|
||||||
- Syntax concealment (bold renders as bold, headings hide # markers)
|
|
||||||
- Folding by heading level
|
|
||||||
|
|
||||||
Key bindings for markdown:
|
|
||||||
|
|
||||||
,mp Open live browser preview (auto-refreshes as you type)
|
|
||||||
,mt Table of contents in a side window
|
|
||||||
,zen Enter zen mode — Goyo + Limelight
|
|
||||||
(distraction-free writing, only current paragraph highlighted)
|
|
||||||
zr Unfold all headings
|
|
||||||
zm Fold all headings
|
|
||||||
|
|
||||||
>>> Open a .md file and try ,zen to enter zen mode.
|
|
||||||
Press ,zen again to leave.
|
|
||||||
|
|
||||||
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Lesson 7: WINDOW MANAGEMENT
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Navigate between splits (works with tmux panes too):
|
|
||||||
|
|
||||||
Ctrl+h Move to left split
|
|
||||||
Ctrl+j Move to split below
|
|
||||||
Ctrl+k Move to split above
|
|
||||||
Ctrl+l Move to right split
|
|
||||||
|
|
||||||
Buffer navigation:
|
|
||||||
|
|
||||||
,h Previous buffer
|
|
||||||
,l Next buffer
|
|
||||||
,bd Close buffer (keeps window layout)
|
|
||||||
,, Switch to last file (toggle between two files)
|
|
||||||
|
|
||||||
Window tricks:
|
|
||||||
|
|
||||||
,z Maximize current window (press again to restore)
|
|
||||||
,= / ,- Resize window height (bigger / smaller)
|
|
||||||
,+ / ,_ Resize window width (bigger / smaller)
|
|
||||||
|
|
||||||
Terminal:
|
|
||||||
|
|
||||||
,tv Open terminal (vertical split)
|
|
||||||
,th Open terminal (horizontal, 10 rows)
|
|
||||||
Esc Esc Exit terminal mode back to normal
|
|
||||||
|
|
||||||
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Lesson 8: LINTING & FORMATTING
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
ALE runs linters and formatters automatically:
|
|
||||||
|
|
||||||
- Linting happens as you type (normal mode) and on save
|
|
||||||
- Format-on-save is enabled by default (black, prettier, gofmt, etc.)
|
|
||||||
|
|
||||||
Navigate errors:
|
|
||||||
|
|
||||||
[e / ]e Previous / next ALE error
|
|
||||||
,aD Show error detail in a popup
|
|
||||||
|
|
||||||
Supported out of the box:
|
|
||||||
Python: flake8 + pylint (lint), black + isort (format)
|
|
||||||
JS/TS: eslint (lint), prettier + eslint (format)
|
|
||||||
Go: staticcheck (lint), goimports (format)
|
|
||||||
Rust: cargo (lint), rustfmt (format)
|
|
||||||
Shell: shellcheck (lint)
|
|
||||||
And more: yaml, dockerfile, css, markdown, sql
|
|
||||||
|
|
||||||
>>> Write some intentionally bad code and watch the sign column
|
|
||||||
light up with X (error) and ! (warning) markers
|
|
||||||
|
|
||||||
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Lesson 9: RUNNING CODE
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Press ,cr to run the current file. It auto-detects the language:
|
|
||||||
|
|
||||||
Python → python3 file.py
|
|
||||||
JavaScript → node file.js
|
|
||||||
TypeScript → npx ts-node file.ts
|
|
||||||
Go → go run file.go
|
|
||||||
Rust → cargo run
|
|
||||||
Shell → bash file.sh
|
|
||||||
C → gcc + run
|
|
||||||
Lua, Ruby, Perl also supported
|
|
||||||
|
|
||||||
>>> Create a test file (e.g. test.py with: print("hello"))
|
|
||||||
and press ,cr to run it
|
|
||||||
|
|
||||||
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Lesson 10: DAILY TIPS
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
SEARCH AND REPLACE:
|
|
||||||
,* Replace word under cursor throughout the file
|
|
||||||
// In visual mode, search for selected text
|
|
||||||
|
|
||||||
USEFUL SHORTCUTS:
|
|
||||||
Y Yank to end of line (consistent with D and C)
|
|
||||||
,y / ,Y Yank to system clipboard
|
|
||||||
,F Re-indent entire file
|
|
||||||
,W Strip all trailing whitespace
|
|
||||||
:w!! Sudo save (when you forgot to open as root)
|
|
||||||
,ev Edit your vimrc
|
|
||||||
,sv Reload your vimrc
|
|
||||||
|
|
||||||
SESSIONS:
|
|
||||||
:Obsess Start recording session (auto-restores next time)
|
|
||||||
:Obsess! Stop recording
|
|
||||||
|
|
||||||
The startup screen (Startify) shows recent files, sessions, and bookmarks.
|
|
||||||
|
|
||||||
>>> Press ,? one more time to review the full cheat sheet.
|
|
||||||
You now know the essential chopsticks features. Happy editing!
|
|
||||||
|
|
||||||
================================================================================
|
|
||||||
Loading…
Reference in a new issue