"====================================================================== " Only for key mapping "====================================================================== " vim: sw=2 ts=2 foldmethod=marker foldmarker={{{,}}} " COMMON {{{ " Space for searching map / " Fast saving function! s:WriteOrEnterFileName() if !empty(bufname('%')) | write! | else | call feedkeys(":w ") | endif endfunc 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 \\sp :set spell!:set spell? " 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 nnoremap q: : " }}} " WORKING_DIR {{{ let g:last_path = execute("pwd") if has('nvim') augroup SaveLatestDir au! autocmd DirChangedPre * let g:last_path = split(execute('pwd'), "\n")[0] augroup END endif " 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 vnoremap 30h nnoremap 30l vnoremap 30l " File under the cursor nnoremap gF :e xnoremap iq i" xnoremap aq a" nnoremap ze zszH nnoremap 0 ^ nnoremap ^ 0 " READLINE {{{ 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 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 " }}} " }}} " EDIT {{{ " Escape normal mode by inoremap l " Set wrap nnoremap \w :set wrap!:set wrap? " Set line numbers nnoremap \n :set nu!:set nu? nnoremap \r :set relativenumber!:set rnu? nnoremap \l :set list!:set list? " Set options for indent nnoremap sw :e'set shiftwidth='..&shiftwidth nnoremap ts :e'set tabstop='..&tabstop " Set text width for auto wrapping nnoremap tw :set fo+=t:e'set tw='..&tw " Set columns nnoremap co :e'set columns='..&co " Move one line up and down nnoremap ddp nnoremap ddkP " Clear current line nnoremap S S " }}} " SURROUND {{{ inoremap ' '' inoremap " "" inoremap ( () inoremap [ [] inoremap { {} vnoremap q ``>la" vnoremap ( ``>la) vnoremap [ ``>la] vnoremap { ``>la} vnoremap Q ``>la」 " vnoremap ' ``>la' " vnoremap ` ``>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() " }}} " SEARCH/SUBSTITUTE {{{ " Search for selected test vnoremap * y/\V=escape(@",'/\') nnoremap g/ gv/\%V vnoremap / /\%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") " }}} " FOLD {{{ " Set fold options nnoremap fm :e'set foldmethod='..&foldmethod nnoremap fc :e'set foldcolumn='..&foldcolumn " Toggle fold and 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 " 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 "1,'<-1fold" execute "'>+1,$fold" else let b:unfold_selection = 0 loadview endif endfunction autocmd BufEnter * let b:clear_matches = 0 function! GrayOutOthers() if b:clear_matches let b:clear_matches = 0 call clearmatches() else let b:clear_matches = 1 let pos = getpos('.') call matchadd('Folded', '\%<'.line("'<").'l') call matchadd('Folded', '\%>'.line("'>").'l') norm! zR call setpos('.', pos) endif endfunction vnoremap \z :call GrayOutOthers() nnoremap \z :call GrayOutOthers() " }}} " REGISTER {{{ " Paste register 0 nnoremap "0p " Toggle paste mode on and off nnoremap p :setlocal paste! " Copy from system clipboard nnoremap gp "+p vnoremap Y "+y " }}} " MARKS {{{ " Delete mark function! DeleteMark(mark) let mark = nr2char(a:mark) echo mark if mark =~ '\a' execute "delmarks " . mark endif endfunc nnoremap dm :call DeleteMark(getchar()) " Usage: z' to fold lines not near marks, use v:count to set offset " For example: 15z' autocmd BufEnter * let b:fold_for_marks = 0 let g:mark_offset = 5 function! ToggleFoldForMarks(offset) if !b:fold_for_marks || a:offset " If toggling from other foldmethod, save view! if !b:fold_for_marks mkview setlocal foldmethod=manual endif " Then clear all folds norm! zEgg " Get list of lines which has mark let line_list = [] for info in getmarklist(bufnr()) if info.mark =~ "[a-z]" call add(line_list, info.pos[1]) endif endfor call uniq(sort(line_list, 'n')) " Create folds not inside offset of marks let offset = a:offset ? a:offset : g:mark_offset for line in line_list let foldstart = line('.') let line_upper = line - offset let line_lower = line + offset if foldstart < (line_upper - 1) exe foldstart..","..(line_upper-1).." fold" endif " Move cursor outside of lower offset exe (line_lower + 1) endfor " Fold lower offset to end of file if line('.') < line('$') norm! zfG endif let b:fold_for_marks = 1 echo "Folds for Marks" else " Reset everything loadview let b:fold_for_marks = 0 echo "Reset Folds" endif endfunction nnoremap z' ":\call ToggleFoldForMarks("..v:count..")\" function! ChangeUnfold(downward, count) " Only do this if foldmethod is manual if &foldmethod != 'manual' | return | endif " If count is not given, reverse direction let downward = a:count ? a:downward : !a:downward let move = a:count ? a:count : -1 " Move to fold upward/downward if downward norm! zj else norm! zk endif let foldstart = foldclosed('.') let foldend = foldclosedend('.') " Change folding area if downward let foldstart += move else let foldend -= move endif norm! zd try | silent! exe foldstart..","..foldend.."fold" | endtry " Get back to origin cursor position norm! '' endfunc nnoremap z> ":\call ChangeUnfold(1,"..v:count..")\" nnoremap z< ":\call ChangeUnfold(0,"..v:count..")\" "}}} " MANAGE_SCRIPTS {{{ " source .vimrc nnoremap so V:so nnoremap so :source % vnoremap so :source autocmd! BUFWRITEPOST $MYVIMRC source $MYVIMRC " Find scripts nnoremap ee :scriptnames nnoremap e :edit $MYVIMRC " }}} " MANAGE_BUFFERS {{{ " Set options nnoremap so :set nnoremap ft :e'set filetype='..&filetype nnoremap \E :set expandtab!:set expandtab? nnoremap \e :call ToggleEventIgnore() function! ToggleEventIgnore() let operator = empty(&eventignore) ? "+=" : "-=" exe "set eventignore".operator.'all' set eventignore? endfunc " 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" nnoremap ZZ :w 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 | call s:WriteOrEnterFileName() | endif if answer == 3 | return | endif if answer == "" | return | endif endif let bufnr = bufnr() if !has_key(t:, 'bufs') || len(t:bufs) <= 1 " Close tab for last buffer tabclose else " Switch to proper buffer let next_buf = filter(t:bufs, 'v:val != '..bufnr)[-1] exe "b "..next_buf " exe "buffer ".g:lastbuffer call filter(t:bufs, 'v:val != '..bufnr) endif " Remove unnamed buffer if empty(bufname(bufnr)) | silent! exe 'bd! '.bufnr | endif endfunction function! Bye() let windows = gettabinfo(tabpagenr())[0]['windows'] if len(gettabinfo()) == 1 && 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 nnoremap tn :tabnew nnoremap tc :tabclose nnoremap tm :tabmove nnoremap 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 nnoremap 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 " }}} " TERMINAL {{{ " Use z to toggle window padding for alacritty let g:alacritty_extra_padding = 0 function! ToggleWinPadding(occupy) if g:alacritty_extra_padding && !a:occupy !alacritty msg config --window-id $WINDOWID --reset call SetEmulaterBackground() hi EndOfBuffer None hi MsgArea None else redir => output | hi LineNr | redir END let bg_color = matchstr(output, 'guibg=\zs[^\s]\+\ze') if empty(bg_color) | let bg_color = "#14161b" | endif redir => win_width_str !xdotool getactivewindow getwindowgeometry --shell | grep WIDTH | sed s/WIDTH=// redir END let win_width = str2nr(matchstr(win_width_str, '[0-9]\+')) let padding = a:occupy ? (100-a:occupy) / 2 * win_width / 100 : 15 * win_width / 100 try exe "hi EndOfBuffer guifg="..bg_color.." guibg="..bg_color exe "hi MsgArea guibg="..bg_color endtry exe "!alacritty msg config --window-id $WINDOWID window.padding.x=" . padding . " 'colors.primary.background=\"\\"..bg_color.."\"'" endif let g:alacritty_extra_padding = !g:alacritty_extra_padding endfunc nnoremap z ":\ silent call ToggleWinPadding(" . v:count . ")\" " 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" "}}} " HIGHLIGHT {{{ nnoremap I :Inspect nnoremap \s exists("g:syntax_on") ? ":syntax off " : ":syntax enable" " Toggle conceallevel0/2 nnoremap \c ":set conceallevel="..(&cole ? 0 : 2)..":set cole?" " 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() " }}} " QUICKFIX {{{ nnoremap cn :cn nnoremap cp :cp nnoremap cw :cw 10 " }}} " REDIRECTION {{{ " 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 try | execute a:cmd | catch | return | redir END | endtry 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() nnoremap rr :Redir " Print Runtimepath nnoremap rtp :Redir echo &rtp:s/,/\r/g " }}} " GIT_TIG {{{ let g:tig_explorer_keymap_commit_split = '' let g:tig_explorer_keymap_commit_vsplit = '' nnoremap silent! Tig nnoremap s silent! TigStatus nnoremap b silent! TigBlame " }}} " Tmp: Common system command {{{ nnoremap ! :r ! " 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 " }}}