"====================================================================== " Only for key mapping "====================================================================== " vim: sw=2 ts=2 foldmethod=marker foldmarker={{{,}}} " COMMON_MAPPING {{{ " Space for searching map / " Escape normal mode by inoremap l " Set wrap nnoremap \w :set wrap! " Fast saving function! s:WriteOrEnterFileName() if !empty(expand('%')) | write! | else | call feedkeys(":w ") | endif endfunction nnoremap w :call WriteOrEnterFileName() " :W sudo saves the file " (useful for handling the permission-denied error) command! W execute 'w !sudo -S tee %' edit! " Quit nnoremap q :q nnoremap cq :cq " Remap in Quickfix, Cmdwin Location list augroup vimrc_CRfix au! autocmd BufReadPost quickfix nnoremap autocmd CmdwinEnter * nnoremap autocmd CmdwinEnter * nnoremap augroup END " Spell nnoremap \s :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 let g:alacritty_extra_padding = 0 function! ToggleWinPadding() if g:alacritty_extra_padding !alacritty msg config --window-id $WINDOWID --reset else redir => output | hi SignColumn | redir END let bg_color = matchstr(output, 'guibg=\zs[^\s]\+\ze') exe "!alacritty msg config --window-id $WINDOWID window.padding.x=300 'colors.primary.background=\"\\"..bg_color.."\"'" endif let g:alacritty_extra_padding = !g:alacritty_extra_padding endfunc nnoremap z silent call ToggleWinPadding() " }}} " 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 nnoremap :cd ..:pwd nnoremap :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) nnoremap j (v:count == 0 ? 'gj' : 'j') nnoremap k (v:count == 0 ? 'gk' : 'k') " Quick move in a line nnoremap 30h nnoremap 30l " File under the cursor nnoremap gf nnoremap gF :e xnoremap iq i" xnoremap aq a" " 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 nnoremap gj nnoremap gk inoremap gj inoremap gk " }}} " JUMP_TO_TABS_WITH_ALT {{{ nnoremap :tabn 1 nnoremap :tabn 2 nnoremap :tabn 3 nnoremap :tabn 4 nnoremap :tabn 5 nnoremap :tabn 6 nnoremap :tabn 7 nnoremap :tabn 8 nnoremap :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 gp "+p vnoremap Y "+y " }}} " MARKS {{{ " Delete mark function! DeleteMark(mark) let mark = nr2char(a:mark) if mark =~ '[a-z]' execute "delmarks " . mark endif endfunction nnoremap dm :call DeleteMark(getchar()) "}}} " EDIT {{{ " Move one line up and down nnoremap ddp nnoremap ddkP " Clear current line nnoremap S S " }}} " TERMINAL {{{ " 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" "}}} " MANAGE_VIMRC {{{ " source .vimrc nnoremap so V:so nnoremap so :source % vnoremap so :source autocmd! BUFWRITEPOST $MYVIMRC source $MYVIMRC " Find scripts nnoremap e :scriptnames nnoremap ee :edit $MYVIMRC " }}} " MANAGE_BUFFERS {{{ " Set options nnoremap so :set nnoremap ft :e'set filetype='..&filetype nnoremap sw :e'set shiftwidth='..&shiftwidth nnoremap ts :e'set tabstop='..&tabstop nnoremap \e :set expandtab!:set expandtab? nnoremap \l :set list!:set list? nnoremap \n :set nu!:set nu? nnoremap \r :set relativenumber!:set rnu? " Open a new buffer nnoremap B :enew nnoremap O :e /tmp/buffer " Let l toggle between this and the last accessed buffer augroup SaveLastBuffer let g:lastbuffer = 1 au BufLeave * if &buflisted | let g:lastbuffer = expand('') | endif augroup END nnoremap 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 let message = g:quitVimWhenPressingCtrlC ? "Unlock" : "Lock" echo message endfunction nnoremap \q :call ToggleQuit() func! QuitWithCheck() if g:quitVimWhenPressingCtrlC silent! quit else echo "Press \\q to allow quit with " endif endfunc function! CloseBufferSafely() " Ask Saving 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 l:bufnr = bufnr() if len(t:bufs) == 1 " Close tab for last buffer tabclose else " Switch to proper buffer let l:next_buf = get(t:bufs, bufnr('#')) ? bufnr('#') : filter(t:bufs, 'v:val != '..l:bufnr)[0] exe "b "..l:next_buf call filter(t:bufs, 'v:val != '..l:bufnr) endif " Delete buffer if every t:buf doesn't have it for tab in gettabinfo() if get(tab.variables.bufs, l:bufnr) | return | endif endfor exe "bd! "..l:bufnr endfunction function! Bye() let windows = gettabinfo(tabpagenr())[0]['windows'] if len(t:bufs) <= 1 && len(windows) == 1 call QuitWithCheck() elseif &diff silent call CloseBuffersForDiff() elseif len(windows) >1 quit else call CloseBufferSafely() " 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 的 "---------------------------------------------------------------------- nnoremap h nnoremap l nnoremap j nnoremap 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 nnoremap :call Tab_MoveLeft() nnoremap :call Tab_MoveRight() " Let tl toggle between this and the last accessed tab let g:lasttab = 1 nnoremap 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 nnoremap fm :e'set foldmethod='..&foldmethod nnoremap fc :e'set foldcolumn='..&foldcolumn nnoremap zi "zizz:silent set foldcolumn="..(&foldenable ? "0" : "auto:3").."\" " 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 onoremap az :silent! keepjumps normal![zV]z xnoremap az :silent! keepjumps normal![zV]z onoremap iz :silent! keepjumps normal![zjV]zk 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 nnoremap :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 ' ``>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 " }}} " SEARCH/SUBSTITUTE {{{ " Search for selected test vnoremap * y/\V=escape(@",'/\') nnoremap g/ gv/\%V vnoremap g/ /\%V " Substitue across file vnoremap s y:%s//0/g " Usage: Press n times for area, and for substitute let g:search_selection = 0 " When leaving visual mode, resume search_selection autocmd Modechanged [vV\x16]*:* let g:search_selection = 0 xmap g:search_selection ? "//e" : "*:let g:search_selection = 1gv//e" xmap g:search_selection ? "??" : "*:let g:search_selection = 1gv??" vnoremap :s//0/g " }}} " SIGN {{{ nnoremap sc :e'set signcolumn='..&signcolumn 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 " }}}