Last active
June 24, 2026 08:43
-
-
Save kokoye2007/b363203ad2c6fd1b2330120ffb63458d to your computer and use it in GitHub Desktop.
vim plugin install script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # ============================================================ | |
| # vim setup installer (idempotent) | |
| # Plugins: vim-plug + coc.nvim, fzf, ale, tpope suite, | |
| # nerdtree, gitgutter, airline | |
| # Target: vim 9 (macOS / Linux) | |
| # | |
| # Author : kokoye2007 <https://gist.github.com/kokoye2007> | |
| # License: MIT | |
| # Credits / original sources: | |
| # vim-plug - junegunn https://github.com/junegunn/vim-plug | |
| # coc.nvim - neoclide https://github.com/neoclide/coc.nvim | |
| # fzf / fzf.vim - junegunn https://github.com/junegunn/fzf.vim | |
| # ALE - dense-analysis https://github.com/dense-analysis/ale | |
| # gruvbox - morhetz https://github.com/morhetz/gruvbox | |
| # vim-airline - vim-airline https://github.com/vim-airline/vim-airline | |
| # tpope suite - tpope https://github.com/tpope | |
| # NERDTree - preservim https://github.com/preservim/nerdtree | |
| # coc.nvim recommended .vimrc settings adapted from its official README. | |
| # ============================================================ | |
| set -euo pipefail | |
| VIMRC="$HOME/.vimrc" | |
| PLUG="$HOME/.vim/autoload/plug.vim" | |
| log() { printf '\033[1;32m==>\033[0m %s\n' "$*"; } | |
| warn() { printf '\033[1;33m[!]\033[0m %s\n' "$*"; } | |
| die() { printf '\033[1;31m[x]\033[0m %s\n' "$*" >&2; exit 1; } | |
| # sudo only when not root and sudo exists (root containers have neither need nor sudo) | |
| SUDO="" | |
| if [ "$(id -u)" -ne 0 ]; then | |
| if command -v sudo >/dev/null 2>&1; then | |
| SUDO="sudo" | |
| else | |
| warn "not root and no sudo; package install may fail" | |
| fi | |
| fi | |
| # ------------------------------------------------------------ | |
| # 1. dependency check | |
| # ------------------------------------------------------------ | |
| log "Checking dependencies" | |
| need=(vim git curl) | |
| soft=(node npm fzf rg) # node/npm required by coc; fzf/rg by fzf.vim | |
| for c in "${need[@]}"; do | |
| command -v "$c" >/dev/null 2>&1 || die "missing required tool: $c" | |
| done | |
| # vim feature/version sanity — coc.nvim needs vim >= 8.1.1719 (9.x recommended) | |
| vim_ver="$(vim --version | sed -n '1s/.*VIM - Vi IMproved \([0-9.]*\).*/\1/p')" | |
| case "$vim_ver" in | |
| ''|7.*|8.0*) warn "vim $vim_ver detected; coc.nvim needs >= 8.1.1719 (vim 9 recommended)" ;; | |
| esac | |
| vim --version | grep -q '+clipboard' || warn "vim built without +clipboard; system clipboard yank/paste disabled (Linux: install vim-gtk3)" | |
| # node version: coc.nvim requires node >= 16.18 | |
| if command -v node >/dev/null 2>&1; then | |
| node_major="$(node -v 2>/dev/null | sed -n 's/^v\([0-9]*\).*/\1/p')" | |
| node_minor="$(node -v 2>/dev/null | sed -n 's/^v[0-9]*\.\([0-9]*\).*/\1/p')" | |
| if [ -n "$node_major" ] && { [ "$node_major" -lt 16 ] || { [ "$node_major" -eq 16 ] && [ "${node_minor:-0}" -lt 18 ]; }; }; then | |
| warn "node $(node -v) too old for coc.nvim (needs >= 16.18); upgrade node" | |
| fi | |
| fi | |
| missing_soft=() | |
| for c in "${soft[@]}"; do | |
| command -v "$c" >/dev/null 2>&1 || missing_soft+=("$c") | |
| done | |
| # map a generic tool name -> package name for a given manager | |
| pkg_name() { | |
| local mgr="$1" tool="$2" | |
| case "$mgr:$tool" in | |
| *:rg) echo "ripgrep" ;; | |
| brew:npm) echo "node" ;; # npm ships with the node formula on brew | |
| apt:node) echo "nodejs" ;; | |
| apt:npm) echo "npm" ;; | |
| dnf:node) echo "nodejs" ;; | |
| dnf:npm) echo "npm" ;; | |
| pacman:node) echo "nodejs" ;; | |
| pacman:npm) echo "npm" ;; | |
| zypper:node) echo "nodejs" ;; | |
| zypper:npm) echo "npm" ;; | |
| *) echo "$tool" ;; | |
| esac | |
| } | |
| # detect package manager: brew (mac/linuxbrew), apt, dnf/yum, pacman, zypper | |
| detect_mgr() { | |
| if command -v brew >/dev/null 2>&1; then echo brew | |
| elif command -v apt-get >/dev/null 2>&1; then echo apt | |
| elif command -v dnf >/dev/null 2>&1; then echo dnf | |
| elif command -v yum >/dev/null 2>&1; then echo dnf | |
| elif command -v pacman >/dev/null 2>&1; then echo pacman | |
| elif command -v zypper >/dev/null 2>&1; then echo zypper | |
| else echo none | |
| fi | |
| } | |
| mgr_install() { | |
| local mgr="$1"; shift | |
| case "$mgr" in | |
| brew) brew install "$@" ;; # brew never runs as root | |
| apt) $SUDO apt-get update -qq && $SUDO apt-get install -y "$@" ;; | |
| dnf) if command -v dnf >/dev/null 2>&1; then $SUDO dnf install -y "$@"; else $SUDO yum install -y "$@"; fi ;; | |
| pacman) $SUDO pacman -Sy --noconfirm "$@" ;; | |
| zypper) $SUDO zypper install -y "$@" ;; | |
| esac | |
| } | |
| if [ "${#missing_soft[@]}" -gt 0 ]; then | |
| warn "missing optional tools: ${missing_soft[*]}" | |
| mgr="$(detect_mgr)" | |
| if [ "$mgr" = none ]; then | |
| warn "No supported package manager found (brew/apt/dnf/pacman/zypper)." | |
| warn "Install manually: ${missing_soft[*]}" | |
| warn "coc.nvim needs node+npm; fzf.vim needs fzf+ripgrep." | |
| else | |
| log "Detected package manager: $mgr" | |
| pkgs=() | |
| for c in "${missing_soft[@]}"; do | |
| pkgs+=("$(pkg_name "$mgr" "$c")") | |
| done | |
| # dedup (node may map twice for node+npm on brew) | |
| uniq_pkgs=() | |
| for p in "${pkgs[@]}"; do | |
| case " ${uniq_pkgs[*]} " in *" $p "*) ;; *) uniq_pkgs+=("$p") ;; esac | |
| done | |
| log "Installing: ${uniq_pkgs[*]}" | |
| mgr_install "$mgr" "${uniq_pkgs[@]}" | |
| fi | |
| fi | |
| # ------------------------------------------------------------ | |
| # 2. vim-plug | |
| # ------------------------------------------------------------ | |
| if [ -f "$PLUG" ]; then | |
| log "vim-plug already present" | |
| else | |
| log "Installing vim-plug" | |
| curl -fLo "$PLUG" --create-dirs \ | |
| https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim | |
| fi | |
| # ------------------------------------------------------------ | |
| # 3. .vimrc (write to temp, replace only if changed -> no .bak spam) | |
| # ------------------------------------------------------------ | |
| tmp_vimrc="$(mktemp "${TMPDIR:-/tmp}/vimrc.XXXXXXXX")" # explicit template: portable to old BSD mktemp | |
| trap 'rm -f "$tmp_vimrc"' EXIT | |
| cat > "$tmp_vimrc" <<'VIMRC_EOF' | |
| " ============================================================ | |
| " Base settings | |
| " ============================================================ | |
| set nocompatible | |
| set encoding=utf-8 " needed for ALE sign glyphs (✘ ⚠) and coc UI | |
| syntax on | |
| filetype plugin indent on | |
| set number | |
| set relativenumber | |
| set hidden | |
| set incsearch | |
| set ignorecase | |
| set smartcase | |
| set mouse=a | |
| " 24-bit color only when the terminal advertises it; otherwise leave | |
| " the terminal's native palette (termguicolors on a 256-color term garbles). | |
| if has('termguicolors') && ($COLORTERM ==# 'truecolor' || $COLORTERM ==# '24bit') | |
| set termguicolors | |
| endif | |
| " System clipboard: unnamedplus (+) on Linux, unnamed (*) on macOS. | |
| " Needs vim built with +clipboard (Linux: install vim-gtk3 / gvim). | |
| if has('clipboard') | |
| if has('unnamedplus') | |
| set clipboard=unnamedplus | |
| else | |
| set clipboard=unnamed | |
| endif | |
| endif | |
| let mapleader = " " | |
| " ============================================================ | |
| " vim-plug plugins | |
| " ============================================================ | |
| " Let coc.nvim own LSP; ALE handles linting/fixing only. | |
| " MUST be set before ALE loads. | |
| let g:ale_disable_lsp = 1 | |
| call plug#begin('~/.vim/plugged') | |
| " Sane defaults | |
| Plug 'tpope/vim-sensible' | |
| " Colorscheme (pairs with termguicolors above) | |
| Plug 'morhetz/gruvbox' | |
| " Conquer of Completion (LSP-style completion). Release branch recommended. | |
| Plug 'neoclide/coc.nvim', {'branch': 'release'} | |
| " Fuzzy finder | |
| Plug 'junegunn/fzf', { 'do': { -> fzf#install() } } | |
| Plug 'junegunn/fzf.vim' | |
| " Asynchronous Lint Engine (linting + fixing; LSP disabled above) | |
| Plug 'dense-analysis/ale' | |
| " Git | |
| Plug 'tpope/vim-fugitive' | |
| Plug 'airblade/vim-gitgutter' | |
| " Editing | |
| Plug 'tpope/vim-surround' | |
| Plug 'tpope/vim-commentary' | |
| Plug 'tpope/vim-repeat' " makes surround/commentary repeatable with . | |
| Plug 'tpope/vim-unimpaired' " bracket mappings: ]q ]b ]l ]<Space> ... | |
| " File tree | |
| Plug 'preservim/nerdtree' | |
| " Statusline | |
| Plug 'vim-airline/vim-airline' | |
| Plug 'vim-airline/vim-airline-themes' | |
| call plug#end() | |
| " ============================================================ | |
| " Colorscheme / statusline theme | |
| " ============================================================ | |
| " silent! so the very first headless PlugInstall run (scheme not yet on disk) | |
| " doesn't abort with E185. | |
| set background=dark | |
| silent! colorscheme gruvbox | |
| let g:airline_theme = 'gruvbox' | |
| " ============================================================ | |
| " coc.nvim recommended settings | |
| " ============================================================ | |
| " Some servers have issues with backup files | |
| set nobackup | |
| set nowritebackup | |
| " Faster update for diagnostics / signature help | |
| set updatetime=300 | |
| " Always show signcolumn (avoids shifting text) | |
| set signcolumn=yes | |
| " Auto-install these coc extensions on startup if missing. | |
| " NOTE: coc-ale was removed (unpublished from npm) — ALE and coc run | |
| " independently; no bridge needed. Add language servers below as required, | |
| " e.g. 'coc-tsserver', 'coc-pyright', 'coc-json'. | |
| let g:coc_global_extensions = [] | |
| " Use <Tab> / <S-Tab> to navigate completion menu | |
| inoremap <silent><expr> <Tab> | |
| \ coc#pum#visible() ? coc#pum#next(1) : | |
| \ "\<Tab>" | |
| inoremap <expr><S-Tab> coc#pum#visible() ? coc#pum#prev(1) : "\<C-h>" | |
| " <CR> confirms selected completion | |
| inoremap <silent><expr> <CR> coc#pum#visible() ? coc#pum#confirm() | |
| \ : "\<C-g>u\<CR>\<c-r>=coc#on_enter()\<CR>" | |
| " Trigger completion with <C-Space> | |
| inoremap <silent><expr> <c-@> coc#refresh() | |
| " GoTo navigation | |
| nmap <silent> gd <Plug>(coc-definition) | |
| nmap <silent> gy <Plug>(coc-type-definition) | |
| nmap <silent> gi <Plug>(coc-implementation) | |
| nmap <silent> gr <Plug>(coc-references) | |
| " Show documentation on K | |
| nnoremap <silent> K :call ShowDocumentation()<CR> | |
| function! ShowDocumentation() | |
| if CocAction('hasProvider', 'hover') | |
| call CocActionAsync('doHover') | |
| else | |
| call feedkeys('K', 'in') | |
| endif | |
| endfunction | |
| " Symbol rename | |
| nmap <leader>rn <Plug>(coc-rename) | |
| " Apply code action / quickfix | |
| nmap <leader>qf <Plug>(coc-fix-current) | |
| " ============================================================ | |
| " ALE settings (lint/fix only; coc owns completion + LSP) | |
| " ============================================================ | |
| let g:ale_fix_on_save = 1 | |
| let g:ale_lint_on_text_changed = 'normal' | |
| let g:ale_lint_on_insert_leave = 1 | |
| let g:ale_sign_error = '✘' | |
| let g:ale_sign_warning = '⚠' | |
| " Navigate ALE diagnostics | |
| nmap <silent> [a <Plug>(ale_previous_wrap) | |
| nmap <silent> ]a <Plug>(ale_next_wrap) | |
| " ============================================================ | |
| " fzf.vim key maps | |
| " ============================================================ | |
| nnoremap <leader>f :Files<CR> | |
| nnoremap <leader>b :Buffers<CR> | |
| nnoremap <leader>g :Rg<CR> | |
| nnoremap <leader>l :Lines<CR> | |
| " ============================================================ | |
| " NERDTree | |
| " ============================================================ | |
| nnoremap <leader>n :NERDTreeToggle<CR> | |
| nnoremap <leader>nf :NERDTreeFind<CR> | |
| VIMRC_EOF | |
| if [ -f "$VIMRC" ] && cmp -s "$tmp_vimrc" "$VIMRC"; then | |
| log ".vimrc already up to date" | |
| else | |
| if [ -f "$VIMRC" ]; then | |
| bak="$VIMRC.bak.$(date +%Y%m%d-%H%M%S)" | |
| cp "$VIMRC" "$bak" | |
| log "Backed up existing .vimrc -> $bak" | |
| fi | |
| cp "$tmp_vimrc" "$VIMRC" | |
| log "Wrote $VIMRC" | |
| fi | |
| # ------------------------------------------------------------ | |
| # 4. install plugins | |
| # ------------------------------------------------------------ | |
| log "Installing plugins (PlugInstall)" | |
| vim -es -u "$VIMRC" -c 'PlugInstall --sync' -c 'qa' || true | |
| # Patch coc.nvim E1208 on Vim 9.1+ (strict -complete check): the release | |
| # branch defines `CocListCancel` with `-nargs=0 -complete=...`, which newer | |
| # vim rejects (E1208: -complete used without allowing arguments). Strip the | |
| # redundant -complete (a no-arg command has nothing to complete). Idempotent. | |
| coc_plugin="$HOME/.vim/plugged/coc.nvim/plugin/coc.vim" | |
| if [ -f "$coc_plugin" ] && grep -q 'CocListCancel.*-complete' "$coc_plugin"; then | |
| log "Patching coc.nvim E1208 (CocListCancel -complete)" | |
| sed -i.e1208bak 's/\(command! -nargs=0\) -complete=[^ ]* \(CocListCancel\)/\1 \2/' "$coc_plugin" | |
| rm -f "$coc_plugin.e1208bak" | |
| fi | |
| # Bootstrap coc extensions headlessly only if any are declared | |
| if grep -q "let g:coc_global_extensions = \[[^]]" "$VIMRC"; then | |
| log "Updating coc extensions (CocUpdateSync)" | |
| vim -es -u "$VIMRC" -c 'CocUpdateSync' -c 'qa' || true | |
| fi | |
| log "Done. Open 'vim' to finish coc bootstrap." | |
| # ------------------------------------------------------------ | |
| # 5. usage cheatsheet | |
| # ------------------------------------------------------------ | |
| cat <<'HELP' | |
| ============================================================ | |
| QUICK START (leader = <Space>) | |
| ============================================================ | |
| Plugins / health | |
| :PlugStatus / :PlugUpdate manage plugins | |
| :checkhealth (or :CocInfo) coc diagnostics | |
| Change theme | |
| 1. add a colorscheme plugin in ~/.vimrc, e.g. | |
| Plug 'catppuccin/vim', { 'as': 'catppuccin' } | |
| 2. :PlugInstall then restart | |
| 3. set in ~/.vimrc: | |
| colorscheme catppuccin_mocha | |
| let g:airline_theme = 'catppuccin' | |
| (current default: gruvbox, dark background) | |
| fzf (fuzzy finder) | |
| <Space>f :Files find files in project | |
| <Space>b :Buffers switch open buffers | |
| <Space>g :Rg live grep (needs ripgrep) | |
| <Space>l :Lines search lines in buffers | |
| inside fzf: <C-t> tab <C-x> split <C-v> vsplit <Esc> cancel | |
| NERDTree (file tree) | |
| <Space>n toggle tree | |
| <Space>nf reveal current file | |
| in tree: o open t tab s vsplit i split m menu(add/del/rename) | |
| coc.nvim (completion / LSP) | |
| <Tab>/<S-Tab> cycle completion <CR> confirm <C-Space> trigger | |
| gd definition gy type gi impl gr references K hover docs | |
| <Space>rn rename <Space>qf quickfix | |
| add a language server: :CocInstall coc-pyright coc-tsserver coc-json | |
| (or list them in g:coc_global_extensions in ~/.vimrc) | |
| ALE (lint + autofix; fixes on save) | |
| [a / ]a previous / next diagnostic | |
| :ALEFix fix now :ALEInfo which linters ran | |
| git | |
| :Git / :Gstatus (fugitive) ]c / [c next/prev hunk (gitgutter) | |
| editing (tpope) | |
| cs"' change surround ds" delete ysiw" add | |
| gcc toggle comment line gc{motion} comment region | |
| ============================================================ | |
| HELP | |
| log "Add language servers via :CocInstall coc-pyright (etc.) or g:coc_global_extensions." |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment