diff --git a/.vimrc b/.vimrc index d3b0f2c..d3e001f 100644 --- a/.vimrc +++ b/.vimrc @@ -160,6 +160,9 @@ call plug#end() " ============================================================================ if g:has_true_color && has('termguicolors') && !g:is_tty + " Required for true color inside tmux + let &t_8f = "\[38;2;%lu;%lu;%lum" + let &t_8b = "\[48;2;%lu;%lu;%lum" set termguicolors endif @@ -216,20 +219,20 @@ nmap q :q nmap x :x " Clear search highlight -map :noh +nnoremap :noh " Buffer navigation -map bd :Bclose -map ba :bufdo bd -map l :bnext -map h :bprevious +nnoremap bd :Bclose +nnoremap ba :bufdo bd +nnoremap l :bnext +nnoremap h :bprevious " Tab management -map tn :tabnew -map to :tabonly -map tc :tabclose -map tm :tabmove -map t :tabnext +nnoremap tn :tabnew +nnoremap to :tabonly +nnoremap tc :tabclose +nnoremap tm :tabmove +nnoremap t :tabnext let g:lasttab = 1 nmap tl :exe "tabn ".g:lasttab @@ -238,8 +241,8 @@ augroup ChopstickTabHistory autocmd TabLeave * let g:lasttab = tabpagenr() augroup END -map te :tabedit =expand("%:p:h")/ -map cd :lcd %:p:h:pwd +nnoremap te :tabedit =expand("%:p:h")/ +nnoremap cd :lcd %:p:h:pwd " File browser (netrw — built-in, no plugins) nnoremap e :Explore @@ -255,18 +258,18 @@ nnoremap gV `[v`] cnoremap cnoremap -" Move lines -nmap mz:m+`z -nmap mz:m-2`z -vnoremap J :m '>+1gv=gv -vnoremap K :m '<-2gv=gv +" Move lines (Alt+j / Alt+k in both normal and visual mode) +nnoremap :m .+1== +nnoremap :m .-2== +vnoremap :m '>+1gv=gv +vnoremap :m '<-2gv=gv " Spell checking -map ss :setlocal spell! -map sn ]s -map sp [s -map sa zg -map s? z= +nnoremap ss :setlocal spell! +nnoremap sn ]s +nnoremap sp [s +nnoremap sa zg +nnoremap s? z= " Toggle modes set pastetoggle= @@ -506,7 +509,11 @@ let g:lsp_signs_warning = {'text': '!'} let g:lsp_signs_information = {'text': 'i'} let g:lsp_signs_hint = {'text': '>'} -set completeopt=menuone,noinsert,noselect +if has('patch-8.1.1517') + set completeopt=menuone,noinsert,noselect,popup +else + set completeopt=menuone,noinsert,noselect +endif set pumheight=15 let g:asyncomplete_auto_popup = 1 let g:asyncomplete_auto_completeopt = 1 @@ -587,9 +594,9 @@ let g:EasyMotion_smartcase = 1 " s + two chars: jump anywhere on screen nmap s (easymotion-overwin-f2) -" J/K: line motions -map j (easymotion-j) -map k (easymotion-k) +" Line motions +nmap j (easymotion-j) +nmap k (easymotion-k) " ============================================================================ " => UndoTree @@ -620,12 +627,12 @@ endif if exists('g:plugs["vim-startify"]') let g:startify_custom_header = [ - \ ' ███╗ ███╗ ██╗███╗ ██╗ ██████╗ ███████╗ █████╗ ███╗ ███╗ █████╗ ', - \ ' ████╗ ████║███║████╗ ██║██╔════╝ ██╔════╝██╔══██╗████╗ ████║██╔══██╗ ', - \ ' ██╔████╔██║╚██║██╔██╗ ██║██║ ███╗███████╗███████║██╔████╔██║███████║ ', - \ ' ██║╚██╔╝██║ ██║██║╚██╗██║██║ ██║╚════██║██╔══██║██║╚██╔╝██║██╔══██║ ', - \ ' ██║ ╚═╝ ██║ ██║██║ ╚████║╚██████╔╝███████║██║ ██║██║ ╚═╝ ██║██║ ██║ ', - \ ' ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝', + \ ' ██████╗██╗ ██╗ ██████╗ ██████╗ ███████╗████████╗██╗ ██████╗██╗ ██╗███████╗', + \ ' ██╔════╝██║ ██║██╔═══██╗██╔══██╗██╔════╝╚══██╔══╝██║██╔════╝██║ ██╔╝██╔════╝', + \ ' ██║ ███████║██║ ██║██████╔╝███████╗ ██║ ██║██║ █████╔╝ ███████╗', + \ ' ██║ ██╔══██║██║ ██║██╔═══╝ ╚════██║ ██║ ██║██║ ██╔═██╗ ╚════██║', + \ ' ╚██████╗██║ ██║╚██████╔╝██║ ███████║ ██║ ██║╚██████╗██║ ██╗███████║', + \ ' ╚═════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚══════╝', \ '', \ ] @@ -636,11 +643,16 @@ if exists('g:plugs["vim-startify"]') \ { 'type': 'bookmarks', 'header': [' Bookmarks'] }, \ ] - let g:startify_bookmarks = [ - \ {'v': '~/.vimrc'}, - \ {'z': '~/.zshrc'}, - \ {'b': '~/.bashrc'}, - \ ] + 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_autoload = 1 @@ -797,7 +809,7 @@ endfunc " Suppress comment continuation on Enter / o / O augroup ChopstickFormatOptions autocmd! - autocmd BufEnter * setlocal formatoptions-=c formatoptions-=r formatoptions-=o + autocmd FileType * setlocal formatoptions-=c formatoptions-=r formatoptions-=o augroup END " Auto-disable paste mode on leaving insert @@ -806,13 +818,6 @@ augroup ChopstickPaste autocmd InsertLeave * set nopaste augroup END -" Strip trailing whitespace on save (real files only) -augroup ChopstickCleanup - autocmd! - autocmd BufWritePre * - \ if empty(&buftype) && !empty(expand('')) | call CleanExtraSpaces() | endif -augroup END - augroup ChopstickFiletype autocmd! @@ -907,7 +912,7 @@ nnoremap nnoremap W :%s/\s\+$//:let @/='' " Source helpers -nnoremap so :source % +nnoremap so :if &filetype ==# 'vim' source % echo "Sourced " . expand('%') else echo "Not a vim file" endif nnoremap ev :edit $MYVIMRC nnoremap sv :source $MYVIMRC:echo "vimrc reloaded" @@ -915,8 +920,10 @@ nnoremap sv :source $MYVIMRC:echo "vimrc reloaded" nnoremap * :%s/\<\>//g " Copy path / filename to clipboard -nnoremap cp :let @+ = expand("%:p"):echo "Copied: " . expand("%:p") -nnoremap cf :let @+ = expand("%:t"):echo "Copied: " . expand("%:t") +if has('clipboard') + nnoremap cp :let @+ = expand("%:p"):echo "Copied: " . expand("%:p") + nnoremap cf :let @+ = expand("%:t"):echo "Copied: " . expand("%:t") +endif " Scratch markdown buffer nnoremap ms :e ~/buffer.md @@ -981,7 +988,6 @@ augroup END function! LargeFileSettings() setlocal bufhidden=unload setlocal undolevels=-1 - setlocal eventignore+=FileType setlocal noswapfile setlocal syntax= let b:ale_enabled = 0 diff --git a/README.md b/README.md index 0f9e938..f2feec7 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,10 @@ Supported languages and their servers: | Markdown | marksman | | SQL | sqls | +**Note:** While vim-lsp itself needs no Node.js, some language servers (TypeScript, +HTML, CSS, JSON, YAML) are npm packages that require Node.js to run. Python (pylsp), +Go (gopls), and Rust (rust-analyzer) language servers do not need Node.js. + **Markdown LSP** requires `marksman` as a standalone binary: ```bash @@ -203,8 +207,14 @@ Press `,?` at any time to open the built-in cheat sheet. | `Space` | Toggle code fold | | `Y` | Yank to end of line | | `Ctrl+d` / `Ctrl+u` | Half-page scroll, cursor centred | -| `Alt+j` / `Alt+k` | Move line down / up | +| `Alt+j` / `Alt+k` | Move line down / up (normal and visual) | | `,u` | Undo tree (visual branch history) | +| `F2` | Toggle paste mode | +| `F3` / `F4` | Toggle line numbers / relative numbers | +| `F5` | Toggle undo tree | +| `F6` | Toggle invisible characters | +| `gV` | Reselect last paste | +| `//` | Search visual selection | ### Survival @@ -212,7 +222,8 @@ Press `,?` at any time to open the built-in cheat sheet. |-----|--------| | `jk` | Exit insert mode | | `Esc` | Exit insert / visual mode | -| `Ctrl+s` | Save | +| `jk` | Exit insert mode | +| `Ctrl+s` | Save (any mode) | | `,w` | Save | | `,x` | Save and quit | | `,q` | Quit | @@ -227,6 +238,13 @@ Press `,?` at any time to open the built-in cheat sheet. | `Esc Esc` | Exit terminal mode | | `,tn` / `,tc` | New tab / close tab | | `,tl` | Toggle to last tab | +| `,ev` / `,sv` | Edit / reload `~/.vimrc` | +| `,cp` / `,cf` | Copy file path / filename to clipboard | +| `,*` | Search and replace word under cursor | +| `,F` | Re-indent entire file | +| `,W` | Strip trailing whitespace | +| `,ms` | Open scratch markdown buffer | +| `,ss` | Toggle spell checking | --- diff --git a/install.sh b/install.sh index 1fec44b..6696e85 100755 --- a/install.sh +++ b/install.sh @@ -1,23 +1,41 @@ #!/usr/bin/env bash # install.sh - chopsticks vim configuration installer -# Usage: cd /path/to/chopsticks && ./install.sh [--yes] +# Usage: cd /path/to/chopsticks && ./install.sh [--yes] [--help] # -# --yes non-interactive: install all optional components automatically +# --yes non-interactive: install all optional components automatically +# --help show this help and exit set -eo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" AUTO_YES=0 -[[ "${1:-}" == "--yes" ]] && AUTO_YES=1 +for arg in "$@"; do + case "$arg" in + --yes) AUTO_YES=1 ;; + --help|-h) + echo "Usage: ./install.sh [OPTIONS]" + echo "" + echo "Options:" + echo " --yes Non-interactive mode: select all defaults automatically" + echo " --help Show this help and exit" + echo "" + echo "Supported platforms: macOS (brew), Debian/Ubuntu (apt), Arch (pacman), Fedora (dnf)" + exit 0 ;; + esac +done -# ── Colours ─────────────────────────────────────────────────────────────────── -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -RED='\033[0;31m' -BOLD='\033[1m' -CYAN='\033[0;36m' -DIM='\033[2m' -NC='\033[0m' +# ── Colours (respect NO_COLOR and non-TTY) ─────────────────────────────────── +if [[ -t 1 ]] && [[ -z "${NO_COLOR:-}" ]]; then + GREEN='\033[0;32m' + YELLOW='\033[1;33m' + RED='\033[0;31m' + BOLD='\033[1m' + CYAN='\033[0;36m' + DIM='\033[2m' + NC='\033[0m' +else + GREEN='' YELLOW='' RED='' BOLD='' CYAN='' DIM='' NC='' +fi ok() { echo -e "${GREEN}[OK]${NC} $1"; } warn() { echo -e "${YELLOW}[!]${NC} $1"; } @@ -50,13 +68,14 @@ ask() { # ── Error trap ──────────────────────────────────────────────────────────────── on_error() { - echo -e "\n${RED}[FATAL]${NC} Unexpected error 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 " ./install.sh 2>&1 | tee /tmp/chopsticks-install.log" >&2 echo " Report issues: https://github.com/m1ngsama/chopsticks/issues" >&2 } trap on_error ERR -trap 'rm -f /tmp/chopsticks-hadolint /tmp/chopsticks-marksman 2>/dev/null' EXIT +_TMPDIR=$(mktemp -d "${TMPDIR:-/tmp}/chopsticks-XXXXXX") +trap 'rm -rf "$_TMPDIR" 2>/dev/null' EXIT # ── Safe download helper ────────────────────────────────────────────────────── safe_download() { @@ -604,7 +623,7 @@ elif [[ $HAS_APT -eq 1 ]]; then else _do_binary_apt "hadolint" hadolint "$_I_HADOLINT" \ "https://github.com/hadolint/hadolint/releases/download/${HVER}/hadolint-Linux-${HARCH}" \ - /tmp/chopsticks-hadolint + "$_TMPDIR/hadolint" fi else skip "hadolint"; SKIPPED+=("hadolint") @@ -621,7 +640,7 @@ elif [[ $HAS_APT -eq 1 ]]; then else _do_binary_apt "marksman" marksman "$_I_MARKSMAN" \ "https://github.com/artempyanykh/marksman/releases/download/${MVER}/marksman-linux-${MARCH}" \ - /tmp/chopsticks-marksman + "$_TMPDIR/marksman" fi else skip "marksman"; SKIPPED+=("marksman") @@ -714,8 +733,10 @@ else if command -v "$check" >/dev/null 2>&1; then ok "$pkg (already installed)"; return fi - if pip3 install --quiet "$pkg" 2>/dev/null || \ - pip3 install --quiet --break-system-packages "$pkg" 2>/dev/null; then + if pip3 install --quiet "$pkg" 2>/dev/null; then + ok "$pkg"; INSTALLED+=("$pkg") + elif pip3 install --quiet --break-system-packages "$pkg" 2>/dev/null; then + warn "$pkg installed with --break-system-packages (consider using a virtualenv)" ok "$pkg"; INSTALLED+=("$pkg") else fail "$pkg"; FAILED+=("$pkg")