" @wookayin's vimrc file " https://dotfiles.wook.kr/vim/vimrc " " https://github.com/wookayin/dotfiles scriptencoding utf-8 " Allow lazy execution of plugin inits (after VimEnter and then UI initializes) autocmd! VimEnter autocmd! User LazyInit if exists("*timer_start") && exists('*nvim_command') " neovim autocmd! VimEnter * call timer_start(0, { -> nvim_command('doautocmd User LazyInit') }) else " No timer support, this will run during VimEnter (before UI draws) autocmd! VimEnter * call execute('doautocmd User LazyInit') endif """"""""""""""""""""""""""""""""""""""""" " 0. Load Plugin {{{ """"""""""""""""""""""""""""""""""""""""" let g:plugs = {} if !has('nvim') && filereadable(expand('$HOME/.vim/plugins.vim')) " Note: for neovim, we use lazy.vim in place of vim-plug. source $HOME/.vim/plugins.vim endif " Functions to be executed for each plugin BEFORE the plugin loads. let g:PlugConfig = {} " }}} """"""""""""""""""""""""""""""""""""""""" " 1. General Settings {{{ """"""""""""""""""""""""""""""""""""""""" filetype plugin on filetype indent on if !has('nvim') && !exists('g:syntax_on') " Use syntax. Note: neovim automatically turns syntax on *AFTER* sourcing init.lua is complete " (see :startup, step 9), so there's no need to turn it on for neovim. Actually, the order matters; " `syntax on` should NOT be called when some syntax plugins are lazy-loaded (folke/lazy.nvim#775). " See $VIMRUNTIME/syntax/synload.vim and $VIMRUNTIME/syntax/syntax.vim syntax on let g:syntax_on = 1 end set nocompatible if !empty($SHELL) && filereadable($SHELL) let &g:shell=$SHELL elseif filereadable('/bin/zsh') set shell=/bin/zsh else set shell=/bin/bash endif " use path '~/.vim' even on non-unix machine set runtimepath+=~/.vim " Add ~/.local/bin and ~/.dotfiles/bin to $PATH even if haven't (e.g., using bash shell) " See ~/.zshenv for a proper $PATH configuration. function! s:ensure_PATH(dir) abort let l:dir = expand(a:dir) if match(":" . $PATH . ":", ":" . l:dir, ":") < 0 let $PATH = l:dir . ":" . $PATH endif endfunction call s:ensure_PATH('~/.local/bin') call s:ensure_PATH('~/.dotfiles/bin') " load plugins with pathogen try runtime bundle/vim-pathogen/autoload/pathogen.vim call pathogen#infect() catch endtry " basic displays set number " show line numbers set ruler " input settings set backspace=indent,eol,start " allow backspaces over everything set autoindent set smartindent set pastetoggle= set nowrap set textwidth=0 " disable automatic line breaking set cursorline " tab settings if has('vim_starting') set tabstop=4 set shiftwidth=4 set softtabstop=4 set shiftround endif " tab navigation set showtabline=2 " always show tab pannel set scrolloff=3 set sidescrolloff=3 if has('nvim') augroup TermScrollFix autocmd! " Disable scrolloff in terminal buffers (see neovim/neovim#11915) autocmd TermOpen,TermEnter * setlocal scrolloff=0 sidescrolloff=0 autocmd TermOpen * setlocal signcolumn=no augroup end endif " search set ignorecase " case-insensitive by default set smartcase " case-sensitive if keyword contains both uppercase and lowercase set incsearch set hlsearch if has('nvim') " live preview of substitute command, with a split window " @seealso http://vimcasts.org/episodes/neovim-eyecandy/ set inccommand=split endif " more sensible jump behavior if has('nvim-0.8') " Use browser-style (or 'tagstack') navigation (see :help jumplist-stack) set jumpoptions+=stack " Preserve "view" such as relative cursor position to window (see :help mark-view) set jumpoptions+=view endif " Fold level: when starting, let all the folds be open set nofoldenable " When jumping to line, folds on the line should be opened (:help 'foldopen') set foldopen+=jump " foldcolumn display if has('nvim-0.5.0') set foldcolumn=auto endif " use spaces for tabbing, by default set expandtab " vertical splits set fillchars+=vert:│ " listchars for whitespaces set list set listchars=tab:»\ ,trail:·,extends:>,precedes:< augroup listchars_filetype autocmd! autocmd FileType GV setlocal listchars-=trail:· augroup END " wildmenu settings set wildmenu try " neovim 0.4.0+: popupmenu completion in the cmdline mode set wildoptions+=pum set wildmode=longest:full,full " make , , , work well with popupmenu in cmdline " (Note) this setting will be overriden later for wilder.nvim cnoremap pumvisible() ? "" : "" cnoremap pumvisible() ? "" : "" catch " old wildmenu heavior (vanilla vim) set wildmode=list,longest:full endtry set wildignore=*.swp,*.swo,*.class,*.pyc,__pycache__, " status line " Neovim(0.8+): lualine will override with laststatus=3 " see ~/.config/nvim/lua/config/statusline.lua if !has('nvim') set laststatus=2 " show anytime (legacy vim behavior) endif set noshowmode " don't display mode, e.g. '-- INSERT --' set showcmd " title: turn on (for tmux and terminal apps tab title) set title " customize the native statusline, just in case airline/lualine is not available " (this setting will be replaced afterwards by airline/lualine) if empty(&statusline) set statusline=%1*%{winnr()}\ %*%<\ %f\ %h%m%r%=%l,%c%V\ (%P) endif " mouse behaviour: in all modes (normal, visual, and insert) " Note: To temporarily disable mouse support, hold the shift or alt key while using the mouse. if has('mouse') set mouse=a endif if ! has('nvim') " vim only (not in neovim) set ttymouse=xterm2 endif " encoding and line ending settings if !has('nvim') set encoding=utf-8 endif set fileencodings=utf-8,cp949,latin1 set fileformats=unix,dos " split and autocomplete settings set splitbelow " :split opens window below (:belowright split), including preview windows set splitright " :vsplit opens window right (:belowright vsplit) set completeopt=menuone,preview,longest " show preview and pop-up menu if has('nvim-0.9') set splitkeep=screen " Keep the text on the same screen line. endif " Avoid 'Pattern not found' messages (nvim-compe, etc.) set shortmess+=c " No intro message when starting vim set shortmess+=I " no fucking swap and backup files set noswapfile set nobackup " dictionary if filereadable('/usr/share/dict/words') set dictionary+=/usr/share/dict/words endif " Retain more history (:, search strings, etc.) set history=10000 set undolevels=1000 if has('nvim') " ': Keep 10000 oldfiles (:h 'shada') " <: Maximum number of lines saved for each register. " default: set shada=!,'100,<50,s10,h set shada=!,'10000,<100,s10,h endif " shut up, vim set novisualbell set belloff=all " lazyredraw: no redrawing during macro execution " This should NOT be set during startup if < 0.9.1, see neovim/neovim#23534 if has('nvim') lua vim.defer_fn(function() vim.opt.lazyredraw = true; end, 0) else " vanilla vim set lazyredraw endif " the timeout value for CursorHold. set updatetime=1000 set matchpairs+=<:> " Note: the built-in plugin matchparen is very slow, " (especially Highlight_Matching_Pair) and block the UI. " We can use shorter timeout value (default is 300ms) let g:matchparen_timeout = 10 " when launching files via quickfix, FZF, or something else, " first switch to existing tab (if any) that contains the target buffer, " or open a new buffer by splitting window in the current tab otherwise. set switchbuf+=usetab,split " diff options (&diffopt) try " Ignore whitespaces set diffopt+=iwhite " Use more intuitive, semantically easy-to-parse diff algorithm if has('patch-8.1.0360') || has('nvim') set diffopt+=internal,algorithm:patience endif " Enhance diff result by line matching algorithm (neovim/neovim#14537) if has('nvim-0.9.0') set diffopt+=linematch:60 endif catch " ignore if diffopt unknown for legacy vimdiff endtry " jump to the last position when reopening a file if has('autocmd') let s:last_position_disable_filetypes = ['gitcommit'] au BufReadPost * if index(s:last_position_disable_filetypes, &ft) < 0 && line("'\"") > 1 && line("'\"") <= line("$") | exe "normal! g'\"zz" | endif endif " use strong encryption if ! has('nvim') if v:version >= 800 || v:version == 704 && has('patch399') set cryptmethod=blowfish2 " Requires >= 7.4.399 else set cryptmethod=blowfish endif endif " When opening http urls using netrw, follow 302 redirects let g:netrw_http_cmd = 'curl -L -o' " errorformat (for parsing compiler output into quickfix) " use "%t" (warning: ..., error: ...) so that errormarker.vim " can show diagnostics with proper level (e.g. show warnings as warning) let &errorformat="%f:%l:%c: %t%*[^:]:%m," . "%f:%l: %t%*[^:]:%m," . &errorformat " terminal mode (neovim) if has('nvim') au TermOpen * setlocal nonumber norelativenumber endif " Use nvr as git editor (inside neovim terminal) " nvr >= 2.5.0 required. " One exception: on a floaterm, nvr should not be used. However, " floaterm does not allow customization of environment variables. " As a workaround, we unset $GIT_EDITOR in ~/.zshenv. if has('nvim') && executable('nvr') let $GIT_EDITOR = 'nvr -cc split --remote-wait' autocmd FileType gitcommit set bufhidden=delete endif " For debugging function! ToggleVerbose() if !&verbose set verbosefile=~/.vim/verbose.log set verbose=15 else set verbose=0 set verbosefile= endif endfunction " :lua vim.notify / VimNotify if has('nvim') && luaeval('vim.notify == nil') " Backward compatibility for neovim < 0.5 " see nvim-notify plugin settings below lua vim.notify = function(msg, log_level, opts) print(msg) end endif if has('nvim') function! VimNotify(msg, ...) " Compatible with vanilla vim, neovim 0.4.x, and 0.5.0+ (native) " Same spec as v:lua.vim.notify(msg, log, opts). let l:log_level = a:0 >= 1 ? a:000[0] : 'info' let l:opts = a:0 >= 2 ? a:000[1] : {} return luaeval("vim.notify(_A[1], _A[2], _A[3])", [a:msg, l:log_level, l:opts]) endfunction else " fallback for vanilla vim function! VimNotify(msg, ...) echom a:msg endfunction endif command! -nargs=+ Notify :call VimNotify() command! -nargs=+ NotifyError :call VimNotify(, 'error') " }}} """"""""""""""""""""""""""""""""""""""""" " 2. Key and Functional Mappings {{{ """"""""""""""""""""""""""""""""""""""""" " the leader key " (NOTE) leader key is mapped to vim-which-key, see sections below let mapleader=',' " comma is the key. let maplocalleader=',' " comma : nnoremap : : nnoremap : inoremap stopinsertif &buftype == '' update endif nnoremap e ezR nnoremap Q nnoremap QQ ZQ " navigation key mapping noremap k gk noremap j gj sunmap k sunmap j nmap gk nmap gj inoremap gk inoremap gj noremap noremap " Ignore errornous input in Mac OS X imap " Workaround neovim bug (it freezes, why?) imap " invokes (omni-completion) inoremap inoremap " dd: do not yank an empty line into the default register if has('nvim-0.7') lua << EOF vim.keymap.set('n', 'dd', function() if vim.api.nvim_get_current_line():match("^%s*$") then return '"_dd' else return "dd" end end, { expr = true, desc = 'dd (do not yank empty line)'}) EOF endif " window navigation nnoremap h nnoremap j nnoremap k nnoremap l nnoremap h nnoremap j nnoremap k nnoremap l " window navigation: aware of tmux let g:tmux_navigator_no_mappings = 1 let g:PlugConfig['vim-tmux-navigator'] = 's:init_tmux_navigator' function! s:init_tmux_navigator() abort if !empty($TMUX) nnoremap TmuxNavigateLeft nnoremap TmuxNavigateDown nnoremap TmuxNavigateUp nnoremap TmuxNavigateRight endif endfunction " window resize (simply) nnoremap _ - nnoremap + + nnoremap > nnoremap < nnoremap - nnoremap + if exists(':tnoremap') tnoremap wincmd > tnoremap wincmd < tnoremap wincmd - tnoremap wincmd + endif " zoom and unzoom (like tmux) -- by 'vim-maximizer' nnoremap z MaximizerToggle let g:maximizer_set_default_mapping = 0 " do not map " Alt-R: toggle relative line number nnoremap setlocal relativenumber! " well...? inoremap " z toggles folding. " It will open the current fold recursively when fold is closed, " or close (the narrow one) when fold is open at the cursor. nnoremap z foldclosed(line(".")) >= 0 ? "zO" : "zc" function! s:safe_expand(arg) abort " When verbose is set, expand(""), etc. will throw errors. " See vim-patch:8.2.4740 (or neovim a9e6cf0e) try return expand(a:arg) catch " E495, E496, E497, E498, E1274, etc. return '' endtry endfunction " in terminal mode (neovim) if has('nvim') " is the key sequence for escaping form terminal mode " double and double also goes for escaping from terminal, " whereas single would be directly passed inside the terminal. tnoremap tnoremap " Automatically enter insert mode when entering neovim terminal buffer augroup terminal_autoinsert autocmd! " There is a bug (or just a strange behavior?) of neovim where buffer options " (e.g. &buftype) gets updated late on WinEnter autocmds. " When a new window is opened, the new buffer that will be opened in the " new window will take effect *after* the WinEnter autocmd. " The actual buffer would not yet have been loaded at the time when " autocmd WinEnter events are being executed. As a result, the new window " will enter the insert mode even if it's not having a terminal buffer. " One known solution is to check if is empty. " Credit: https://vi.stackexchange.com/questions/15966/strange-behaviour-of-autocmd autocmd WinEnter,BufWinEnter * \ if !empty(s:safe_expand('')) && (&buftype == 'terminal') \ | startinsert | endif autocmd TermOpen * \ if nvim_buf_get_name(0) =~# '^term://' \ | startinsert | endif " mouse click puts into normal mode even in terminal; disable this autocmd TermOpen * tnoremap autocmd TermOpen * tnoremap startinsert augroup END " terminal in a new tab or vsplit buffer command! -nargs=* -complete=shellcmd Term :tabnew | :term command! -nargs=* -complete=shellcmd Ttab :tabnew | :term command! -nargs=* -count -complete=shellcmd TermSplit :exec .' '. ( ? : '').'split | :term ' . command! -nargs=* -count -complete=shellcmd TermVSplit :exec .' '. ( ? : '').'vsplit | :term ' . command! -nargs=* -count -complete=shellcmd Tsplit :exec .' '. ( ? : '').'split | :term ' . command! -nargs=* -count -complete=shellcmd Tvsplit :exec .' '. ( ? : '').'vsplit | :term ' . " Mapping for in terminal mode tmap wincmd h tmap wincmd j tmap wincmd k "tmap wincmd l " Ctrl-L is clear screen tmap wincmd h tmap wincmd j tmap wincmd k tmap wincmd l " Ctrl-/ requires key remapping (neovim 0.8.0, see neovim/neovim#18735) tmap endif if has('nvim') " ignore erroneous key input on scrolling, tnoremap endif " jump behavior: gF (follows the line number) by default nnoremap gf gF nnoremap ]f gF " Buffer navigations nnoremap [b bprevious nnoremap ]b bnext if has('nvim') tnoremap [b bprevious tnoremap ]b bnext endif " Managing buffers nnoremap b0 bfirst nnoremap b$ blast nnoremap bd bdelete nnoremap Q bdelete nnoremap bq bdelete nnoremap bn bnext nnoremap bp bprev " Tab navigations nnoremap [t tabprevious nnoremap ]t tabnext if has('nvim') tnoremap [t tabprevious tnoremap ]t tabnext endif nnoremap tabnew nnoremap tt Tsplit nnoremap tabprevious nnoremap tabnext if has('nvim') tnoremap tabprevious tnoremap tabnext endif " Handy tab navigations: nnoremap 1 1gt nnoremap 2 2gt nnoremap 3 3gt nnoremap 4 4gt nnoremap 5 5gt nnoremap 6 6gt nnoremap 7 7gt nnoremap 8 8gt nnoremap 9 9gt " do not exit from visual mode when shifting " (gv : select the preivous area) vnoremap < >gv " Locations nnoremap [l lprevious nnoremap ]l lnext " ------------------ " Make and Build {{{ " ------------------ " We use and to quickly build and execute the code. " :Build => Build and run the program. " :Build => Build and run the program. (same as for compatibility) " :Debug => Build and run the program in a debug mode (with DAP in the future) if available. " To specialize project-wise, filetype-wise, or buffer-wise behaviors, " the Build, Debug, and Output commands be overriden. " However, we should NOT directly remap and . " By default, runs :Build -> :Make. Ftplugins may define :Build and :Debug. " see config/dap.lua to how :Debug will be overriden when DAP exists nmap silent wBuild nmap nmap silent wDebug imap a imap a " Alternative to (shortcut) nmap m " Build commands default to Make command! -nargs=* -bar Build Make " :Make " runs a &makeprg job asynchronously in the background. " " Note that filetype plugins or individual buffers may override the command :Make " For instance, ~/.vim/after/ftplugin/python.vim ~/.vim/after/ftplugin/cpp.vim " command! -nargs=* -complete=customlist,MakeCommandCompletion -bar Make \ call s:run_make("", ) function! s:run_make(bang, qargs) abort " Asynchronous job dispatching in the background (output goes to quickfix) exec 'AsyncMake ' . a:qargs echohl Special echom Truncate('Make: ' . &makeprg . (empty(a:qargs) ? "" : " ") . a:qargs, v:echospace) echohl NONE endfunction " Completion for :Make commands function! MakeCommandCompletion(A, L, P) abort if &makeprg == 'make' " credit: @pbnj https://dev.to/pbnj/how-to-get-make-target-tab-completion-in-vim-4mj1 let l:targets = systemlist('make -qp | awk -F'':'' ''/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split($1,A,/ /);for(i in A)print A[i]}'' | grep -v Makefile | sort -u') return filter(l:targets, 'v:val =~ "^' . a:A . '"') else return [] end endfunction " A handy way to set (global) makeprg. " See ~/.dotfiles/nvim/lua/config/commands/Makeprg.lua for nvim+lua version command! -nargs=1 Makeprg let &makeprg="" | echo "&makeprg = " command! -nargs=1 MakeprgGlobal let &g:makeprg="" | echo "&g:makeprg = " command! -nargs=1 MakeprgLocal let &l:makeprg="" | echo "&l:makeprg = " " :Just, :just command! -nargs=* -bar Just AsyncRun just autocmd CmdlineEnter * ++once call CommandAlias('just', 'Just') " }}} " :OutputToggle or (Toggle quickfix) " --------------------------------------- " :Output => Show the output or build status window (quickfix, terminal, etc.) map Output imap Output nnoremap call QuickfixToggle() " By default, :Output opens or closes the quickfix (copen) window " filetype plugin or individual buffers may override the command :Output (or :COpen) command! -nargs=0 -bar Output QuickfixToggle if !has('nvim') command! -nargs=0 -bar Output botright copen endif " : Copen no matter what (note: might be overridden, e.g. neotest) nnoremap Copen " : Copen vertically nnoremap cclosevertical Copen 80 " L: show/close location list window function! LocListToggle() let nr = winnr('$') :lopen let nr2 = winnr('$') if nr == nr2 | lclose | endif endfunction map L call LocListToggle() " [F4] Next Error [Shift+F4] Previous Error nnoremap cnext nnoremap cprevious " [F2] save inoremap stopinsertw noremap w " save in the insert mode like in other editors inoremap updateredraw " Sudo Save (:Wsudo command) command! Wsudo w !sudo tee % > /dev/null " Useful leader key combinations {{{ " : turn off search highlight nmap noh " CD: switch to the directory of the current buffer (or project root), " per-tab (cmd = 'tcd'), per-window (cmd = 'lcd'), or globally (cmd = 'cd') command! -nargs=0 CD call CD_to(expand('%:p:h'), 'lcd') function! CD_to(dir, cmd) abort if empty(a:dir) || a:dir == -1 return endif " Use per-tab CWD.see :help :tcd, :lcd, etc. if index(['cd', 'tcd', 'lcd'], a:cmd) < 0 | echoerr "Invalid cmd: " . a:cmd | return | end execute printf('silent %s %s', a:cmd, a:dir) if has('nvim-0.8') call luaeval('vim.api.nvim_echo({ \ { "[" .. _A[1] .. "] ", "Special" }, \ { _A[2], "Directory" } \ }, true, {}) \', [{'cd': 'global', 'tcd': 'tab', 'lcd': 'window'}[a:cmd], a:dir]) else echohl Directory | echom printf('%s: %s', a:cmd, getcwd()) | echohl None endif " if NERDTree is open, chdir NERDTree as well " Note: neotree haves automatic 2-way cwd binding if exists('g:NERDTree') && g:NERDTree.IsOpen() :NERDTreeCWD | wincmd w endif endfunction function! DetermineProjectRoot(...) abort let l:arg = a:0 >= 1 ? a:000[0]: '%' " If the current file is under a git repository, that's it! let l:git_dir = s:get_git_workdir(l:arg) if !empty(l:git_dir) return l:git_dir endif " Not a git repository. Use heuristic and domain knowledge as much as you can let l:base_dir = expand(l:arg . ":p") if l:base_dir =~ 'python[^/]*/site-packages/[^/]\+/' return resolve(substitute(l:base_dir, '\(python[^/]*/site-packages/[^/]\+/\).*$', '\1', 'g')) else return '' endif endfunction " CDRoot: switch to the project root directory of the current buffer command! -nargs=? CDRoot :call s:CDRoot() nmap cd CDRoot nmap lcd CDRoot lcd function! s:CDRoot(...) abort let l:cd_cmd = a:0 >= 1 ? a:000[0] : 'tcd' let l:project_root = DetermineProjectRoot() if empty(l:project_root) echohl WarningMsg | echom 'Cannot determine project root directory. (Want :CD?)' | echohl None else return CD_to(l:project_root, l:cd_cmd) endif endfunction " R : screen sucks, redraw everything function! Redraw() :mode endfunction nnoremap R :call Redraw() " src : source ~/.vimrc nnoremap src call _source_rc() if v:vim_did_enter == 0 " Do not redefine the function function! _source_rc() abort if has('nvim') source ~/.config/nvim/init.lua doautocmd VimEnter echo 'Sourced ~/.config/nvim/init.lua' else source ~/.vimrc doautocmd VimEnter echo 'Sourced ~/.vimrc' endif endfunction endif " :Vimrc opens vim config files in a new tab command! -nargs=0 Vimrc call s:open_vimrc_tab() function! s:open_vimrc_tab() let l:plug_width = max([5, float2nr(0.333 * &columns)]) let l:addon_height = max([2, float2nr(0.1 * &lines)]) exec ":tabnew " . resolve(expand("~/.vim/vimrc")) if filereadable(expand("~/.config/nvim/lua/config/lsp.lua")) exec ":vsplit " . resolve(expand("~/.config/nvim/lua/config/lsp.lua")) exec printf(":vertical resize %d", 2 * l:plug_width) endif if filereadable(expand("~/.vimrc.local")) exec ":botright sp " . resolve(expand("~/.vimrc.local")) exec printf(":resize %d", l:addon_height) endif exec ":botright vnew " . resolve(expand("~/.config/nvim/lua/config/plugins.lua")) exec printf(":vertical resize %d", l:plug_width) wincmd t " focus on the first window (~/.vimrc) endfunction " :Plugins command! -nargs=0 Plugins exec ":Config plugins" " {y,x,p} : {yank,cut,paste} wrt the system clipboard " Note: on MacOS and windows, "* and "+ are the same (for the system clipboard) " Note: on X11 (Linux), The register "* is the primary, "+ is the secondary, " but "+ is the one that works as the external clipboard without X11 or tmux map y "+y noremap x "+x noremap p "+p " w : save nnoremap w w! " q : quit/close window nnoremap q q " S : Strip trailing whitespaces command! -nargs=0 Strip call StripTrailingWhitespaces() nnoremap S Strip " df : diffthis nnoremap df diffthis " Surround a word with quotes, single quotes, parens, brackets, braces, etc. " requires and powered by the plugin surround.vim :-) " (Note) for visual blocks, use S command from surround.vim nmap s" ysiw" nmap s' ysiw' nmap s` ysiw` nmap s* ysiw*l nmap s_ ysiw_l nmap s~ ysiw~l nmap s$ ysiw$ nmap s( ysiw( nmap s) ysiw) nmap s[ ysiw[ nmap s] ysiw] nmap s{ ysiw{ nmap s} ysiw} " ask function: e.g., word -> function(word) nmap sf ysiwf nmap ( ysiwf vmap s" S" vmap s' S' vmap s` S` vmap s* S* vmap s_ S_ vmap s~ S~ vmap s$ S$ vmap s( S( vmap s) S) vmap s[ S[ vmap s] S] vmap s{ S{ vmap s} S} " ask function: e.g., word -> function(word) vmap sf Sf vmap ( Sf " Zoom Tmux noremap z :silent exec "!tmux resize-pane -Z" " Prevent accidental (on tmux) and if !empty($TMUX) nnoremap nnoremap " To increase/decrease numbers, press , , a ...? nnoremap nnoremap endif " }}} " }}} """"""""""""""""""""""""""""""""""""""""" " 3. More Functions and Commands {{{ """"""""""""""""""""""""""""""""""""""""" " Utilities if exists('*trim') function! Trim(input_string) return trim(a:input_string) " builtin trim() if neovim 0.3.2+ or vim 8.0.1630+ endfunction else function! Trim(input_string) return substitute(a:input_string, '^\s*\(.\{-}\)\s*$', '\1', '') endfunction endif function! TrimRight(input_string) return substitute(a:input_string, '\s*$', '\1', '') endfunction function! Echo(msg) abort echo a:msg return a:msg endfunction function! Echom(msg) abort echom a:msg return a:msg endfunction function! Truncate(msg, length) abort if len(a:msg) >= a:length - 3 return a:msg[:(a:length - 3)] . " ⋯" endif return a:msg endfunction if exists('*expandcmd') " neovim 0.5, vim: +v8.1.1510 function! ExpandCmd(string) abort return expandcmd(a:string) endfunction else let s:expandable = '\\*\%(<\w\+>\|%\|#\d*\)\%(:[p8~.htre]\|:g\=s\(.\).\{-\}\1.\{-\}\1\)*' function! ExpandCmd(string) abort return substitute(a:string, s:expandable, '\=expand(submatch(0))', 'g') endfunction endif " Command aliases or abbrevations " CommandAlias(aliasname, target, [register_cmd = v:false] | {options}) function! CommandAlias(aliasname, target, ...) let l:opts = get(a:000, 0, {}) if type(l:opts) != type({}) let l:opts = {'register_cmd' : l:opts} " backward compatibility endif let l:register_cmd = get(l:opts, 'register_cmd', 0) " :aliasname => :target (only applicable at the beginning of the command line) let l:target = a:target exec printf('cnoreabbrev %s ', a:aliasname) \ .printf('((getcmdtype() ==# ":" && getcmdline() =~# "^%s$") ? ', a:aliasname) \ .printf('("%s") : ("%s"))', escape(l:target, '"'), escape(a:aliasname, '"')) " optionally, make a dummy command to facilitate cmdline completion if l:register_cmd exec printf(':command! -nargs=* %s %s ', a:aliasname, l:target) endif endfunction command! -nargs=+ CommandAlias call CommandAlias() " some basic command abbrs call CommandAlias('s%', 'source %') call CommandAlias('tnew', 'tabnew') call CommandAlias('E', 'e') call CommandAlias('t', 'tab') " we don't use :copy call CommandAlias('v', 'vertical') " we don't use :vglobal call CommandAlias('vsb', 'vertical sb') call CommandAlias('vb', 'vertical sb') " WinDo: Like :windo, but preserves the current window and view. function! WinDo(command) abort let l:currwin = winnr() let l:view = winsaveview() try execute printf('noautocmd windo execute "%s"', escape(a:command, '"')) finally noautocmd execute l:currwin . 'wincmd w' call winrestview(l:view) endtry endfunction command! -nargs=* WinDo call WinDo() " :Q -- close tab function! s:Q() abort if tabpagenr('$') == 1 qall else tabclose endif endfunction command! -nargs=0 Q call s:Q() " Lua utilities (helpful for debugging) if has('nvim') " (nvim 0.7.0+) :lua= ... is equivalent as :lua print(vim.inspect(...)) call CommandAlias('LuaEcho', 'lua=') call CommandAlias('luaecho', 'lua=') call CommandAlias('l', 'lua=') call CommandAlias('L', 'LuaREPL') " There is no ':require' command in vim; it is a common mistake with intending lua require... call CommandAlias('require', 'lua= require') " neorepl.nvim let g:loaded_neorepl = v:true " no built-in commands (:Repl) command! -nargs=* -count=16 -complete=lua LuaREPL call s:LuaREPL('', , ) function! s:LuaREPL(mods, count, args) abort if a:mods == "vertical" exec printf("%s new | lua require('neorepl').new { lang = 'lua' }", a:mods) else exec printf("%s %dnew | lua require('neorepl').new { lang = 'lua' }", a:mods, a:count) endif if !empty(a:args) call nvim_input(a:args .. "") endif endfunction call CommandAlias('REPL', 'LuaREPL', v:true) call CommandAlias('Lua', 'LuaREPL', v:true) endif " ---------------------------------------------------------------------------- " ?/! : Google it / Feeling lucky " (code brought from @junegunn/dotfiles) " ---------------------------------------------------------------------------- function! s:goog(pat, lucky) let q = '"'.substitute(a:pat, '["\n]', ' ', 'g').'"' let q = substitute(q, '[[:punct:] ]', \ '\=printf("%%%02X", char2nr(submatch(0)))', 'g') call system(printf('open "https://www.google.com/search?%sq=%s"', \ a:lucky ? 'btnI&' : '', q)) endfunction nnoremap ? :call goog(expand(""), 0) nnoremap ! :call goog(expand(""), 1) xnoremap ? "gy:call goog(@g, 0)gv xnoremap ! "gy:call goog(@g, 1)gv " command abbrevations " :eh, :vsh, :sph, :tabnewh (:th) => :{e, vs, sp, tabnew} %:h/ " (to open files in the same directory as the current buffer) function! EatWhitespace() let c = nr2char(getchar(0)) " Invoke wilder manually because the completion is lost due to command-line abbrev. if exists('*wilder#start_from_normal_mode') call wilder#start_from_normal_mode() endif return index([nr2char(9), nr2char(10), nr2char(13), ' '], c) >= 0 ? '' : c endfunction call CommandAlias('eh', "e %:h/=EatWhitespace()") call CommandAlias('vsh', "vs %:h/=EatWhitespace()") call CommandAlias('sph', "sp %:h/=EatWhitespace()") call CommandAlias('tabnewh', "tabnew %:h/=EatWhitespace()") call CommandAlias('th', "tabnew %:h/=EatWhitespace()") " :eplug, :vsplug => :e $VIMPLUG/, :vs $VIMPLUG/ call CommandAlias('eplug', "e $VIMPLUG/=EatWhitespace()") call CommandAlias('vsplug', "vs $VIMPLUG/=EatWhitespace()") " Command line mode mapping {{{ " Motion in the command line similar to the normal mode (:help tcsh-style) cnoremap cnoremap cnoremap cnoremap cnoremap cnoremap " move by one letter (h, l) cnoremap cnoremap cnoremap cnoremap cnoremap cnoremap " move by word (b, w) cnoremap cnoremap " }}} " :Toggle... command aliases if has('lambda') && exists('##CmdlineEnter') augroup RegisterToggleCommands autocmd! autocmd CmdlineEnter * call RegisterToggleCommands() | autocmd! RegisterToggleCommands augroup END function! RegisterToggleCommands() abort " collect all commands ending with :...Toggle and re-register them as :Toggle... redir => cout silent command redir END let command_list = split(cout, "\n")[1:] " strip the header line let command_list = filter( \ map(command_list, { l, v -> Trim((matchlist(v, '\S[A-Z]\+Toggle ') + [''])[0]) }), \ '!empty(v:val)') for cmd in command_list let new_cmd = 'Toggle' . substitute(cmd, 'Toggle$', '', '') " register both alias and command (to make tab completion work) call CommandAlias(new_cmd, cmd, v:true) endfor endfunction endif " :CloseAllFloatingWindows " Closes all floating windows, useful for cleaning up messed up pop-ups if has('nvim-0.4.0') command! CloseAllFloatingWindows lua _G.CloseAllFloatingWindows() lua << EOF _G.CloseAllFloatingWindows = function() local closed_windows = {} for _, win in ipairs(vim.api.nvim_list_wins()) do local config = vim.api.nvim_win_get_config(win) if config.relative ~= "" then -- is_floating_window? local bufnr = vim.api.nvim_win_get_buf(win) local info = vim.fn.bufname(bufnr) if info == "" then info = vim.bo[bufnr].filetype end if info ~= "scrollview" then vim.api.nvim_win_close(win, false) -- do not force table.insert(closed_windows, ("%s[%d]"):format(info, win)) end end end if #closed_windows > 0 and vim.o.verbose > 0 then print(string.format('Closed %d floating windows: %s', #closed_windows, table.concat(closed_windows, ', '))) end end EOF nnoremap CloseAllFloatingWindows endif " Open python libraries/modules easily. " :Pyedit (:pye) -- open the file for the python module " :Pysplit (:pysp), :Pyvsplit (:pyvs) -- similar, but in :split, :vsplit " e.g., :Pyedit numpy.core will open $(site-packages)/numpy/core/__init__.py function! s:setup_pyedit_commands() abort if !has('python3') | return | endif " This feature requires jedi installed on the host python. let g:python_jedi_available = 0 python3 << EOF try: import jedi vim.command('let g:python_jedi_available = 1') except ImportError: pass EOF " Get module path for the given module (e.g., numpy.core) *without* actually importing it. function! PyModulePath(module) abort return py3eval(printf('__import__("jedi").Script("import %s").infer()[0].module_path.__str__()', a:module)) endfunction function! s:edit_python_file(cmd, module) abort if !g:python_jedi_available echohl WarningMsg | echom 'This feature requires jedi. Please pip install jedi.' | echohl NONE return end try exe a:cmd . " " . PyModulePath(a:module) catch echohl WarningMsg | echom 'Cannot find python module "' . a:module . '"' | echohl NONE endtry endfunction command! -nargs=1 -complete=customlist,CompletePythonModules Pyedit call s:edit_python_file("edit", ) command! -nargs=1 -complete=customlist,CompletePythonModules Pysplit call s:edit_python_file("split", ) command! -nargs=1 -complete=customlist,CompletePythonModules Pyvsplit call s:edit_python_file("vsplit", ) call CommandAlias('pye', 'Pyedit') call CommandAlias('pysp', 'Pysplit') call CommandAlias('pyvs', 'Pyvsplit') " Jedi-based completion for the current python interpreter. function! CompletePythonModules(...) if !g:python_jedi_available return [] endif let l:prefix = get(a:, 1, '') let l:cmdline = get(a:, 2, '') let l:cmdwords = len(split(l:cmdline, ' ')) let query = l:prefix let completions = py3eval( \ printf('[c.full_name for c in __import__("jedi").Script("import %s").complete(line=1)]', query)) return completions endfunction endfunction autocmd CmdlineEnter * ++once call s:setup_pyedit_commands() " }}} """"""""""""""""""""""""""""""""""""""""" " 4. Appearance (e.g. Colors, Syntax) {{{ """"""""""""""""""""""""""""""""""""""""" " color settings " @see http://www.calmar.ws/vim/256-xterm-24bit-rgb-color-chart.html set t_Co=256 " use 256 color if &background != 'dark' set background=dark endif if &term =~ '256color' " Disable Background Color Erase (BCE) so that color schemes " work properly when Vim is used inside tmux and GNU screen. set t_ut= endif " Use 24-bit color. if has('termguicolors') " Note: We will use GUI colors even if the current session is running under mosh. " Although the stable release does not support 24-bit color yet (see GH-961), " users are expected to use a HEAD version of mosh both on server and client " (e.g., brew install mosh --HEAD, dotfiles install mosh) if !&termguicolors set termguicolors endif endif " apply colorscheme (base: xoria256 + customization) " See ~/.vim/colors/xoria256-wook.vim " For neovim + lazy.nvim: see init.lua if !has('nvim-0.8') && !exists('g:colors_name') try silent! colorscheme xoria256-wook catch /E185/ " Cannot find color scheme silent! colorscheme evening endtry endif " Helper to register Colorscheme autocmd events (and call once immediately) " so that custom highlight overrides can still be applied after changing colorscheme function! RegisterHighlights(funcname) abort call funcref(a:funcname)() augroup Colorscheme_RegisterHighlights exec "autocmd Colorscheme * call " .. a:funcname .. "()" augroup END endfunction augroup Colorscheme_RegisterHighlights | autocmd! | augroup END " airline theme: status line and tab line if has('termguicolors') && &termguicolors let g:airline_theme = 'deus' else let g:airline_theme = 'bubblegum' endif " show cursorline for active window only let g:NrHighlight_preserve_filetypes = ['nerdtree', 'neo-tree'] augroup NrHighlight autocmd! autocmd WinEnter * setlocal cursorline autocmd WinLeave * if index(g:NrHighlight_preserve_filetypes, &ft) == -1 \ | setlocal nocursorline \ | endif augroup END " vim syntax: embedded python (P) and lua (l) scripts; see $VIMRUNTIME/syntax/vim.vim " DO NOT EMBED LUA SYNTAX, ALWAYS BROKEN (neovim/neovim#20456) both in VIM and NVIM 0.8+ " Note that we have completely moved to treesitter highlighting let g:vimsyn_embed = 'P' autocmd FileType vim hi! vimEmbedError guifg=NONE guibg=NONE " filetype detections au BufRead,BufNewFile /etc/nginx/* if &ft == '' | setfiletype nginx | endif au BufRead,BufNewFile *.prototxt if &ft == '' | setfiletype yaml | endif au BufRead,BufNewFile *.ipynb if &ft == '' | setfiletype json | endif autocmd FileType git setlocal foldlevel=1 autocmd FileType gitcommit setlocal cc=73 textwidth=72 " remove trailing whitespaces on save let g:enable_strip_trailing_whitespaces = 1 function! StripTrailingWhitespaces() if !g:enable_strip_trailing_whitespaces | return | endif let l = line('.') let c = col('.') %s/\s\+$//e call cursor(l, c) endfun command! StripTrailingWhitespacesToggle let g:enable_strip_trailing_whitespaces = 1 - g:enable_strip_trailing_whitespaces \ | echom 'Auto-strip trailing whitespaces: ' .. (g:enable_strip_trailing_whitespaces ? 'enabled' : 'disabled') augroup AutoStripTrailingWhitespaces autocmd! autocmd FileType c,cpp,java,javascript,html,ruby,python,markdown \ autocmd BufWritePre :call StripTrailingWhitespaces() augroup END " highlight trailing whitespaces function! HighlightsExtraWhitespace() abort highlight ExtraWhitespace ctermbg=red guibg=red endfunction call RegisterHighlights('HighlightsExtraWhitespace') augroup trailing_whitespaces autocmd! autocmd FileType GV,gitmessengerpopup,gitcommit,fugitive highlight clear ExtraWhitespace autocmd BufWinEnter * if _is_file_buffer() | match ExtraWhitespace /\s\+$/ | endif autocmd InsertEnter * if _is_file_buffer() | match ExtraWhitespace /\s\+\%#\@ trans<' \ . synIDattr(synID(line('.'),col('.'),0),'name') . '> lo<' \ . synIDattr(synIDtrans(synID(line('.'),col('.'),1)),'name') . '>' endfunction noremap syn call ShowSyntaxGroup() " }}} """"""""""""""""""""""""""""""""""""""""" " 5. GUI Options {{{ """"""""""""""""""""""""""""""""""""""""" " GUI settings if has('gui_running') " GUI apps usually have a default CWD set to $HOME or '/'. " Prevent scanning the entire home directory, move to DOTFILES. if v:vim_did_enter == 0 && (getcwd() == expand('$HOME') || getcwd() == '/') cd $HOME/.dotfiles endif if has('unix') let s:uname = substitute(system('uname -s'), '\n', '', '') endif if has('gui_win32') language mes en " use english messages (korean characters broken) set langmenu=none " use english context menus (korean characters broken) set guioptions-=T " exclude toolbar set guioptions-=m " exclude menubar " font setting for windows set guifont=Consolas:h11:cANSI set guifontwide=GulimChe:h12:cDEFAULT elseif has('gui_gtk2') " font setting for Ubuntu linux (GTK) set guifont=Ubuntu\ Mono\ derivative\ Powerline\ 12 elseif has('mac') && exists('g:neovide') " neovide source $DOTVIM/lua/config/neovide.lua endif endif " }}} """"""""""""""""""""""""""""""""""""""""" " 6. Plugin Settings {{{ """"""""""""""""""""""""""""""""""""""""" " ---------------------------------------------------------------- " vim-localvimrc {{{ " store and restore all decisions on whether to load lvimrc let g:localvimrc_persistent = 2 " ---------------------------------------------------------------- }}} " vim-polyglot {{{ " in favor of TS(treesitter) and vimtex ... let g:polyglot_disabled = ['python-indent', 'python-compiler', 'latex'] " rust: no needed let g:polyglot_disabled += ['rust'] " polyglot syntax (coming from vim-lua) conflicts with " $VIMRUNTIME/syntax/lua.vim shipped with neovim 0.8.0+ " see https://github.com/neovim/neovim/issues/20456 let g:polyglot_disabled += ['lua'] " LSP preview conflicts with polyglot's syntax. let g:polyglot_disabled += ['markdown'] " ---------------------------------------------------------------- }}} " vim-asterisk (enhanced *) {{{ " Use z (stay) behavior as default let g:PlugConfig['vim-asterisk'] = 's:init_asterisk' function! s:init_asterisk() abort map * (asterisk-z*) map # (asterisk-z#) map g* (asterisk-gz*) map g# (asterisk-gz#) endfunction " Keep cursor position across matches let g:asterisk#keeppos = 1 " ---------------------------------------------------------------- }}} " highlightedyank (neovim) {{{ let g:highlightedyank_highlight_duration = 300 if has('nvim') " vim-highlightedyank is not used, neovim has built-in support " https://neovim.io/doc/user/lua.html#lua-highlight augroup HighlightedYank autocmd! autocmd TextYankPost * silent! lua vim.highlight.on_yank { \ higroup = "IncSearch", \ timeout = vim.g.highlightedyank_highlight_duration } augroup END endif " ---------------------------------------------------------------- }}} " vim-highlightedundo {{{ let g:PlugConfig['vim-highlightedundo'] = 's:init_highlightedundo' function! s:init_highlightedundo() abort nmap u (highlightedundo-undo) nmap (highlightedundo-redo) nmap U (highlightedundo-Undo) nmap g- (highlightedundo-gminus) nmap g+ (highlightedundo-gplus) let g:highlightedundo#highlight_duration_delete = 500 let g:highlightedundo#highlight_duration_add = 700 endfunction " ---------------------------------------------------------------- }}} " vim-commentary {{{ let g:PlugConfig['vim-commentary'] = 's:init_commentary' function! s:init_commentary() abort nmap c CommentaryLine xmap c Commentary endfunction " ---------------------------------------------------------------- }}} " vim-peekaboo {{{ " Use floating window instead of vsplit for peeking registers. " https://github.com/junegunn/vim-peekaboo/issues/68 if has('nvim-0.5.0') let g:peekaboo_window = "call CreateCenteredFloatingWindow()" function! CreateCenteredFloatingWindow() let width = float2nr(&columns * 0.8) let height = float2nr(&lines * 0.7) let top = ((&lines - height) / 2) - 1 let left = (&columns - width) / 2 let opts = { \ 'relative': 'editor', 'row': top, 'col': left, 'width': width, 'height': height, \ 'style': 'minimal', 'border': 'rounded'} call nvim_open_win(nvim_create_buf(v:false, v:true), v:true, opts) setlocal winblend=10 endfunction endif " ---------------------------------------------------------------- }}} " editorconfig {{{ let g:EditorConfig_core_mode = 'vim_core' "let g:EditorConfig_core_mode = 'python_external' "let g:EditorConfig_core_mode = 'external_command' " ---------------------------------------------------------------- }}} " Airline {{{ " Note: for airline theme, see the 'appearance' section " (Neovim 0.5.0+) airline is deprecated in favor of lualine.nvim " use airline, with powerline-ish theme let g:airline_powerline_fonts=1 " [section customization] (:h airline-sections) " See ~/.vim/plugged/vim-airline/autoload/airline/init.vim -> airline#init#sections() to see defaults {{{ " ----------------------- autocmd User AirlineAfterInit call AirlineSectionInit() function! AirlineSectionInit() " define minwidth for some parts call airline#parts#define_minwidth('branch', 120) " section b: git info (need to call again after define_minwidth/branch) let g:airline_section_b = airline#section#create(['hunks', 'branch']) " section c: let g:airline_section_c = airline#section#create([ \ '%<', 'file', g:airline_symbols.space, 'readonly', \ ]) " LSP support (this should run after those plugin has been initialized) if exists(':LspStatus') | call airline#parts#define_function('lsp_status', 'AirlineLspStatus') | endif " section y: +lsp status, -filetype let g:airline_section_x = airline#section#create_right([ \ 'lsp_status', \ 'bookmark', 'tagbar', 'vista', 'gutentags', 'grepper', \ ]) " excludes filetype endfunction " airline + lsp-status function! AirlineLspStatus() abort return v:lua.LspStatus() endfunction " section y (ffenc): skip if utf-8[unix] let g:airline#parts#ffenc#skip_expected_string = 'utf-8[unix]' " section z: current position, but more concisely let g:airline_section_z = 'L%3l:%v' " }}} " enable tabline feature let g:airline#extensions#tabline#enabled = 1 " disable tagbar (in favor of LSP) let g:airline#extensions#tagbar#enabled = 0 " Display buffers (like tabs) in the tabline " if there is only one tab let g:airline#extensions#tabline#show_buffers = 1 " suppress mixed-indent warning for javadoc-like comments (/** */) let g:airline#extensions#whitespace#mixed_indent_algo = 1 " Disable neomake let g:airline#extensions#neomake#enabled = 0 " ---------------------------------------------------------------- }}} " nvim-bqf (enhanced quickfix) {{{ " See ~/.dotfiles/nvim/lua/config/quickfix.lua " ---------------------------------------------------------------- }}} " asyncrun.vim (Deprecated in favor of asyncrun.nvim) {{{ " see ~/.vim/after/syntax/qf.vim for color + highlights " see $DOTVIM/lua/config/build.lua " Note that asyncrun.vim allows only one job running at a time. " For multiple job dispatch, we may need more advanced plugins. " (Settings) " Do not open quickfix after job starts let g:asyncrun_open = 0 " Trigger autocmd QuickFixCmdPost make after job finish (triggers errormarker) let g:asyncrun_auto = "make" command! -nargs=* -complete=customlist,MakeCommandCompletion AsyncMake \ call AsyncMake() function! AsyncMake(qarg) abort let l:qarg = (a:qarg) if &makeprg == 'make' exe 'AsyncRun make ' . l:qarg else if !empty(l:qarg) echohl WarningMsg | echo 'Error: Cannot have arguments with &makeprg = ' . &makeprg echohl None return endif exe 'AsyncRun ' . ExpandCmd(&makeprg) endif endfunction call CommandAlias('R', 'AsyncRun') " Alias :Run => :AsyncRun call CommandAlias('Run', 'AsyncRun', v:true) " legacy asyncrun.vim autocmd events: notification callback when job finishes augroup AsyncRunEvents autocmd! autocmd User AsyncRunStart call OnAsyncRunJobStart(v:null) autocmd User AsyncRunStop call OnAsyncRunJobFinished(v:null) augroup END let g:asyncrun_job_status = {} function! OnAsyncRunJobStart(job) abort if !empty(a:job) let l:cmdline = a:job.cmdline else let l:cmdline = g:asyncrun_info " legacy asyncrun.vim endif let l:jobid = 3000 " asyncrun.vim supports only ONE concurrent job let g:asyncrun_job_status[l:jobid] = { \ 'status': 'running', \ 'cmdline': l:cmdline, \ } endfunction function! OnAsyncRunJobFinished(job) abort let l:jobid = 3000 " asyncrun supports only ONE concurrent job " a:job => nil for legacy asyncrun.vim, an object for the new asyncrun.nvim (currently not used) let l:job = get(g:asyncrun_job_status, l:jobid, {}) if l:job == {} | return | endif let l:job['status'] = g:asyncrun_status " Notifications. if l:job['status'] != "success" let l:failure_message = l:job['cmdline'] let l:job_title = printf("AsyncRun") " TODO: How to get the cmd or job info? call VimNotify('Job Failed: ' . l:failure_message, 'warn', {'title': l:job_title}) " Automatically show quickfix window if the job has failed. " open the qf window at the bottom and scroll all the way down Copen | cbottom endif " Statusline integration " Remove the job from the statusline after 1 seconds if !exists('*timer_start') silent! unlet g:asyncrun_job_status[l:jobid] else call timer_start(1000, { -> execute(printf( \"silent! unlet g:asyncrun_job_status[%d]", l:jobid)) }) endif endfunction " Actions in the asyncrun window augroup AsyncRunQuickfixWindow autocmd! " Ctrl-C twice: kill the asyncrun job autocmd FileType qf noremap echo 'Press Ctrl-C twice to kill the job' autocmd FileType qf noremap call AsyncrunQuickfixCtrlC() augroup END function! AsyncrunQuickfixCtrlC() abort " legacy asyncrun.vim if get(g:, "asyncrun_status") == "running" AsyncStop endif endfunction " ---------------------------------------------------------------- }}} " errormarker.vim (quickfix to signs) {{{ " signs let g:errormarker_errortext = '✘' let g:errormarker_warningtext = '' let g:errormarker_errortextgroup = "DiagnosticSignError" let g:errormarker_warningtextgroup = "DiagnosticSignWarn" " text line let g:errormarker_errorgroup = 'ErrormarkerErrorText' let g:errormarker_warninggroup = 'ErrormarkerWarningText' function! HighlightsErrormarker() abort hi ErrormarkerErrorText guibg=#3d0f0f hi ErrormarkerWarningText guibg=NONE endfunction call RegisterHighlights('HighlightsErrormarker') " ---------------------------------------------------------------- }}} " FZF {{{ " If floating window is not available, use 33%-bottom layout let g:fzf_layout = { 'down' : '~33%' } " vim 8.2+: popup window (junegunn/fzf.vim#821) if !has('nvim') && has('patch-8.2.194') let g:fzf_layout = { 'window' : { 'width': 0.9, 'height': 0.6 } } call extend(g:fzf_layout['window'], { 'border': 'sharp', 'highlight': 'FZFFloatBorder' }) hi def link FZFFloatBorder Comment endif " neovim 0.4+: FZF + floating window. if has('nvim-0.4') " On floating windows, we use reverse layout, i.e. prompt is at the above. let $FZF_DEFAULT_OPTS = $FZF_DEFAULT_OPTS . ' --layout=reverse --margin=1,2' let g:fzf_layout = { 'window': 'call FloatingFZF()' } function! FloatingFZF() let buf = nvim_create_buf(v:false, v:true) call setbufvar(buf, '&signcolumn', 'no') let height = float2nr(0.6 * &lines) let width = float2nr(0.9 * &columns) let horizontal = float2nr((&columns - width) / 2) let vertical = (&lines - height) / 2 let opts = { \ 'relative': 'editor', \ 'row': vertical, \ 'col': horizontal, \ 'width': width, \ 'height': height, \ } let winhl = 'Normal:FZFFloatNormal' if has('nvim-0.5') let opts['border'] = 'rounded' let winhl = winhl . ',FloatBorder:FZFFloatNormal' end let fwinnr = nvim_open_win(buf, v:true, opts) call setwinvar(fwinnr, '&winhl', winhl) " Additional keymappings on the floating terminal buffer for fzf " e.g. make {H, J, K, L} work as if it were normal vim windows tnoremap H :wincmd H:startinsert tnoremap J :wincmd J:20wincmd _:startinsert tnoremap K :wincmd K:20wincmd _:startinsert tnoremap L :wincmd L:startinsert " remap wincmd commands so that the window can work without the prefix tnoremap :wincmd l tnoremap h :wincmd h tnoremap j :wincmd j tnoremap k :wincmd k tnoremap l :wincmd l tnoremap w :wincmd w " Ctrl-{J, K} scrolls fzf (rather than wincmd) tnoremap tnoremap endfunction function! HighlightsFZF() abort hi FZFFloatNormal term=None guibg=#1a2a31 endfunction call RegisterHighlights('HighlightsFZF') endif " Inside vim, set environment variable FZF_DEFAULT_COMMAND " so that it can list the files by 'git ls-files' or 'ag'. if executable('ag') "let $FZF_DEFAULT_COMMAND = '(git ls-files ":/" || ag -l -g "") | LC_COLLATE=C sort | uniq 2> /dev/null' let $FZF_DEFAULT_COMMAND = 'ag -l -g "" 2> /dev/null' endif " fzf.vim options " neovim: fzf.vim is deprecated in favor of fzf-lua. " During migration, all fzf-vim commands will be prefixed with Fzf " e.g., History => FzfHistory let g:fzf_vim = {} if has('nvim') let g:fzf_vim.command_prefix = 'Fzf' endif " :Buffers => Jump to the existing window if possible let g:fzf_vim.buffers_jump = 1 function! s:get_git_workdir(path) let curr = fnamemodify(expand(a:path), ':p') if empty(curr) " empty buffer, etc.: fall back to current dir let curr = getcwd() endif return FugitiveWorkTree(FugitiveExtractGitDir(curr)) endfunction " Command :F (FzfSmart) function! FzfSmart(path) abort doautocmd CmdlineEnter " for lazy-loaded plugins " If neovim, try to load fzf-lua early because this can be executed much before let l:fzf_lua = has('nvim') && luaeval('pcall(require, "fzf-lua")') " If args are given (e.g. :F ), resolve it to a directory let l:path = Trim(a:path) if !empty(l:path) let l:path = expand(l:path) if !isdirectory(l:path) " for a file, the directory that contains it if filereadable(l:path) let l:path = fnamemodify(l:path, ":h") else " does not exist, error echohl WarningMsg | call Echom('Path does not exist: ' . l:path) | echohl NONE | return 0 endif endif endif " (1) If a FZF tree/explorer is shown with no argument, " invoke :Files (with preview) rather than :GFiles if empty(l:path) && &filetype == 'nerdtree' let l:target_path = b:NERDTree.root.path._str() " if the current buffer is the pinned on in the tab (vim-nerdtree-tabs), " let fzf open files in another window. Otherwise, open in the current window. if exists('t:NERDTreeBufName') && bufname('%') == t:NERDTreeBufName wincmd w " we need to move the focus outside nerdtree endif execute ':Files ' . l:target_path return 1 endif if empty(l:path) && &filetype == 'neo-tree' let l:target_path = luaeval('require("config.neotree").get_path()') if !empty(l:target_path) execute ':Files ' . l:target_path return 1 endif endif " (2) If the given path (or CWD) is in a git repo, " invoke :GFiles (plus untracked files) let l:git_workdir = s:get_git_workdir(!empty(l:path) ? l:path : '%') if ! empty(l:git_workdir) let l:old_cwd = getcwd() execute ':tcd ' . l:git_workdir try echon ':GitFiles! (' | echohl Directory | echon getcwd() | echohl NONE | echon ')' | echon ' ' if l:fzf_lua " see $DOTVIM/lua/config/fzf.lua exec 'GitFiles! ' . l:path else " vanilla vim, using fzf.vim exec 'GFiles --cached --others --modified --exclude-standard --deduplicate ' . l:path endif finally execute ':tcd ' . l:old_cwd endtry return 2 " (3) not in git repo, invoke :Files by fallback else execute ':Files ' . l:path return 3 endif endfunction " :F is a shortcut for :GFiles or :FZF command! -complete=dir -nargs=* F call FzfSmart() call CommandAlias('FC', 'F ~/.dotfiles/', v:true) call CommandAlias('FP', 'F ~/.dotfiles/vim/plugged/=EatWhitespace()', v:true) call CommandAlias('FPlug', 'F ~/.dotfiles/vim/plugged/=EatWhitespace()', v:true) call CommandAlias('Fplug', 'F ~/.dotfiles/vim/plugged/=EatWhitespace()', v:false) call CommandAlias('Flua', expand("F $VIMRUNTIME/lua"), v:true) " Invoke F (FZF) Using Ctrl-P nmap F " custom commands using fzf " ------------------------- " Utility functions brought from @junegunn/fzf.vim {{{ " Copyright (c) Junegunn Choi, under MIT License function! s:get_color(attr, ...) let gui = has('termguicolors') && &termguicolors let fam = gui ? 'gui' : 'cterm' let pat = gui ? '^#[a-f0-9]\+' : '^[0-9]\+$' for group in a:000 let code = synIDattr(synIDtrans(hlID(group)), a:attr, fam) if code =~? pat return code endif endfor return '' endfunction let s:ansi = {'black': 30, 'red': 31, 'green': 32, 'yellow': 33, 'blue': 34, 'magenta': 35, 'cyan': 36} function! s:csi(color, fg) let prefix = a:fg ? '38;' : '48;' if a:color[0] == '#' return prefix.'2;'.join(map([a:color[1:2], a:color[3:4], a:color[5:6]], 'str2nr(v:val, 16)'), ';') endif return prefix.'5;'.a:color endfunction function! s:ansi(str, group, default, ...) let fg = s:get_color('fg', a:group) let bg = s:get_color('bg', a:group) let color = (empty(fg) ? s:ansi[a:default] : s:csi(fg, 1)) . \ (empty(bg) ? '' : ';'.s:csi(bg, 0)) return printf("\x1b[%s%sm%s\x1b[m", color, a:0 ? ';1' : '', a:str) endfunction for s:color_name in keys(s:ansi) execute "function! s:".s:color_name."(str, ...)\n" \ " return s:ansi(a:str, get(a:, 1, ''), '".s:color_name."')\n" \ "endfunction" endfor " }}} " Python helpers let g:python_package_alias = { \ 'np': 'numpy', 'pd': 'pandas', 'mpl': 'matplotlib', 'plt': 'matplotlib', 'sns': 'seaborn', \ 'tf': 'tensorflow', 'tfa': 'tensorflow_addon', 'tfp': 'tensorflow_probability', \ 'tfio': 'tensorflow_io', 'snt': 'sonnet', 'ta': 'tf_agents', \ } function! PythonSitePackagesDir() return systemlist('python -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())"')[0] endfunction " Command line completion for python modules in site-packages. function! CompletePythonSitePackages(...) let l:prefix = get(a:, 1, '') let l:cmdline = get(a:, 2, '') let l:cmdwords = len(split(l:cmdline, ' ')) if l:cmdwords >= 3 || (l:cmdwords == 2 && l:cmdline =~ ' $') return [] " from the second argument, no completion endif if empty(get(g:, 'python_site_packages_cache', [])) " Listing of the site-packages directory (cached) autocmd CmdlineLeave * ++once let g:python_site_packages_cache = [] let g:python_site_packages_cache = systemlist( \ 'python -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())" ' . \ ' | xargs -I{} find {}/ -maxdepth 1 -type d ' . \ ' | grep -v "\(dist\|egg\)-info$" | grep -v "\.egg$" | grep -v "__pycache__" ' . \ ' | sort ' . \ ' | sed -e "s#\(.*\)/\(.*\)#\2#"' \) endif let filter_expr = '!empty(v:val)' if !empty(l:prefix) let filter_expr .= printf(' && v:val =~# "^%s"', escape(l:prefix, "'\"")) endif return filter(copy(g:python_site_packages_cache), filter_expr) endfunction " FZF-based finder commands " ------------------------- " :Z -- cd to recent working directories using fasd command! -nargs=* Z call s:Z() function! s:Z(args) abort call fzf#run(fzf#wrap({ \ 'source': printf('fasd -Rdl "%s"', \ escape(empty(a:args) ? '' : a:args, '"\')) \ . ' | ' . s:sed_highlight_path_last_segment, \ 'options': ['--ansi', '-1', '-0', '--no-sort', '--no-multi', \ '--prompt', 'Z> ', \ ] + s:fzf_preview_directory_if_applicable(), \ 'sink': function('s:Z_sink'), \})) endfunction function! s:Z_sink(path) abort exec 'tcd ' . a:path Neotree endfunction " :Plugs -- list all vim plugins and open the directory of the selected if !empty(g:plugs) command! -nargs=* Plugs call fzf#run \(fzf#wrap({ \ 'source': map(sort(keys(g:plugs)), 'g:plug_home . "/" . s:yellow(v:val)'), \ 'options': ['--ansi', '--delimiter', '/', '--nth', '-1', '--no-multi', '--inline-info', \ '--query', , '--prompt', 'Plugs> ', \ ] + s:fzf_preview_directory_if_applicable(), \ 'sink': function('s:Z_sink'), \})) endif " :SitePackages -- quickly jump to the site-packages directory for the current python command! -nargs=* SitePackages call s:SitePackages() function! s:SitePackages(query) abort let l:root = Trim(system('python -W ignore -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())" 2>/dev/null')) call fzf#run(fzf#wrap({ \ 'source': ('find . -mindepth 1 -maxdepth 1 -type d ' . \ ' | grep -v "\(dist\|egg\)-info$" | grep -v "\.egg$" | sort ' . \ ' | ' . s:sed_highlight_path_last_segment \ ), \ 'options': ['--ansi', '--delimiter', '/', '--nth', '-1', '--no-multi', '--inline-info', \ '--query', a:query, '--prompt', 'SitePackages> ', \ ] + s:fzf_preview_directory_if_applicable() + \ ['--header', l:root], \ 'dir': l:root, \ 'sink': 'Neotree' \})) endfunction function! s:fzf_preview_directory_if_applicable() let l:fzf_options = [] if &columns >= 100 && executable("tree") " enable directory preview if width is sufficient; max depth 2. let l:fzf_options += [ \ '--preview-window', 'right:30%', \ '--preview', 'tree -C -I "node_modules|.git|*.pyc" -L 2 -x {}', \ '--bind', '?:toggle-preview,ctrl-/:toggle-preview', \ '--header', ':: Press "?" or "CTRL-/" to toggle preview' \ ] endif return l:fzf_options endfunction let s:sed_highlight_path_last_segment = 'sed -e "s#\(.*\)/\(.*\)#\1/$(tput setaf 3)\2$(tput sgr0)#"' " ---------------------------------------------------------------- }}} " vim-floaterm {{{ let g:floaterm_position = 'center' let g:floaterm_background = '#0c1418' function! HighlightsFloaterm() abort hi Floaterm guibg=#0c1418 hi FloatermBorder guibg=#0c1418 guifg=white hi def link FloatermNF Floaterm hi def link FloatermNC Floaterm hi def link FloatermBorderNF FloatermBorder endfunction call RegisterHighlights('HighlightsFloaterm') command! -bang -nargs=? -complete=shellcmd T :FloatermToggleOrNew command! -bang -nargs=? -complete=shellcmd FT :FloatermToggleOrNew command! -bang -nargs=? -complete=shellcmd FloatermToggleOrNew call FloatermToggleOrNew(, 0) function! FloatermToggleOrNew(...) abort " FloatermToggleOrNew(cmdline: str, bang: int = 0) " - if bang=1, run the command in a shell use sendkeys. " - if bang=0, execute the program without shell " We use a dedicated name 'FT' for the floaterm triggered by the :FT, :T command let ft_bufnr = floaterm#terminal#get_bufnr('FT') let bang = a:0 >= 2 ? a:2 : 0 let cmd = a:0 > 0 ? Trim(a:1) : '' if ft_bufnr == -1 if empty(cmd) " when there is no existing floaterm window " and no argument given, just open a new floaterm shell. execute ':FloatermNew --name=FT --autoclose=1' elseif !bang " if argument given but without bang, " use it as the command line to execute (e.g. :FT git status) execute ':FloatermNew --name=FT --autoclose=0 ' . cmd elseif bang " Given bang, (e.g. :FT! git status) run cmd in a (interactive) shell. execute ':FloatermNew --name=FT --autoclose=1' let ft_bufnr = floaterm#terminal#get_bufnr('FT') call floaterm#terminal#send(ft_bufnr, [cmd]) endif else FloatermToggle FT endif endfunction " Use 90% width for floaterm. If error occurs, update the plugin let g:floaterm_width = 0.9 let g:floaterm_height = 0.85 " Turn off auto-hiding of previous floaterm windows, " in favor of the autohide autocmd below (only works for 'floating' windows) let g:floaterm_autohide = 0 " Turn off GIT_EDITOR integration. let g:floaterm_giteditor = 0 augroup FloatermCustom autocmd! autocmd user FloatermOpen call s:floaterm_configure_after() augroup END function! s:floaterm_configure_after() " Automatically hide floaterm when leaving the buffer: " When leaving out of floaterm, hide it so that it doesn't cover other windows " This autocmd should be set after 'opening' floaterm is complete (i.e., autocmd FloatermOpen), " because BufLeave can be triggered before that (e.g., while creating border windows). augroup FloatermAutoHide autocmd! autocmd BufLeave call autohide_floaterm() augroup end " Use to auto-hide the current floaterm window and move back to the previous window tnoremap :wincmd p endfunction function! s:floaterm_hide(bufnr) "return floaterm#window#hide_floaterm(a:bufnr) " API removed (d7196ee0) execute a:bufnr . 'FloatermHide' endfunction function! s:autohide_floaterm() abort " Ensure this BufLeave event handler gets executed only once autocmd! FloatermAutoHide if &filetype == 'floaterm' && nvim_win_get_config(0).relative != "" let l:FuncHide = function('s:floaterm_hide', [bufnr('%')]) " neovim or 7.4.1836+ call timer_start(0, { -> l:FuncHide() }) endif endfunction " ---------------------------------------------------------------- }}} " wilder.nvim - Better wildmenu {{{ " https://github.com/gelguy/wilder.nvim#example-configs set wildchar= set wildcharm= if has('nvim') augroup WilderLazyInit autocmd! autocmd CmdlineEnter * ++once call s:setup_wilder() augroup END endif function! s:setup_wilder() abort " If python3 host has failed to load or wilder is disabled, do not activate wilder. if !luaeval('require("config.pynvim")()') | return | endif if !luaeval('vim.F.npcall(require, "wilder") and true') | return | endif call s:check_wilder_rplugins() " only / and ? are enabled by default try call wilder#setup({'modes': ['/', '?', ':']}) catch /E117/ " Do nothing when wilder.nvim is not installed. return endtry cmap wilder#in_context() ? wilder#next() : "\" cmap wilder#in_context() ? wilder#previous() : "\" " pipeline: enable fuzzy command matching, fuzzy file search (GH-101), search history let l:pipeline = {} let l:pipeline.noop = [{_ -> v:false}] let l:pipeline.cmdline = wilder#cmdline_pipeline({ \ 'fuzzy' : 2, 'fuzzy_filter': wilder#lua_fzy_filter() }) let l:pipeline.history = [ wilder#check({_, x -> empty(x)}), wilder#history() ] let l:pipeline.file_fuzzy = executable('fd') && has('nvim-0.5.0') ? \ wilder#python_file_finder_pipeline({ \ 'debounce': 50, \ 'file_command': {ctx, arg -> v:lua._wilder_fuzzy_fd_pipeline_command(ctx, arg, 'f')}, \ 'dir_command': {ctx, arg -> v:lua._wilder_fuzzy_fd_pipeline_command(ctx, arg, 'd')}, \ 'filters': ['fuzzy_filter', 'difflib_sorter'], \ 'path': {-> getcwd()}, \ }) : l:pipeline.noop lua << EOF _G._wilder_fuzzy_fd_pipeline_command = function(ctx, arg, file_mode) local cmd = {'fd'} -- Do not enable if the argument starts with "./", if string.sub(arg, 1, 2) == "./" then return false end -- Do not enable if it's not a git repository, for the performance reason local is_git_repo = (vim.fn.FugitiveExtractGitDir(vim.fn.getcwd()) ~= '') if not is_git_repo then return false end if file_mode == 'f' then -- both file and directory (TODO: sharkdp/fd#436) cmd = vim.list_extend(cmd, {'--type', 'f', '--type', 'd'}) else -- directory only cmd = vim.list_extend(cmd, {'--type', 'd'}) end local arg_basedir, arg_filename = string.match(arg, "(.-)([^\\/]-%.?[^%.\\/]*)$") -- Starting with dot: include hidden files, but restrict to the depth 1 only (exact subdir) if string.sub(arg_filename, 1, 1) == "." then cmd = vim.list_extend(cmd, {'--no-ignore', '--hidden', '--maxdepth', '1'}) end table.insert(cmd, '.') -- When the current path argument exactly matches a subdirectory, search inside it (narrow down); -- otherwise search the entire current directory if not (arg_basedir == "") and vim.fn.isdirectory(arg_basedir) == 1 then table.insert(cmd, './' .. arg_basedir) end -- Avoid accidentally scanning the large volume ... if not vim.tbl_contains(cmd, '--maxdepth') then cmd = vim.list_extend(cmd, {'--maxdepth', '5'}) end -- vim.o.titlestring = table.concat(cmd, ' ') -- just for debugging :) return cmd end EOF call wilder#set_option('pipeline', [ \ wilder#branch( \ l:pipeline.file_fuzzy, \ l:pipeline.history, \ l:pipeline.cmdline, \ wilder#vim_search_pipeline(), \ )]) " renderer: use popupmenu (may be different than Pmenu, some yellow-ish color) let l:wilder_hl = { \ 'default': wilder#make_hl('WilderPmenu', [ \ {}, {'foreground': 0, 'background': 11}, \ {'foreground': 'black', 'background': '#ffec99'} \ ]), \ 'accent': wilder#make_hl('WilderAccent', 'WilderPmenu', [ \ {}, {}, {'foreground': '#f03e3e', 'bold': 1, 'underline': 1}]) \ } let l:wilder_renderer_option = { \ 'apply_incsearch_fix': 1, \ 'winblend': &pumblend, \ 'highlighter': wilder#lua_fzy_highlighter(), \ 'highlights': l:wilder_hl, \ 'reverse': v:false, \ 'max_height': '50%', \ 'max_width': '90%', \ 'left': [' ', wilder#popupmenu_devicons()], \ 'right': [' ', wilder#popupmenu_scrollbar()], \ } call wilder#set_option('renderer', wilder#popupmenu_renderer(l:wilder_renderer_option)) " Additional custom keymap for wilder " in the command line: refresh completion (close and start again) cmap [wilder#main#stop(), wilder#main#start_from_normal_mode()][-1] call wilder#main#start() endfunction function! s:check_wilder_rplugins() abort if !exists(':UpdateRemotePlugins') | return | endif " noplugin? " Note that rplugin functions are defined only after its first invocation, " so we first force loading it even before wilder initialization is complete. doautocmd FuncUndefined _wilder_init if !exists('*_wilder_init') && luaeval('require("config.pynvim")()') " has('python3') lua require("utils.plug_utils").UpdateRemotePlugins(nil, { force = true }) let l:msg = ':UpdateRemotePlugins has been executed. Please restart neovim.' echohl WarningMsg | echom l:msg | echohl None call VimNotify(l:msg, 'error', {'timeout': 30 * 1000, 'title': 'wilder'}) endif endfunction " ---------------------------------------------------------------- }}} " Dash {{{ " ---------------------------------------------------------------------------- " / : Launch Dash on the words on cursor or in block " ---------------------------------------------------------------------------- nnoremap / :Dash xnoremap / "gy:Dash ggv " ---------------------------------------------------------------- }}} " SuperTab {{{ " Use 'omnicomplete' as the default completion type. " It may fallback to default keyword completion (). let g:SuperTabDefaultCompletionType = '' " sometimes we may want to insert tabs or spaces for indentation. " no tab completion at the start of line or after whitespace. let g:SuperTabNoCompleteAfter = ['^', '\s'] " ---------------------------------------------------------------- }}} " NerdTree (deprecated) {{{ " change CWD when the NERDtree is first loaded to the directory initialized in " (e.g. change CWD to the directory hitted by CtrlPZ) let g:NERDTreeChDirMode = 1 " N toggles NERDTree (across tab) map N NERDTreeTabsToggle " By default nerdtree maps and which I want to reserve for window " navigation instead for the sake consistency. Therefore use different keymaps. let g:NERDTreeMapJumpNextSibling = '' let g:NERDTreeMapJumpPrevSibling = '' " Startup Options (do NOT show automatically) let g:nerdtree_tabs_open_on_console_startup = 0 let g:nerdtree_tabs_open_on_gui_startup = 0 " filter out some files, by extension let NERDTreeIgnore = [ \ '\.git$', '^__node_modules__$', \ '\.pyc$', '^__pycache__$', '^.pytest_cache$', '.mypy_cache', '\.egg-info$', \ '\.class$', '\.o$', \] let NERDTreeRespectWildIgnore = 1 " see wildignore " ---------------------------------------------------------------- }}} " neotree {{{ " see nvim/lua/plugins/ui.lua " see nvim/lua/config/neotree.lua if !exists(':Neotree') " fallback to NERDTree for vanilla vim command! -complete=dir -nargs=* Neotree NERDTree end " ---------------------------------------------------------------- }}} " Voom {{{ let g:voom_ft_modes = {'pandoc': 'markdown', 'tex': 'latex'} "nnoremap V :VoomToggle " ---------------------------------------------------------------- }}} " Easymotion {{{ " Trigger <,f> to launch easymotion global jump nmap f (easymotion-s) " backward, forward search may mapped to easymotion. "map / (easymotion-sn) "omap / (easymotion-tn) " Jump to first match, by Enter or Space let g:EasyMotion_enter_jump_first = 1 let g:EasyMotion_space_jump_first = 1 " ---------------------------------------------------------------- }}} " quick-scope {{{ " Trigger a highlight in the appropriate direction when pressing these keys: let g:qs_highlight_on_keys = ['f', 'F', 't', 'T'] function! HighlightsQuickscope() abort highlight QuickScopePrimary guifg=#afff5f gui=underline ctermfg=155 cterm=underline highlight QuickScopeSecondary guifg=#5fffff gui=underline ctermfg=81 cterm=underline endfunction call RegisterHighlights('HighlightsQuickscope') " ---------------------------------------------------------------- }}} " vim-easy-align {{{ " Start interactive EasyAlign in visual mode (e.g. vipga) xmap ga (EasyAlign) " Start interactive EasyAlign for a motion/text object (e.g. gaip) nmap ga (EasyAlign) " ---------------------------------------------------------------- }}} " indent-blankline.nvim {{{ " :help indent-blankline let g:PlugConfig['indent-blankline.nvim'] = 's:init_indent_blankline' function! s:init_indent_blankline() abort let g:indent_blankline_char = '┊' let g:indent_blankline_filetype_exclude = ['help', 'NvimTree', 'markdown'] let g:indent_blankline_buftype_exclude = ['terminal'] let g:indent_blankline_show_first_indent_level = v:false endfunction function! HighlightsIndentBlankline() abort highlight! IndentBlanklineChar guifg=#444444 gui=nocombine endfunction call RegisterHighlights('HighlightsIndentBlankline') " ---------------------------------------------------------------- }}} " indentLine {{{ " (not used in neovim) let g:PlugConfig['indentLine'] = 's:init_indentline_legacy' function! s:init_indentline_legacy() abort " conceal might break latex/json/markdown syntax, etc. " see also after/ftplugin/*.vim settings for conceal level configuration. let g:indentLine_conceallevel = 1 " conceal values might not work well for tex files (hides some characters) let g:indentLine_fileTypeExclude = ['tex', 'markdown', 'markdown', 'NvimTree', 'floaterm', 'tagbar'] " command alias command! -nargs=0 ToggleIndentLines :IndentLinesToggle endfunction " ---------------------------------------------------------------- }}} " nvim-scrollview {{{ " Make the scrollbar aware of foldings (see dstein64/nvim-scrollview#11). let g:scrollview_mode = 'virtual' " Do not use signs, it looks a bit messy without proper configs (see dstein64/nvim-scrollview#85). let g:scrollview_signs_on_startup = [''] " Disable scrollbar on some filetypes " (to avoid which can cause floating-window related errors) let g:scrollview_excluded_filetypes = [ \ 'vim-plug', 'git', 'fugitiveblame', \ 'neo-tree', \ ] " ---------------------------------------------------------------- }}} " UltiSnips {{{ let g:UltiSnipsExpandTrigger = '' let g:UltiSnipsJumpForwardTrigger = '' let g:UltiSnipsJumpBackwardTrigger = '' " edit snippets: split the window vertically or horizontally let g:UltiSnipsEditSplit = 'context' let g:UltiSnipsSnippetDirectories = [$HOME . '/.config/nvim/UltiSnips'] command! -nargs=0 UltiSnipsRefreshSnippets call UltiSnips#RefreshSnippets() \ | call VimNotify("Reloaded Snippets.", 'info', {'title': 'Ultisnips'}) command! -nargs=0 RefreshUltiSnips UltiSnipsRefreshSnippets augroup UltiSnipsAutoReload autocmd! autocmd BufWritePost *.snippets :UltiSnipsRefreshSnippets augroup END call CommandAlias('SnippetEdit', 'UltiSnipsEdit', v:true) call CommandAlias('EditSnippets', 'UltiSnipsEdit', v:true) call CommandAlias('USE', 'UltiSnipsEdit', v:false) " ---------------------------------------------------------------- }}} " semshi {{{ " Disable diagonostics sign in favor of LSP. let g:semshi#error_sign = 0 " ---------------------------------------------------------------- }}} " fugitive {{{ " See also: config/git.lua let g:fugitive_legacy_commands = 0 " command aliases command! -nargs=* GCommit call s:GCommit('', ) function! s:GCommit(mods, args) abort let l:mods = empty(a:mods) ? "tab" : a:mods " defaults to a new tab exec printf("%s Git commit -v %s", l:mods, a:args) endfunction " Remove or replace some fugitive commands augroup FugitiveCustomizeCommands autocmd CmdlineEnter * ++once silent! delcommand GDelete " :G => :GDiff " :G => :Git autocmd CmdlineEnter * ++once command! -nargs=? -complete=customlist,fugitive#Complete G call s:G() augroup END function! s:G(arg) abort if empty(Trim(a:arg)) GDiff else exec 'Git ' . a:arg endif endfunction " fugitive key mappings nnoremap gd :Gvdiff nnoremap gD :GitThreeWayDiff nnoremap gw :Gwrite nnoremap gb :echo ':Git blame -w':Git blame -w:call FugitiveResizeGblame() function! FugitiveResizeGblame() abort if &ft == 'fugitiveblame' | vertical resize 10 | endif endfunction " on commit, type 'cA' to enter in amend mode au FileType gitcommit nnoremap \ cA :bd:Git commit --verbose --amend " Git: more configuration and keymaps for fugitive index buffers augroup FugitiveAddon autocmd! " [?] show help / [ci] commit -v autocmd FileType fugitive nnoremap ? :vertical help fugitive-maps:wincmd p autocmd FileType fugitive nmap g? ? autocmd FileType fugitive nmap ci cvc " [g=] Toggle inline diffs for all files " [space] same as = (toggle inline diffs for the current file) autocmd FileType fugitive nmap g= ggVG=zM autocmd FileType fugitive nmap = autocmd FileType fugitive nmap = " [,gd] diffvsplit autocmd FileType fugitive nmap gd dv augroup END function! ReloadFugitiveBlobs(...) abort WinDo if bufname('%') =~ '^fugitive://' | :e | endif endfunction " highlights for fugitive function! HighlightsFugitive() abort hi fugitiveUntrackedHeading gui=bold guifg=#000087 guibg=Gold1 hi fugitiveUntrackedSection gui=bold guifg=#bbbb00 guibg=NONE hi fugitiveUnstagedHeading gui=bold guifg=black guibg=#d7875f hi fugitiveUnstagedSection gui=bold guifg=#af0000 guibg=NONE hi fugitiveStagedHeading gui=bold guifg=black guibg=SeaGreen2 hi fugitiveStagedSection gui=bold guifg=#009900 guibg=NONE hi fugitiveHeading gui=bold guibg=NONE endfunction call RegisterHighlights('HighlightsFugitive') " fugitive://... buffers (index) should have better statusline; " display nothing but buffer/file names (would contain SHA, or ref, etc.) let g:PlugConfig['vim-airline'] = 's:init_airline_fugitive' " not used function! s:init_airline_fugitive() abort function! FugitiveAirlinePatch(...) if bufname('%') =~ '^fugitive://' let w:airline_section_b = airline#section#create(['branch']) let w:airline_section_c = '%f' let w:airline_section_z = '' " no line numbers, etc. endif endfunction try call airline#remove_statusline_func('FugitiveAirlinePatch') call airline#add_statusline_func('FugitiveAirlinePatch') catch /E117/ " airline doesn't exist yet endtry endfunction " ---------------------------------------------------------------- }}} " gitsigns.nvim {{{ " :Gitsigns " see ~/.config/nvim/lua/config/git.lua " ---------------------------------------------------------------- }}} " diffview.nvim {{{ " Assumed fugitive is always available. " see ~/.config/nvim/lua/config/git.lua " ---------------------------------------------------------------- }}} " git-messenger {{{ " map / to jumping to older and Older(recent) commits, " respectively (see rhysd/git-messenger.vim#3) augroup git_messenger_autocmd autocmd! autocmd FileType gitmessengerpopup nmap o autocmd FileType gitmessengerpopup nmap O augroup END " Display content diff as well in the popup window let g:git_messenger_include_diff = 'current' " Use git blame -w (ignore-whitespaces). let g:git_messenger_extra_blame_args = '-w' " Use border for the popup window. if has('nvim-0.5.0') lua << EOF vim.g.git_messenger_floating_win_opts = { border = 'single', } EOF endif " ---------------------------------------------------------------- }}} " gundo key mappings and options {{{ let g:gundo_right = 1 " show at right nnoremap G :GundoToggle " ---------------------------------------------------------------- }}} " tagbar {{{ nnoremap T :TagbarToggle " Do not display visibility symbols (+, -, ...) which are not pretty. let g:tagbar_show_visibility = 0 " ---------------------------------------------------------------- }}} " Source all PlugConfig for 'enabled' plugins {{{ let g:PlugConfig = map(g:PlugConfig, 'funcref(v:val)') function! s:source_PlugConfig() abort " for vim-plug for name in keys(g:plugs) if has_key(g:PlugConfig, name) call g:PlugConfig[name]() endif endfor endfunction | call s:source_PlugConfig() " }}} " }}} """"""""""""""""""""""""""""""""""""""""" " Extra Settings {{{ """"""""""""""""""""""""""""""""""""""""" " Use local vimrc if available if filereadable(expand('~/.vimrc.local')) source \~/.vimrc.local endif " }}} " vim: set ts=2 sts=2 sw=2 foldmethod=marker: