Skip to content

Instantly share code, notes, and snippets.

@kokoye2007
Last active June 24, 2026 08:43
Show Gist options
  • Select an option

  • Save kokoye2007/b363203ad2c6fd1b2330120ffb63458d to your computer and use it in GitHub Desktop.

Select an option

Save kokoye2007/b363203ad2c6fd1b2330120ffb63458d to your computer and use it in GitHub Desktop.
vim plugin install script
#!/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