"====================================================================== " Only for key mapping "====================================================================== " vim: sw=2 ts=2 foldmethod=marker foldmarker={{{,}}} " COMMON_MAPPING ----------------{{{ " Space for searching map / " Escape normal mode by inoremap l " Search for selected test vnoremap * y/\V=escape(@",'/\') " Set wrap nnoremap W :set wrap! " Fast saving function! s:WriteOrEnterFileName() if !empty(expand('%')) | write! | else | call feedkeys(":w ") | endif endfunction nmap w :call WriteOrEnterFileName() " :W sudo saves the file " (useful for handling the permission-denied error) command! W execute 'w !sudo -S tee %' edit! " Quit nmap q :q nmap cq :cq " Remap in Quickfix, Cmdwin Location list augroup vimrc_CRfix au! autocmd BufReadPost quickfix nnoremap autocmd CmdwinEnter * nnoremap autocmd CmdwinEnter * nnoremap augroup END " In case ALT key is not working " execute "set =\e2" " execute "set =\e1" " execute "set =\e3" " execute "set =\e4" " execute "set =\e5" " execute "set =\e6" " execute "set =\e7" " execute "set =\e8" " execute "set =\e9" " execute "set =\e0" " execute "set =\ef" " execute "set =\eb" " execute "set =\ed" " execute "set =\el" " execute "set =\eh" " Spell nnoremap sp :set spell!:set spell? nnoremap ss ]s nnoremap S [s " Show full path by default nnoremap 1 " Translate by Google API vnoremap Tz :!trans -t zh-TW -b vnoremap Te :!trans -t en-US -b " }}} " WORKING_DIR ----------------{{{ let g:last_path = execute("pwd") augroup SaveLatestDir au! autocmd DirChangedPre * let g:last_path = split(execute('pwd'), "\n")[0] augroup END " Switch CWD to the directory of the open buffer nnoremap cd :cd %:p:h:pwd nnoremap cd :cd nnoremap cdg :call CdToGitRepo():pwd noremap :cd ..:pwd noremap :call InCaseCdToLatestDir() " Switch CWD to root git directory function! CdToGitRepo() let l:git_dir = finddir('.git', escape(expand('%:p:h'), ' ') . ';') let l:repo = fnameescape(fnamemodify(l:git_dir, ':h')) execute "cd" l:repo endfunction function! InCaseCdToLatestDir() try execute "norm! \" catch cd - pwd endtry endfunction " }}} " MOTION ----------------{{{ " j/k will move virtual lines (lines that wrap) noremap j (v:count == 0 ? 'gj' : 'j') noremap k (v:count == 0 ? 'gk' : 'k') " Quick move in a line noremap 30h noremap 30l " File under the cursor nnoremap gf nnoremap gF :e " READLINE_FEATURES ----------------{{{ inoremap inoremap inoremap 0 inoremap $ inoremap inoremap inoremap inoremap inoremap inoremap set cedit= cnoremap cnoremap cnoremap cnoremap cnoremap cnoremap cnoremap " cnoremap cnoremap cnoremap cnoremap cnoremap d$ cnoremap de " Moving with wrap noremap gj noremap gk inoremap gj inoremap gk " }}} " JUMP_TO_TABS_WITH_ALT ----------------{{{ noremap :tabn 1 noremap :tabn 2 noremap :tabn 3 noremap :tabn 4 noremap :tabn 5 noremap :tabn 6 noremap :tabn 7 noremap :tabn 8 noremap :tablast inoremap :tabn 1 inoremap :tabn 2 inoremap :tabn 3 inoremap :tabn 4 inoremap :tabn 5 inoremap :tabn 6 inoremap :tabn 7 inoremap :tabn 8 inoremap :tablast " }}} " }}} " REGISTER {{{ " Paste register 0 nnoremap "0p " Toggle paste mode on and off map pp :setlocal paste! " Copy from system clipboard nnoremap P :r !xsel -ob vnoremap Y "+y " }}} " EDIT {{{ " Move one line up and down nnoremap ddp nnoremap ddkP " Clear current line nnoremap S S " }}} " MANAGE_VIMRC ----------------{{{ " source .vimrc nnoremap so V:so nnoremap so :source ~/.vimrc vnoremap so :source autocmd! BUFWRITEPOST $MYVIMRC source $MYVIMRC " Find scripts nnoremap e :scriptnames nnoremap ee :edit $MYVIMRC " }}} " MANAGE_BUFFERS ----------------{{{ " Set options noremap st :set noremap ft :e'set filetype='..&filetype noremap li :set list! noremap sw :e'set shiftwidth='..&shiftwidth noremap nu :set number! noremap ru :set relativenumber! " Open a new buffer nmap B :enew nmap O :e /tmp/buffer " Let l toggle between this and the last accessed buffer augroup SaveLastBuffer let g:lastbuffer = 1 au BufLeave * let g:lastbuffer = bufnr() augroup END noremap l :exe "buffer ".g:lastbuffer " Use Ctrl-C for buffer delete or quit vim ----------------{{{ " Toggle behavior for the last buffer in the last window let g:quitVimWhenPressingCtrlC = 1 function! ToggleQuit() let g:quitVimWhenPressingCtrlC = g:quitVimWhenPressingCtrlC ? 0 : 1 let message = g:quitVimWhenPressingCtrlC ? "Unlock" : "Lock" echo message endfunction nnoremap gl :call ToggleQuit() function! CloseBufferSafely() if &modified let answer = confirm("Save changes?", "&Yes\n&No\n&Cancel") if answer == 1 | write | endif if answer == 3 | return | endif if answer == "" | return | endif endif let bufs = getbufinfo({'buflisted': 1}) if len(bufs) == 1 bdelete! else b# | bd! # endif endfunction func! QuitWithCheck() if g:quitVimWhenPressingCtrlC silent! quit else echo "Press gl to allow quit with " endif endfunc function! Bye() let windows = gettabinfo(tabpagenr())[0]['windows'] try let bufs = gettabinfo(tabpagenr())[0]['variables']['bufs'] catch let bufs = getbufinfo({'buflisted': 1}) endtry if len(windows) == 1 && len(bufs) == 1 call QuitWithCheck() elseif &diff silent call CloseBuffersForDiff() elseif len(windows) >1 quit else silent! call CloseBufferSafely() endif endfunction nnoremap :call Bye() " Diff Mode ---------------- function! CloseBuffersForDiff() windo | if &diff && &buftype == "nofile" | bdelete | endif norm! zv endfunction command! DiffOrig vert new | set buftype=nofile nobuflisted | read ++edit # | 0d_ \ | diffthis | wincmd p | diffthis " Uset d to toggle Diff mode function! s:SwitchDiff() if &diff call CloseBuffersForDiff() else DiffOrig endif endfunction com! SwitchDiff call s:SwitchDiff() nnoremap d silent! SwitchDiff function! s:SwitchDiffForGitHEAD() norm cdg if &diff windo | if &buftype == "nofile" | bdelete | endif norm! zv else vert new | set buftype=nofile nobuflisted read !git show HEAD:# 0d_ | diffthis | wincmd p | diffthis endif endfunction command! SwitchDiffForGitHEAD call s:SwitchDiffForGitHEAD() nnoremap D silent! SwitchDiffForGitHEAD " }}} " }}} " MANAGE_WINDOWS ----------------{{{ nnoremap sb :windo set scrollbind! " 窗口切换:ALT+SHIFT+hjkl " 传统的 CTRL+hjkl 移动窗口不适用于 vim 8.1 的终端模式,CTRL+hjkl 在 " bash/zsh 及带文本界面的程序中都是重要键位需要保留,不能 tnoremap 的 "---------------------------------------------------------------------- noremap h noremap l noremap j noremap k inoremap h inoremap l inoremap j inoremap k if has('terminal') && exists(':terminal') == 2 && has('patch-8.1.1') " vim 8.1 支持 termwinkey ,不需要把 terminal 切换成 normal 模式 " 设置 termwinkey 为 CTRL 加减号(GVIM),有些终端下是 CTRL+? " 后面四个键位是搭配 termwinkey 的,如果 termwinkey 更改,也要改 set termwinkey= tnoremap h tnoremap l tnoremap j tnoremap k tnoremap elseif has('nvim') " neovim 没有 termwinkey 支持,必须把 terminal 切换回 normal 模式 tnoremap h tnoremap l tnoremap j tnoremap k tnoremap endif " }}} " MANAGE_TABS ----------------{{{ " Useful mappings for managing tabs map tn :tabnew map tc :tabclose map tm :tabmove map to :tabonly noremap :call Tab_MoveLeft() noremap :call Tab_MoveRight() " Let tl toggle between this and the last accessed tab let g:lasttab = 1 nmap tl :exe "tabn ".g:lasttab autocmd TabLeave * let g:lasttab = tabpagenr() " Opens a new tab with the current buffer's path " Super useful when editing files in the same directory map te :tabedit =expand("%:p:h") " Tab move functions function! Tvab_MoveLeft() let l:tabnr = tabpagenr() - 2 if l:tabnr >= 0 exec 'tabmove '.l:tabnr endif endfunc function! Tab_MoveRight() let l:tabnr = tabpagenr() + 1 if l:tabnr <= tabpagenr('$') exec 'tabmove '.l:tabnr endif endfunc " }}} " FOLD ----------------{{{ " Set fold options noremap fm :e'set foldmethod='..&foldmethod noremap fc :e'set foldcolumn='..&foldcolumn nnoremap zi zizz " Show fold level when it changes nnoremap zm zm:set foldlevel nnoremap zr zr:set foldlevel " Fold all except selection vnoremap zF :call ToggleUnfoldSelection() " Resume nnoremap zF :call ToggleUnfoldSelection()zv nnoremap \z :call GrayOutOtherFolds() " Select current fold xnoremap az :silent! keepjumps normal![zV]z xnoremap iz :silent! keepjumps normal![zjV]zk " Use l to open fold nnoremap l foldclosed('.') == -1 ? 'l' : 'zo' " Open fold in next line nnoremap zo foldclosed('.') == -1 ? 'zjzo' : 'zo' nnoremap zO foldclosed('.') == -1 ? 'zjzO' : 'zO' " Go to next fold and unfold nnoremap zJ zjzx nnoremap zK zkzx " Fold file except selection autocmd BufEnter * let b:unfold_selection = 0 function! ToggleUnfoldSelection() if !b:unfold_selection let b:unfold_selection = 1 mkview echo 'Unfold'..&foldmethod let &foldmethod = "manual" norm! zE execute "0,'<-1fold" execute "'>+1,$fold" else let b:unfold_selection = 0 loadview endif endfunction autocmd BufEnter * let b:clear_matches = 0 function! GrayOutOtherFolds() if b:clear_matches let b:clear_matches = 0 call clearmatches() else let b:clear_matches = 1 let pos = getpos('.') exe "norm! [zV]z\" call matchadd('Folded', '\%<'.line("'<").'l') call matchadd('Folded', '\%>'.line("'>").'l') norm! zR call setpos('.', pos) endif endfunction " }}} " HIGHLIGHT ----------------{{{ " Disable highlight when is pressed noremap :noh function! HiFile() let i = 1 while i <= line("$") if strlen(getline(i)) > 0 && len(split(getline(i))) > 2 let w = split(getline(i))[0] let l:command = "syn match " . w . " /^" . w . "\\s\\+xxx/" exe l:command endif let i += 1 endwhile endfunction function! GetHighlightGroupName() let l:syntaxID = synID(line('.'), col('.'), 1) let l:groupName = synIDattr(l:syntaxID, 'name') echo l:groupName endfunction nnoremap H :call GetHighlightGroupName() " Persist visualized lines " define line highlight color highlight MultiLineHighlight ctermbg=LightYellow guibg=LightYellow ctermfg=Black guifg=Black " highlight the current line nnoremap gh :call matchadd('MultiLineHighlight', '\%'.line('.').'l') " clear all the highlighted lines nnoremap gH :call clearmatches() " }}} " SURROUND ----------------{{{ inoremap ' '' inoremap " "" inoremap ( () inoremap [ [] inoremap { {} vnoremap S sa vnoremap ' ``>la' vnoremap q ``>la" vnoremap ( ``>la) vnoremap [ ``>la] vnoremap { ``>la} vnoremap ` ``>la` vnoremap Q ``>la」 function! AddSpaceForSelection() " If visual selection by lines, add empty space at top and bottom if line("'<") != line("'>") || (col("'<") == 1 && col("'>") == len(getline('.'))+1) '< norm! O '> norm! o exe "norm! "..(line("'<")-1).."GV"..(line("'>")+1).."G" " Otherwise, add space at start and end column else call cursor('.', col("'<")) execute "norm! i\" call cursor('.', col("'>")+1) execute "norm! a\" endif endfunction vnoremap :call AddSpaceForSelection() " }}} " QUICKFIX ----------------{{{ nnoremap cn :cn nnoremap cp :cp nnoremap cw :cw 10 " }}} " REDIRECTION_WITH_BUFFER ----------------{{{ " Usage: " :Redir hi ............. show the full output of command ':hi' in a scratch window " :Redir !ls -al ........ show the full output of command ':!ls -al' in a scratch window " function! Redir(cmd) if a:cmd =~ '^!' let output = system(matchstr(a:cmd, '^!\zs.*')) else redir => output execute a:cmd redir END endif enew let w:scratch = 1 setlocal buftype=nofile noswapfile call setline(1, split(output, "\n")) endfunction command! -nargs=1 -complete=command Redir silent call Redir() command! -nargs=1 -complete=command R silent call Redir() nnoremap rr :Redir " }}} " QUICK_SUBSTITUTE ----------------{{{ " Usage: Press n times for area, and for substitute " substitute across file vnoremap s y:%s//0/g let g:search_not_in_register = 1 " When leaving visual mode, resume search_not_in_register autocmd Modechanged [vV\x16]*:* let g:search_not_in_register = 1 function! ExpandSelectionBySearch(sep) if g:search_not_in_register " Save current selection to register, and keep selection norm! ygv let g:search_not_in_register = 0 endif " Use register s to go to next search, counts/total is displayed in " statusline call feedkeys(a:sep.."\0"..a:sep.."e\") endfunction function! SubstituteBySearch() " Apply current search for default substitute text call feedkeys(":s//\0/g\\") endfunction vnoremap call ExpandSelectionBySearch('/') vnoremap call ExpandSelectionBySearch('?') vnoremap call SubstituteBySearch() " }}} " SIGN ----------------{{{ nnoremap si :exe ":sign place " .. line('.') .. " line=" .. line('.') .. " name=piet file=" .. expand("%:p") nnoremap sI :exe ":sign unplace * file=" .. expand("%:p") " }}} " GIT_TIG ----------------{{{ let g:tig_explorer_keymap_commit_split = '' let g:tig_explorer_keymap_commit_vsplit = '' nnoremap Tig nnoremap s TigStatus nnoremap b TigBlame " }}} " Tmp: Markdown items (temproray solution) ----------------{{{ " Toggle list item in markdown: "- [ ] XXX" -> "XXX" -> "- XXX" -> "- [ ] XXX" " autocmd FileType markdown nnoremap i V:!sed -E '/^ *- \[.\]/ { s/^( *)- \[.\] */\1/; q; }; /^ *[^[:space:]-]/ { s/^( *)/\1- /; q; }; /^ *- / { s/^( *)- /\1- [ ] /; q; }' " autocmd FileType markdown nnoremap I V:!sed -E 's/^( *)/\1- [ ] /' " Toggle task status: "- [ ] " -> "- [x]" -> "- [.] " -> "- [ ] " " nnoremap x V:!sed -E '/^ *- \[ \]/ { s/^( *)- \[ \]/\1- [x]/; q; }; /^ *- \[\x\]/ { s/^( *)- \[\x\]/\1- [.]/; q; }; /^ *- \[\.\]/ { s/^( *)- \[\.\]/\1- [ ]/; q; }' " }}} " Tmp: Common system command ----------------{{{ " Show date selector nnoremap dd :r !sh -c 'LANG=en zenity --calendar --date-format="\%Y.\%m.\%d" 2>/dev/null' nnoremap dD :r !sh -c 'LANG=en zenity --calendar --date-format="\%a \%b \%d" 2>/dev/null' nnoremap dt :r !date +\%H:\%mA " }}} " Tmp: Compile ----------------{{{ " 编译运行 C/C++ 项目 " 详细见:http://www.skywind.me/blog/archives/2084 "---------------------------------------------------------------------- " 自动打开 quickfix window ,高度为 6 let g:asyncrun_open = 6 " 任务结束时候响铃提醒 let g:asyncrun_bell = 1 " 设置 F10 打开/关闭 Quickfix 窗口 nnoremap :call asyncrun#quickfix_toggle(6) " F9 编译 C/C++ 文件 nnoremap :AsyncRun gcc -Wall -O2 "$(VIM_FILEPATH)" -o "$(VIM_FILEDIR)/$(VIM_FILENOEXT)" " F5 运行文件 nnoremap :call ExecuteFile() " F7 编译项目 nnoremap :AsyncRun -cwd= make " F8 运行项目 nnoremap :AsyncRun -cwd= -raw make run " F6 测试项目 nnoremap :AsyncRun -cwd= -raw make test " 更新 cmake nnoremap :AsyncRun -cwd= cmake . " Windows 下支持直接打开新 cmd 窗口运行 if has('win32') || has('win64') nnoremap :AsyncRun -cwd= -mode=4 make run endif " F5 运行当前文件:根据文件类型判断方法,并且输出到 quickfix 窗口 "---------------------------------------------------------------------- function! ExecuteFile() let cmd = '' if index(['c', 'cpp', 'rs', 'go'], &ft) >= 0 " native 语言,把当前文件名去掉扩展名后作为可执行运行 " 写全路径名是因为后面 -cwd=? 会改变运行时的当前路径,所以写全路径 " 加双引号是为了避免路径中包含空格 let cmd = '"$(VIM_FILEDIR)/$(VIM_FILENOEXT)"' elseif &ft == 'python' let $PYTHONUNBUFFERED=1 " 关闭 python 缓存,实时看到输出 let cmd = 'python "$(VIM_FILEPATH)"' elseif &ft == 'javascript' let cmd = 'node "$(VIM_FILEPATH)"' elseif &ft == 'perl' let cmd = 'perl "$(VIM_FILEPATH)"' elseif &ft == 'ruby' let cmd = 'ruby "$(VIM_FILEPATH)"' elseif &ft == 'php' let cmd = 'php "$(VIM_FILEPATH)"' elseif &ft == 'lua' let cmd = 'lua "$(VIM_FILEPATH)"' elseif &ft == 'zsh' let cmd = 'zsh "$(VIM_FILEPATH)"' elseif &ft == 'ps1' let cmd = 'powershell -file "$(VIM_FILEPATH)"' elseif &ft == 'vbs' let cmd = 'cscript -nologo "$(VIM_FILEPATH)"' elseif &ft == 'sh' let cmd = 'bash "$(VIM_FILEPATH)"' else return endif " Windows 下打开新的窗口 (-mode=4) 运行程序,其他系统在 quickfix 运行 " -raw: 输出内容直接显示到 quickfix window 不匹配 errorformat " -save=2: 保存所有改动过的文件 " -cwd=$(VIM_FILEDIR): 运行初始化目录为文件所在目录 if has('win32') || has('win64') exec 'AsyncRun -cwd=$(VIM_FILEDIR) -raw -save=2 -mode=4 '. cmd else exec 'AsyncRun -cwd=$(VIM_FILEDIR) -raw -save=2 -mode=0 '. cmd endif endfunc " F2 在项目目录下 Grep 光标下单词,默认 C/C++/Py/Js ,扩展名自己扩充 " 支持 rg/grep/findstr ,其他类型可以自己扩充 " 不是在当前目录 grep,而是会去到当前文件所属的项目目录 project root " 下面进行 grep,这样能方便的对相关项目进行搜索 "---------------------------------------------------------------------- if executable('rg') noremap :AsyncRun! -cwd= rg -n --no-heading \ --color never -g *.h -g *.c* -g *.py -g *.js -g *.vim \ "" elseif has('win32') || has('win64') noremap :AsyncRun! -cwd= findstr /n /s /C:"" \ "\%CD\%\*.h" "\%CD\%\*.c*" "\%CD\%\*.py" "\%CD\%\*.js" \ "\%CD\%\*.vim" \ else noremap :AsyncRun! -cwd= grep -n -s -R \ --include='*.h' --include='*.c*' --include='*.py' \ --include='*.js' --include='*.vim' \ '' endif " }}}