" _ _ ____ _ _ ____ _ _ _ _ _ | " |__| |___ | | | | | | | |\/| | " | | |___ |___ |___ |__| . \/ | | | . " ' " This is the Vim(Neovim) initialization file categorized practically. " Bundle(plugin) dependent configs are splited into "vimrc.bundle". " " Author: Bohr Shaw " Comments:" {{{ " Be: healthy, stable, efficient, consistent, intuitive, convenient, accessible! " First and foremost, master the help system. (:h helphelp) " For an overview, :h quickref, :h index " Could view and set all options. (:h :options) " See minimal sensible settings: https://github.com/tpope/vim-sensible/blob/master/plugin/sensible.vim " Analyse startup performance with vim-profile.sh " Mapping notes: " Use instead of to cancel a mapping " :h map-which-keys " Potentially unused keys: "\ Z Q R S X _ !" " Keys waiting for a second key: "f t d c g z v y m q ' [ ]" " Special keys like , are often mapped solely, as well as 'q' which is " often mapped to quit a window. " /, /, / are pairs of exactly same keys. " Some keys like Caps Lock, , etc. are not mappable. " is the same as , use instead. " }}} " Starting:" {{{ " Define an augroup for all autocmds in this file and empty it augroup vimrc | execute 'autocmd!' | augroup END if has('vim_starting') set all& " override system vimrc and cmdline options like --noplugin set nocompatible " make Vim behave in a more useful way if has('win32') " Wish to use a forward slash for path separator? But 'shellslash' is not " designed to be set alone. Plugins must explicitly cope with it. Wish a " 'internal_shellslash' be available! " " Plugins like 'fugitive' works regardless of this option. While more " plugins like 'gnupg', 'jedi' wouldn't function well when it's set. " Troublesomely, 'unite', 'vimproc' have problems when it's not set. set shellslash& endif let $MYVIMRC = empty($MYVIMRC) ? expand(':p') : resolve($MYVIMRC) let $MYVIM = fnamemodify($MYVIMRC, ':p:h') " be portable let g:ported = $MYVIM == expand('~/.vim') ? 0 : 1 " Cross-platform 'runtimepath' set rtp=$MYVIM,$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after,$MYVIM/after if has('gui_running') || $termencoding ==? 'utf-8' set encoding=utf-8 " used inside Vim, allow mapping with the ALT key endif set timeout ttimeout " Nvim has different defaults " set timeoutlen=3000 " mapping delay set ttimeoutlen=10 " key code delay (instant escape from Insert mode) " Deal with meta-key mappings:" {{{ if has('nvim') " Map meta-chords to esc-sequences in terminals for c in map(range(33, 123) + range(125, 126), 'nr2char(v:val)') execute 'tnoremap '.' '.c endfor tnoremap \| tnoremap else runtime autoload/key.vim " mappable meta key in terminals endif " }}} if has('nvim') " skip python check to reduce startup time let [g:python_host_skip_check, g:python3_host_skip_check] = [1, 1] endif " Whether to include the least number of bundles, for shell command line editing let g:l = get(g:, 'l', $VL) || argv(0) =~# '^\V'. \(empty($TMPPREFIX)?'/tmp/zsh':$TMPPREFIX).'ecl\|'.$TMP.'/bash-fc' let $MYVIMRCPRE = g:ported ? $MYVIM.'/vimrc.pre' : $HOME.'/.vimrc.pre' if filereadable($MYVIMRCPRE) execute 'silent source' $MYVIMRCPRE endif endif " }}} " Meta:" {{{ " let mapleader = "\r" " replace in a map let maplocalleader = "\t" " replace in a map noremap let g:mapinsertleader = "\" " Commands for defining mappings in several modes command! -nargs=1 NXnoremap nnoremap xnoremap command! -nargs=1 NXmap nmap xmap command! -nargs=1 NOnoremap nnoremap onoremap command! -nargs=1 NOmap nmap omap command! -nargs=1 NXOnoremap nnoremap xnoremap onoremap command! -nargs=1 NXOmap nmap xmap omap " Allow chained commands, but also check for a " to start a comment command! -bar -nargs=1 NXInoremap nnoremap xnoremap \ inoremap " Execute a remapped key in its un-remapped(vanilla) state noremap nr2char(getchar()) noremap! nr2char(getchar()) " Execte a global key shadowed by the same local one nnoremap g\ :call gmap('n') xnoremap g\ :call gmap('x') function! s:gmap(mode) " {{{ let k = v#getchar() let map = maparg(k, a:mode, 0, 1) try execute a:mode.'unmap ' k catch Echow 'No such local mapping.' | return 1 endtry execute 'normal' (a:mode == 'x' ? 'gv' : '').k execute a:mode.(map.noremap?'nore':'').'map' \ map.silent?'':'' map.expr?'':'' map.nowait?'':'' \ '' map.lhs map.rhs endfunction " }}} " Define a full-id abbreviation with minimal conflict command! -nargs=1 Abbr execute substitute(, '\v\s+\S+\zs', 'SoXx', '') " Complete and trigger a full-id abbreviation noremap! SoXx " Echo a warning message command! -bar -nargs=1 Echow echohl WarningMsg | echo | echohl None " A command doing nothing while accepting args (for quick composition) command! -nargs=* Nop : " }}} " Shortcuts:" {{{ " Escape inoremap if has('nvim') tnoremap endif inoremap " Enter the command line NXnoremap : inoremap : inoremap : if has('nvim') tnoremap : endif " Resolve local mapping conflicts with autocmd vimrc BufWinEnter option-window autocmd CursorMoved option-window \ execute 'nnoremap r '.maparg("")| \ unmap | \ autocmd! CursorMoved option-window " The command line window NXnoremap q: NXnoremap q: NXnoremap q/ " set cedit= cnoremap cnoremap autocmd vimrc CmdwinEnter * noremap q:| \ NXInoremap | \ NXInoremap " Yank till the line end instead of the whole line nnoremap Y y$ " Character-wise visual mode nnoremap vv ^vg_ nnoremap vV vg_ " quick access to GUI/system clipboard NXnoremap " "+ " Copy from the command line cabbrev c getcmdtype() == ':' && getcmdpos() == 2 ? 'copy' : 'c' " Access to the black hole register NXnoremap _ "_ " Run the current command with a bang(!) cnoremap einsert_bang() " Run the last command with a bang nnoremap @! :einsert_bang() function! s:insert_bang() " {{{ let [cmd, args] = split(getcmdline(), '\v(^\a+)@<=\ze(\A|$)', 1) return cmd.'!'.args endfunction " }}} " }}} " Motion:" {{{ set virtualedit=onemore " consistent cursor position on EOL set whichwrap& " left/right motions across lines " Search forward/backward regardless of the direction of the previous character search"{{{ " Note: These are overwritten in Sneak, but the semantics retains. if 0 && exists('*getcharsearch') " Vim patch 7.4.813 NXOnoremap ; getcharsearch().forward ? ';' : ',' NXOnoremap , getcharsearch().forward ? ',' : ';' elseif 0 NOnoremap F :execute 'silent! normal! mzf'.nr2char(getchar()).'g`z'.v:count1.',' xnoremap F :execute 'silent! normal! mzf'.nr2char(getchar()).'g`zgv'.v:count1.',' NOnoremap T :execute 'silent! normal! mzt'.nr2char(getchar()).'g`z'.v:count1.',' xnoremap T :execute 'silent! normal! mzt'.nr2char(getchar()).'g`zgv'.v:count1.',' endif "}}} " Display lines up/down (consecutive motions are quicker) nnoremap gj nnoremap gk " Jump to the middle of the current written line as opposed to the window width nnoremap gm :call cursor(0, virtcol('$')/2)|nnoremap gM gm set matchpairs+=<:> " character pairs matched by '%' if !has('nvim') " nvim put it in plugin/ runtime macros/matchit.vim " extended pair matching with '%' endif " Sections backword/forward nmap [[ nmap ]] " Navigate the change list nnoremap g; nnoremap g, " Go to the second-newest or current position in the change list nnoremap g. :try\|execute 'normal! g,g;'\| \ catch\|execute 'normal! g,'\|endtry " Print the change list or mark list cabbrev cs getcmdtype() == ':' && getcmdpos() == 3 ? 'changes' : 'cs' cabbrev ms getcmdtype() == ':' && getcmdpos() == 3 ? 'marks' : 'ms' " Navigate the jumper list nnoremap nnoremap " Jump to the definition of the current tag NXmap " Auto-place the cursor when switching buffers or files:" {{{ " Don't move the cursor to the start of the line when switching buffers augroup vimrc_cursor autocmd! autocmd BufLeave * set nostartofline| \autocmd vimrc_cursor CursorMoved * set startofline| \autocmd! vimrc_cursor CursorMoved augroup END " Jump to the last known position in a file just after opening it autocmd vimrc BufRead * silent! normal! g`" " }}} " }}} " Search:" {{{ set incsearch " show matches when typing the search pattern if !&hlsearch|set hlsearch|endif " highlight all matches of a search pattern set ignorecase " case insensitive in search patterns and command completion set smartcase " case sensitive only when up case characters present " Substitute in a visual area:" {{{ xnoremap sv :s/\%V " Substitute in a visual area (eat the for-expanding-space) " Hack: Use an expression to save a temporary value. cabbrev sv getcmdtype() == ':' && getcmdpos() =~ '[38]' ? \ 's/\%V'.setreg('z', nr2char(getchar(0)))[1:0].(@z == ' ' ? '' : @z) : 'sv' " }}} " Grep:" {{{ if executable('ag') set grepprg=ag\ --column " --nocolor --nobreak implicitly set grepformat^=%f:%l:%c:%m " the output format when not running interactively elseif executable('ack') set grepprg=ack\ --column set grepformat^=%f:%l:%c:%m endif " A wrapper around grep using 'ag' or 'ack' without affecting 'grepprg' and " 'grepformat'. Notice that a grep command like :grep, :lgrep, :grepadd etc. " still needs to be explicitly specified. command! -bar -nargs=+ -complete=file WithAg call grep#grep('ag', ) command! -bar -nargs=+ -complete=file WithAck call grep#grep('ack', ) " Grep all HELP docs preferably with ag, ack, helpgrep, in this order command! -nargs=+ -complete=command Help call grep#help() " A shortcut to ":Help grep" command! -nargs=+ -complete=command Helpgrep call grep#help('grep '.) " Grep through all buffers command! -nargs=1 BufGrep cexpr [] | bufdo vimgrepadd % " command! -nargs=1 BufGrep cexpr [] | mark Z | " \ execute "bufdo silent! g//caddexpr " \ expand('%').':'.line('.').':'.getline('.')" | normal `Z " }}} " QuickFix:" {{{ " Execute a command in each buffer in the quickfix or location list command! -nargs=1 -complete=command Qdo call vimrc#errdo('q', ) command! -nargs=1 -complete=command Ldo call vimrc#errdo() " Clear the current quickfix list command! -bar Cclear call setqflist([]) " Mappings/options for a quickfix/location window autocmd vimrc FileType qf nnoremap | \ nnoremap q c| \ nnoremap v H| \ nnoremap t T " }}} " }}} " View:" {{{ " Scroll relative to cursor (@_ suppresses [count] for zt) nnoremap zt v:count > 0 ? '@_zt'.v:count.'' : 'zt' nnoremap zb v:count > 0 ? '@_zb'.v:count.'' : 'zb' " The leader key for managing windows and tabs NXmap " Jump to {count}th next/previous window, able to jump back with p nnoremap j :let _w = winnr() + v:count1 \| \ execute (_w > winnr('$') ? _w - winnr('$') : _w).'wincmd w' nnoremap k :let _w = winnr() - v:count1 \| \ execute (_w < 1 ? _w + winnr('$') : _w).'wincmd w' for i in [2, 3, 4, 5] execute 'nmap '.i.'j' i.'j' execute 'nmap '.i.'k' i.'k' endfor nmap j nmap k nnoremap q inoremap q nnoremap :execute repeat('tabn\|', v:count1-1).'tabn' nnoremap gT nnoremap :execute 'tabmove+'.v:count1 nnoremap :execute 'tabmove-'.v:count1 nnoremap :windo quit nmap Q " Maxmize the current window by duplicate it in a new tab nnoremap sT nnoremap sT " Maxmize the current window or restore the previously window layout nnoremap O :call win_toggle() function! s:win_toggle() " {{{ if exists('t:winrestcmd') execute t:winrestcmd unlet t:winrestcmd else let t:winrestcmd = winrestcmd() resize | vertical resize cal winrestcmd() endif endfunction " }}} " Exchange the current window with the {count}th window nnoremap e :execute 'buffer '.winbufnr(v:count1).'\|' \.v:count1.'wincmd w\|buffer '.winbufnr(0) " Attach the current window bellow the last windows with the same width nnoremap a :execute 'close\|$wincmd w\|belowright sbuffer '.bufnr('') cabbrev v getcmdtype() == ':' && getcmdpos() == 2 ? 'vert' : 'v' cabbrev t getcmdtype() == ':' && getcmdpos() == 2 ? 'tab' : 't' " Deal with terminal buffers if has('nvim') tnoremap tnoremap w tnoremap W tnoremap tnoremap tnoremap tnoremap cabbrev st getcmdtype() == ':' && getcmdpos() == 3 ? 'new\|te' : 'st' cabbrev vt getcmdtype() == ':' && getcmdpos() == 3 ? 'vne\|te' : 'vt' cabbrev tt getcmdtype() == ':' && getcmdpos() == 3 ? 'tab new\|te' : 'tt' autocmd vimrc BufWinEnter,WinEnter term://* startinsert autocmd vimrc BufLeave term://* stopinsert endif " }}} " Fold: "{{{ " Open the fold the cursor is in, recursively nnoremap z zczO " Focus on a region using manual folding (mnemonic: pick) nnoremap zp :set operatorfunc=fold_othersg@ xnoremap zp :call fold_others() nnoremap zP :call fold_restore() function! s:fold_others(...) " {{{ let [line1, line2] = a:0 == 1 ? ["'[", "']"] : ["'<", "'>"] let b:fold_opts = [&fdm, &fdl, &fde] set fde=0 fdm=expr | redraw " disable existing folding set fdm=manual execute '1,'.line1.'-1fold|'.line2.'+1,$'.'fold' endfunction function! s:fold_restore() normal! zE let [&fdm, &fdl, &fde] = b:fold_opts normal! zvzz endfunction " }}} " Toggle fold methods nnoremap cof :let &foldmethod = tolower(matchstr( \',mmanual,kmarker,iindent,ssyntax,eexpr,ddiff', \','.nr2char(getchar()).'\zs\a*\C'))\|set foldmethod nmap zfm cof " Don't screw up folds when inserting text that might affect them. Also improve " speed by avoiding updating folds eagerly. " autocmd vimrc InsertEnter * if !exists('w:vfdml') && " \ &foldmethod != 'manual' && empty(&buftype) | " \ let w:vfdml=&foldmethod | set foldmethod=manual | endif " However, restoring 'foldmethod' on InsertLeave would cause text under the " cursor be closed if the inserted text creates a new fold level. " autocmd vimrc InsertLeave * if exists('w:vfdml') && empty(&buftype) | " \ let &foldmethod=w:vfdml | unlet w:vfdml | " \ execute 'silent! normal! zo' |endif "}}} " Buffer:" {{{ set hidden autoread " autowrite nnoremap d :bdelete " Delete the current buffer without closing its window nnoremap x :Bdelete command! -bang Bdelete try|b#|silent! bd#|catch|bd|endtry nnoremap w :bwipeout cabbrev vb getcmdtype() == ':' && getcmdpos() == 3 ? 'vert sb' : 'vb' cabbrev tb getcmdtype() == ':' && getcmdpos() == 3 ? 'tab sb' : 'tb' " Delete all buffers in the buffer list except the current one command! -bang BufOnly let _b = bufnr('') | let _f = &confirm | \ try | set noconfirm | \ silent! execute '1,'._b.'-bd|'._b.'+,$bd' | \ finally | let &confirm = _f | endtry " Wipe out all unlisted buffers command! BwipeoutUnlisted call vimrc#bufffer_wipe_unlisted() " }}} " File:" {{{ nnoremap w :write nnoremap w :write! nnoremap u :update nnoremap u :update! nnoremap a :wall nnoremap a :wall! nnoremap A :windo update nnoremap A :windo update! " Quick save and exit, useful when editing the shell command line inoremap ZZ nnoremap e :edit nnoremap e :edit! cnoremap =expand('%:h')/ nnoremap f :filetype detect nnoremap F :silent! unlet b:did_ftplugin b:did_after_ftpluginfiletype detect nnoremap c :checktime " Switch to the alternative buffer nnoremap :buffer # if has('nvim') tnoremap :buffer # endif nnoremap :sbuffer # nnoremap :vert sbuffer # " Find a file in 'path' cabbrev fi getcmdtype() == ':' && getcmdpos() == 3 ? 'fin' : 'fi' cabbrev vf getcmdtype() == ':' && getcmdpos() == 3 ? 'vert sf' : 'vf' cabbrev tf getcmdtype() == ':' && getcmdpos() == 3 ? 'tab sf' : 'tf' " Directories to search by `gf, :find, cd, lcd etc.` " (dir of the current file, current dir, etc.) let &g:path = '.,,~,'.$MYVIM.','.$MYVIM.'/after' set cdpath=,,.,~ if has('vim_starting') && 0 == argc() && has('gui_running') && !g:l cd $HOME endif " Open a destination file of a link cnoremap eget_link_targets() function! s:get_link_targets() " {{{ let [cmd; links] = split(getcmdline()) for l in links let cmd .= ' '.fnamemodify(resolve(expand(l)), ':~:.') endfor return cmd endfunction " }}} " Easy access to vimrc files Abbr cabbr v $MYVIMRC Abbr cabbr b $MYBUNDLE nnoremap v :execute 'Be' $MYVIMRC nnoremap b :execute 'Be' $MYBUNDLE " Switch to a file without reloading it command! -nargs=1 -bang Be execute (buflisted(expand())?'b': \filereadable(expand())||0?'e':'Nop').' '. " Make the file '_' a scratch buffer autocmd vimrc BufNewFile,BufReadPost _ set buftype=nofile nobuflisted bufhidden=hide autocmd vimrc SessionLoadPost * silent! bwipeout! _ " Recognise a file's encoding in this order " set fileencodings=ucs-bom,utf-8,cp936,gb18030,big5,latin1 set fileformats=unix,dos,mac " end-of-line formats precedence set fileformat=unix " only for the initial unnamed buffer set nowritebackup " write to symbolic files safely on windows " }}} " Completion:" {{{ " A smart and light to do insert-completion:" {{{ inoremap getline('.')[col('.')-2] !~ '^\s\?$' \|\| pumvisible() \ ? '' : '' inoremap pumvisible() \|\| getline('.')[col('.')-2] !~ '^\s\?$' \ ? '' : '' " Remove auto-definded mappings autocmd vimrc CmdwinEnter * silent! iunmap " }}} " CTRL-X completion-sub-mode :" {{{ " Shortcuts map! for s:c in split('lnpkti]fdvuos', '\zs') execute 'inoremap '.s:c.' ' endfor " Insert a digraph noremap! g " }}} " Auto-reverse letter case in insert mode {{{ inoremap =toggle(1) inoremap =toggle(2) function! s:toggle(arg) let b:case_reverse = get(b:, 'case_reverse') ? 0 : a:arg if !exists('#case_reverse#InsertCharPre#') augroup case_reverse autocmd InsertCharPre if b:case_reverse| \ let v:char = v:char =~# '\l' ? toupper(v:char) : tolower(v:char)| \ endif| \ if b:case_reverse == 1 && v:char !~ '\h'| \ let b:case_reverse = 0| \ endif " Wouldn't be triggered if leaving insert mode with autocmd InsertLeave let b:case_reverse = 0| autocmd! case_reverse augroup END endif return '' endfunction " }}} set completeopt=menu,longest " insert-completion mode set complete-=i " don't scan included files when insert-completing by set pumheight=15 " max candidates on insert-mode completion set wildcharm= " the key to trigger wildmode expansion in mappings set wildmenu wildmode=longest:full,full " command line completion mode silent! set wildignorecase " ignore case when completing file names/directories " Show all candidates cnoremap " Make a command(e.g. `:h ...`) split vertically or in a new tab. cnoremap v e'vert '.getcmdline() cnoremap t e'tab '.getcmdline() " Expand a mixed case command name:" {{{ cnoremap ecmd_expand() function! s:cmd_expand() let cmd = getcmdline() let [range, abbr] = [matchstr(cmd, '^\A*'), matchstr(cmd, '\a.*')] let parts = map(split(abbr, abbr =~ '\s' ? '\s' : '\zs'), 'toupper(v:val[0]).v:val[1:]') return range . join(parts, '*') endfunction " }}} " Abbreviations Abbr abbr bs Bohr Shaw " Type notated keys noremap! special_key() function! s:special_key() " {{{ let c1 = v#getchar(1, 1) if empty(c1) return '' endif if strtrans(c1)[0] == '^' let c1_2 = strtrans(c1)[1] if c1_2 == 'i' return '' elseif c1_2 == 'm' return '' elseif c1_2 == '[' return '' else return '' endif elseif has_key(s:keymap, c1) == 1 return s:keymap[c1] endif let c2 = v#getchar(1, 1) if empty(c2) return '' endif let c2_ = has_key(s:keymap_sp, c2) ? s:keymap_sp[c2] : c2 let cc = c1.c2 if has_key(s:keymap, cc) == 1 return s:keymap[cc] elseif c1 ==? 'f' let c2 = c2 == 0 ? 1.v#getchar() : c2 return '<'.toupper(c1).c2.'>' elseif cc =~# 'c.' return '' elseif cc =~# '[Cx].' return 'CTRL-'.toupper(c2) elseif cc =~# '[md].' return '<'.toupper(c1).'-'.c2_.'>' else return '' endif endfunction let s:keymap_sp = { \"\": 'Tab', \' ': 'Space', \"\": 'CR', \"\": 'BS', \} let s:keymap = { \' ': '', \"\": '', \"\": '', \"\": '', \"\": '', \"\": '', \'<': '', \'\': '', \'|': '', \'bu': '', \'no': '', \'nw': '', \'nm': '', \'si': '', \'sp': '', \'sc': '