├── .gitignore ├── .gitmodules ├── Readme.md ├── after ├── compiler │ └── tex.vim ├── ftplugin │ ├── c.vim │ ├── context.vim │ ├── csv.vim │ ├── dirvish.vim │ ├── help.vim │ ├── ledger.vim │ ├── markdown.vim │ ├── mp.vim │ ├── qf.vim │ ├── sql.vim │ ├── tex.vim │ └── undotree.vim ├── indent │ └── mp.vim └── syntax │ └── markdown.vim ├── autoload ├── legacy │ └── statusline.vim └── local │ ├── buffer.vim │ ├── fossil.vim │ ├── git.vim │ ├── markdown.vim │ ├── msg.vim │ ├── run.vim │ ├── search.vim │ ├── tags.vim │ ├── term.vim │ ├── tex.vim │ ├── text.vim │ ├── tmux.vim │ └── win.vim ├── cheat40.txt ├── ftdetect └── csv.vim ├── indent └── tex.vim ├── snippets ├── c.txt ├── context.txt ├── markdown.txt ├── sql.txt └── tex.txt ├── tmp ├── backup │ └── .gitignore ├── swap │ └── .gitignore └── undo │ └── .gitignore ├── vimrc └── vimrc_minimal /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /*~ 3 | /init.vim 4 | /spell 5 | /tags 6 | /view 7 | /viminfo 8 | /vimrc_local 9 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "pathogen"] 2 | path = pack/bundle/opt/pathogen 3 | url = https://github.com/tpope/vim-pathogen.git 4 | shallow = true 5 | [submodule "goyo"] 6 | path = pack/bundle/start/goyo 7 | url = https://github.com/junegunn/goyo.vim.git 8 | shallow = true 9 | [submodule "limelight"] 10 | path = pack/bundle/start/limelight 11 | url = https://github.com/junegunn/limelight.vim.git 12 | shallow = true 13 | [submodule "tagbar"] 14 | path = pack/bundle/opt/tagbar 15 | url = https://github.com/majutsushi/tagbar.git 16 | shallow = true 17 | [submodule "undotree"] 18 | path = pack/bundle/opt/undotree 19 | url = https://github.com/mbbill/undotree.git 20 | shallow = true 21 | [submodule "surround"] 22 | path = pack/bundle/start/surround 23 | url = https://github.com/tpope/vim-surround.git 24 | shallow = true 25 | [submodule "easy-align"] 26 | path = pack/bundle/start/easy-align 27 | url = https://github.com/junegunn/vim-easy-align.git 28 | shallow = true 29 | [submodule "swift"] 30 | path = pack/bundle/start/swift 31 | url = https://github.com/keith/swift.vim.git 32 | shallow = true 33 | [submodule "repeat"] 34 | path = pack/bundle/start/repeat 35 | url = https://github.com/tpope/vim-repeat.git 36 | shallow = true 37 | [submodule "pgsql"] 38 | path = pack/my/start/pgsql 39 | url = https://github.com/lifepillar/pgsql.vim.git 40 | shallow = false 41 | [submodule "sneak"] 42 | path = pack/bundle/start/sneak 43 | url = https://github.com/justinmk/vim-sneak.git 44 | shallow = true 45 | [submodule "outlaw"] 46 | path = pack/my/start/outlaw 47 | url = https://github.com/lifepillar/vim-outlaw.git 48 | shallow = false 49 | [submodule "wwdc16"] 50 | path = pack/my/opt/wwdc16 51 | url = https://github.com/lifepillar/vim-wwdc16-theme.git 52 | shallow = false 53 | [submodule "solarized8"] 54 | path = pack/my/opt/solarized8 55 | url = https://github.com/lifepillar/vim-solarized8 56 | shallow = false 57 | [submodule "cheat40"] 58 | path = pack/my/start/cheat40 59 | url = https://github.com/lifepillar/vim-cheat40.git 60 | shallow = false 61 | [submodule "ledger"] 62 | path = pack/bundle/start/ledger 63 | url = https://github.com/ledger/vim-ledger.git 64 | shallow = true 65 | [submodule "mucomplete"] 66 | path = pack/my/start/mucomplete 67 | url = https://github.com/lifepillar/vim-mucomplete.git 68 | shallow = false 69 | [submodule "neomake"] 70 | path = pack/bundle/opt/neomake 71 | url = https://github.com/neomake/neomake 72 | shallow = true 73 | [submodule "clang_complete"] 74 | path = pack/completion/opt/clang_complete 75 | url = https://github.com/Rip-Rip/clang_complete.git 76 | shallow = true 77 | [submodule "dirvish"] 78 | path = pack/bundle/start/dirvish 79 | url = https://github.com/justinmk/vim-dirvish.git 80 | shallow = true 81 | [submodule "wwdc17"] 82 | path = pack/my/opt/wwdc17 83 | url = https://github.com/lifepillar/vim-wwdc17-theme 84 | shallow = false 85 | [submodule "colortemplate"] 86 | path = pack/my/start/colortemplate 87 | url = https://github.com/lifepillar/vim-colortemplate.git 88 | [submodule "gruvbox8"] 89 | path = pack/my/opt/gruvbox8 90 | url = https://github.com/lifepillar/gruvbox8.git 91 | [submodule "zeef"] 92 | path = pack/my/start/zeef 93 | url = https://github.com/lifepillar/vim-zeef.git 94 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | **ARCHIVED:** I am gradually moving my personal projects to Fossil. 2 | 3 | ## My Vim setup 4 | 5 | My own Vim configuration. Some features: 6 | 7 | - Foldable and thoroughly commented `vimrc`. 8 | - Loads in way less than 50ms. 9 | - Put your customizations into `vimrc_local`. 10 | - Move freely between Vim and tmux using `⌥-h/j/k/l` 11 | (plugin-free, requires [some configuration](https://github.com/lifepillar/dotfiles/blob/master/dot-tmux.conf) 12 | in tmux, too). 13 | - 40-column **cheat sheet** always two keys away (courtesy of 14 | [Cheat40](https://github.com/lifepillar/vim-cheat40)). 15 | - Handcrafted, collapsible, fully customizable, **"plugin-free" status line** 16 | (let Vim spend a few tens of microseconds on updating the status line rather 17 | than the several milliseconds that plugins “as light as air” need). It used to 18 | support Powerline fonts up to commit 19 | [ca915737](https://github.com/lifepillar/vimrc/commit/ca9157376be876b030e5306adf38efd7093b870a), 20 | when I decided that simple is better (and Powerline fonts are an ugly hack 21 | anyway). 22 | - Uses [Zeef](https://github.com/lifepillar/vim-zeef), aka wise man's CtrlP. 23 | - **Distraction-free mode** (courtesy of 24 | [Goyo](https://github.com/junegunn/goyo.vim) and 25 | [Limelight](https://github.com/junegunn/limelight.vim)). 26 | - Etc... (read the cheat sheet and the source!) 27 | 28 | 29 | ### Requirements 30 | 31 | - A fairly recent Vim (7.4 or later) (`brew install vim` recommended on macOS). 32 | 33 | Recommended: 34 | 35 | - Vim 8. 36 | - [Exuberant ctags](http://ctags.sourceforge.net); 37 | - A fuzzy finder, one among 38 | [fzf](https://github.com/junegunn/fzf) (only the executable, not the Vim plugin), 39 | [fzy](https://github.com/jhawthorn/fzy), 40 | [pick](https://github.com/calleerlandsson/pick), 41 | [selecta](https://github.com/garybernhardt/selecta), or 42 | [skim](https://github.com/lotabout/skim); 43 | - [ripgrep](https://github.com/BurntSushi/ripgrep). 44 | 45 | 46 | ### Installation 47 | 48 | ```sh 49 | cd ~ 50 | git clone --depth 1 https://github.com/lifepillar/vimrc.git .vim 51 | cd .vim 52 | chmod 700 ./tmp ./tmp/{backup,swap,undo} 53 | git checkout -b local 54 | # We use shallow submodules; --remote makes sure we are able to check them out: 55 | git submodule update --init --remote 56 | # Commit changes (needed only if there are changes): 57 | git commit -a -m "Git submodule update --remote." 58 | ``` 59 | 60 | 61 | ### Update 62 | 63 | ```sh 64 | cd ~/.vim 65 | git checkout master 66 | git pull origin master 67 | git submodule sync 68 | git submodule update --init --recursive 69 | git checkout local 70 | git rebase master 71 | ``` 72 | 73 | …and fix conflicts. 74 | 75 | 76 | ### Update plugins and colorschemes 77 | 78 | Make sure the repo is in a clean state. 79 | 80 | ```sh 81 | git submodule update --remote --depth 1 82 | git commit -a 83 | git submodule update --recursive # Optional, only if there are plugins with submodules 84 | ``` 85 | 86 | 87 | ### How to add a new plugin or colorscheme 88 | 89 | To add a plugin `Foo` from `https://repo/foo.git`: 90 | 91 | ```sh 92 | git submodule add --name foo --depth 1 https://repo/foo.git pack/bundle/start/foo 93 | git config -f .gitmodules submodule.foo.shallow true 94 | git add .gitmodules 95 | git commit 96 | ``` 97 | 98 | To add `Foo` as an optional plugin, change `start` with `opt` (it works if Vim 99 | has packages, otherwise you also have to add the plugin to 100 | `g:pathogen_blacklist`). 101 | 102 | To add a color scheme, change `bundle/start` with `bundle/opt` (create the 103 | directory if it does not exist). 104 | 105 | 106 | ### Useful resources 107 | 108 | - [VimGolf](https://vimgolf.com) 109 | - [VimAwesome](https://vimawesome.com) 110 | - [vimrcfu](https://vimrcfu.com) 111 | - [vim-galore](https://github.com/mhinz/vim-galore) 112 | - [vimcolors](https://vimcolors.com) 113 | 114 | -------------------------------------------------------------------------------- /after/compiler/tex.vim: -------------------------------------------------------------------------------- 1 | " The following error format is adapted from vimtex 2 | " (https://github.com/lervag/vimtex). 3 | 4 | let g:tex_errorformat = '' 5 | \ . '%-P**%f,' 6 | \ . '%-P**\"%f\",' 7 | \ . '%E%f:%l: LaTeX %trror: %m,' 8 | \ . '%E%f:%l: %m,' 9 | \ . '%E! LaTeX %trror: %m,' 10 | \ . '%E!LuaTeX %trror: %m,' 11 | \ . '%E! %m,' 12 | 13 | " More info for undefined control sequences 14 | let g:tex_errorformat .= '%Z %m,' 15 | 16 | " More info for some errors 17 | let g:tex_errorformat .= '%Cl.%l %m,' 18 | 19 | if !get(g:, 'lf_latex_no_warnings', 0) 20 | " Match warnings 21 | let g:tex_errorformat .= '' 22 | \ . '%+WLaTeX %.%#Warning: %.%#line %l%.%#,' 23 | \ . '%+W%.%# at lines %l--%*\\d,' 24 | \ . '%+W%.%# at line %l,' 25 | \ . '%WLaTeX %.%#Warning: %m,' 26 | \ . '%+W%.%#%.%#Warning: %m,' 27 | \ . '%Z%.%# on input line %l.%#,' 28 | \ . '%Z%.%# line %l.,' 29 | \ . '%C(Font) %m.,' 30 | endif 31 | 32 | " Parse biblatex warnings 33 | let g:tex_errorformat .= '%-C(biblatex)%.%#in t%.%#,' 34 | \ . '%-C(biblatex)%.%#Please v%.%#,' 35 | \ . '%-C(biblatex)%.%#LaTeX a%.%#,' 36 | \ . '%-Z(biblatex)%m,' 37 | 38 | " Parse hyperref warnings 39 | let g:tex_errorformat .= '%-C(hyperref)%.%#on input line %l.,' 40 | 41 | " Ignore unmatched lines 42 | let g:tex_errorformat .= '%-G%.%#' 43 | 44 | execute 'CompilerSet errorformat=' . escape(g:tex_errorformat, ' ') 45 | 46 | CompilerSet makeprg=make 47 | -------------------------------------------------------------------------------- /after/ftplugin/c.vim: -------------------------------------------------------------------------------- 1 | setlocal commentstring=//%s 2 | setlocal cinoptions+=*200 3 | setlocal foldmethod=syntax 4 | setlocal foldlevel=10 5 | 6 | if !exists('g:loaded_neomake') 7 | packadd neomake 8 | endif 9 | call neomake#configure#automake('nw', 1000) 10 | setlocal signcolumn=yes 11 | 12 | call local#text#load_snippets() 13 | 14 | " Cscope 15 | if !local#tags#load_cscope_db() 16 | finish 17 | endif 18 | 19 | nnoremap ca :cs find a =fnameescape(expand("")):bo cwindow 20 | nnoremap cc :cs find c =fnameescape(expand("")):bo cwindow 21 | nnoremap cd :cs find d =fnameescape(expand("")):bo cwindow 22 | nnoremap ce :cs find e =fnameescape(expand("")):bo cwindow 23 | nnoremap cf :cs find f =fnameescape(expand("")) 24 | nnoremap cg :cs find g =fnameescape(expand("")) 25 | nnoremap ci :cs find i ^=fnameescape(expand("")):bo cwindow 26 | nnoremap cs :cs find s =fnameescape(expand("")):bo cwindow 27 | nnoremap ct :cs find t =fnameescape(expand("")):bo cwindow 28 | 29 | -------------------------------------------------------------------------------- /after/ftplugin/context.vim: -------------------------------------------------------------------------------- 1 | " Better auto-completion for things like '\in{...}[fig:...]' 2 | setlocal iskeyword+=: 3 | 4 | " Typeset with ConTeXt MKIV 5 | nnoremap tt :update:ConTeXt 6 | " Clean generated files: 7 | nnoremap tc :call local#tex#clean() 8 | " Open PDF previewer (Skim): 9 | nnoremap tv :call local#tex#preview() 10 | " Forward search using Skim: 11 | nnoremap ts :call local#tex#forward_search() 12 | 13 | " Use ConTeXt Beta by default 14 | let g:context_mtxrun = 'PATH=$HOME/Applications/context-osx-64/tex/texmf-osx-64/bin:$PATH mtxrun' 15 | let g:context_synctex = 1 16 | 17 | call local#text#load_snippets() 18 | -------------------------------------------------------------------------------- /after/ftplugin/csv.vim: -------------------------------------------------------------------------------- 1 | imap 2 | imap 3 | setlocal noexpandtab 4 | TabWidth 10 5 | 6 | -------------------------------------------------------------------------------- /after/ftplugin/dirvish.vim: -------------------------------------------------------------------------------- 1 | nmap - (dirvish_up) 2 | silent! unmap 3 | silent! unmap 4 | 5 | " Open in a new tab 6 | nnoremap t :call dirvish#open('tabedit', 0) 7 | xnoremap t :call dirvish#open('tabedit', 0) 8 | 9 | if has('patch-8.1.1372') " Has g:statusline_winid 10 | fun! LFBuildDirvishStatusLine() 11 | return '%#'.LFStlHighlight().'# BROWSE %* %{winnr()} %f %= %l:%v %P ' 12 | endf 13 | else 14 | call legacy#statusline#dirvish() 15 | endif 16 | 17 | if exists("g:default_stl") 18 | setlocal statusline=%!LFBuildDirvishStatusLine() 19 | endif 20 | 21 | -------------------------------------------------------------------------------- /after/ftplugin/help.vim: -------------------------------------------------------------------------------- 1 | if has('patch-8.1.1372') " Has g:statusline_winid 2 | fun! LFBuildHelpStatusLine() 3 | return '%#'.LFStlHighlight().'# HELP %* %{winnr()} %t (%n) %= %l:%v %P ' 4 | endf 5 | else 6 | call legacy#statusline#help() 7 | endif 8 | 9 | if exists("g:default_stl") 10 | setlocal statusline=%!LFBuildHelpStatusLine() 11 | endif 12 | 13 | -------------------------------------------------------------------------------- /after/ftplugin/ledger.vim: -------------------------------------------------------------------------------- 1 | setlocal noinfercase 2 | let b:mucomplete_empty_text = 1 3 | let b:mucomplete_chain = ['omni', 'keyn', 'incl'] 4 | call extend(g:mucomplete#can_complete, { 5 | \ 'ledger': { 6 | \ 'keyn': { t -> g:mucomplete_with_key || t =~# '\a\a$' }, 7 | \ 'incl': { t -> t =~# '\a\a$' }, 8 | \ 'omni': { t -> t =~# '[:A-z]\{2}$\|\%1c$' } 9 | \ } 10 | \ }) 11 | 12 | let g:ledger_qf_register_format = '%(date) %-25(payee) %-45(account) %15(amount) %15(total)\n' 13 | let g:ledger_table_sep = "\t" 14 | 15 | let g:ledger_tables = { 16 | \ 'register': { 17 | \ 'names': ['date', 'week_day', 'amount', 'total', 'payee', 'account'], 18 | \ 'fields': [ 19 | \ '%(format_date(date,"' . join(['%Y-%m-%d', '%a'], g:ledger_table_sep) . '"))', 20 | \ '%(quantity(scrub(display_amount)))', 21 | \ '%(quantity(scrub(display_total)))', 22 | \ '%(payee)', 23 | \ '%(display_account)\n' 24 | \ ] 25 | \ }, 26 | \ 'cleared': { 27 | \ 'names': ['latest', 'latest_cleared', 'balance', 'uncleared', 'partial_account', 'account'], 28 | \ 'fields': [ 29 | \ '%(latest ? format_date(latest) : " ")', 30 | \ '%(latest_cleared ? format_date(latest_cleared) : " ")', 31 | \ '%(quantity(scrub(get_at(display_total, 0))))', 32 | \ '%(quantity(scrub(get_at(display_total, 1))))', 33 | \ '%(partial_account)', 34 | \ '%(account)\n%/' 35 | \ ] 36 | \ }, 37 | \ 'budget': { 38 | \ 'names': ['actual', 'budgeted', 'remaining', 'used', 'partial_account', 'account'], 39 | \ 'fields': [ 40 | \ '%(quantity(scrub(get_at(display_total, 0))))', 41 | \ '%(get_at(display_total, 1) ? quantity(-scrub(get_at(display_total, 1))) : 0.0)', 42 | \ '%(get_at(display_total, 1) ? (get_at(display_total, 0) ? quantity(-scrub(get_at(display_total, 1) + get_at(display_total, 0))) : quantity(-scrub(get_at(display_total, 1)))) : quantity(-scrub(get_at(display_total, 0))))', 43 | \ '%(get_at(display_total, 1) ? quantity(100% * (get_at(display_total, 0) ? scrub(get_at(display_total, 0)) : 0.0) / -scrub(get_at(display_total, 1))) : "na")', 44 | \ '%(partial_account)', 45 | \ '%(account)\n%/' 46 | \ ] 47 | \ } 48 | \ } 49 | 50 | fun! s:ledger_table(type, args) 51 | let l:format = join(g:ledger_tables[a:type].fields, g:ledger_table_sep) 52 | if ledger#output(ledger#report(g:ledger_main, join([a:type, a:args, "-F '".l:format."'"]))) 53 | set modifiable 54 | call setline(1, join(g:ledger_tables[a:type].names, "\t")) " Add header 55 | setlocal filetype=csv 56 | set nomodifiable 57 | endif 58 | endf 59 | 60 | fun! s:ledger_complete() 61 | if match(getline('.'), escape(g:ledger_decimal_sep, '.').'\d\d\%'.col('.').'c') > -1 62 | return "\=ledger#autocomplete_and_align()\" 63 | else 64 | return mucomplete#tab_complete(1) 65 | endif 66 | endf 67 | 68 | command! -buffer -nargs=+ LedgerTable call ledger_table('register', ) 69 | command! -buffer -nargs=+ BalanceTable call ledger_table('cleared', ) 70 | command! -buffer -nargs=+ BudgetTable call ledger_table('budget', ) 71 | 72 | " Toggle transaction state 73 | nnoremap :call ledger#transaction_state_toggle(line('.'), '* !') 74 | " Set today's date as auxiliary date 75 | nnoremap lx :call ledger#transaction_date_set('.', "auxiliary") 76 | " Autocompletion and alignment 77 | imap ledger_complete() 78 | vnoremap :LedgerAlign 79 | " Enter a new transaction based on the text in the current line 80 | nnoremap :call ledger#entry() 81 | inoremap :call ledger#entry() 82 | 83 | " Monthly average 84 | nnoremap la :Ledger reg --collapse --empty -A -O --real --aux-date --monthly -p 'this year' expenses 85 | " Annualized budget 86 | nnoremap lA :execute "Ledger budget -p 'this year' --real --aux-date --now " 87 | \ . strftime('%Y', localtime()) . "/12/31 expenses payable income 88 | \ -F '%(justify((get_at(display_total, 1) ? -scrub(get_at(display_total, 1)) : 0.0), 16, -1, true, color)) 89 | \ %(!options.flat ? depth_spacer : \"\") %-(ansify_if(partial_account(options.flat), blue if color))\\n%/%$1 %$2 %$3\\n 90 | \%/%(prepend_width ? \" \" * int(prepend_width) : \"\") --------------------------\\n'" 91 | " Balance report 92 | nnoremap lb :Ledger bal --real --aux-date \(\(assets or liabilities\) and \(not prepaid\)\) 93 | " Budget 94 | nnoremap lB :Ledger budget --real -p 'this year' expenses payable 95 | " Cleared report 96 | nnoremap lc :Ledger cleared --real --aux-date \(\(assets or liabilities\) and \(not prepaid\)\) 97 | nnoremap lC :Ledger cleared --real --aux-date --current \(\(assets or liabilities\) and \(not prepaid\)\) 98 | " Debit/credit report 99 | nnoremap ld :Ledger reg --dc -S date --real --cleared -d 'd>=[2 months ago]' 'liabilities:credit card' 100 | " Expense report 101 | nnoremap le :Ledger bal --subtotal --aux-date --real -p 'this month' expenses 102 | " Earnings report (see commits ce8b535f and 0ee28dd4 in my personal journal repository) 103 | nnoremap lE :Ledger bal --aux-date tag earnings -p 'this month' 104 | " Cash flow 105 | nnoremap lf :Ledger bal --collapse --dc --related --real --aux-date --cleared -p 'last 30 days' 106 | " Income statement 107 | nnoremap li :Ledger bal --real --aux-date -p 'this month' \(income or expenses\) 108 | " Monthly totals 109 | nnoremap lm :Ledger reg --monthly --aux-date --real --collapse -p 'this year' expenses 110 | " Net worth 111 | nnoremap ln :Ledger reg -F '%10(date)%20(display_total)\n' --collapse --real --aux-date -d 'd>=[this year]' --monthly \(\(assets or liab\) and \(not prepaid\)\) 112 | " Pending/uncleared transactions 113 | nnoremap lp :Register --pending 114 | " Register 115 | nnoremap lr :Ledger reg --real --aux-date -p 'this month' 116 | " Sorted expenses 117 | nnoremap ls :Ledger reg --period-sort '(-amount)' --aux-date --real --monthly -p 'this month' expenses 118 | " Savings 119 | nnoremap lS :Ledger bal --collapse --real --aux-date -p 'last month' \(income or expenses\) 120 | " Uncleared transactions 121 | nnoremap lu :Register --uncleared 122 | -------------------------------------------------------------------------------- /after/ftplugin/markdown.vim: -------------------------------------------------------------------------------- 1 | setlocal autoindent 2 | setlocal completefunc=local#markdown#complete 3 | setlocal conceallevel=2 4 | setlocal dictionary=/usr/share/dict/words 5 | " Enable completion after [[ and # 6 | setlocal iskeyword+=[,# 7 | " Disable HTML completion function set by the Markdown plugin: 8 | setlocal omnifunc= 9 | setlocal path=.,**3/ 10 | setlocal spell 11 | setlocal spelllang=en 12 | setlocal suffixesadd=.md 13 | 14 | " Search notes in the current directory 15 | nnoremap n :call zeef#open(local#markdown#notes(''), 'local#markdown#set_arglist', 'Choose notes') 16 | 17 | let b:mucomplete_chain = ['user', 'path', 'keyn', 'dict', 'uspl'] 18 | 19 | let s:root = finddir("Notes", ".;") 20 | execute "lcd" (empty(s:root) && !empty(expand("%")) ? "%:h" : s:root) 21 | unlet s:root 22 | 23 | call local#text#load_snippets() 24 | -------------------------------------------------------------------------------- /after/ftplugin/mp.vim: -------------------------------------------------------------------------------- 1 | let b:match_words .= ',\:\,\:\,\:\' 2 | 3 | let &l:path = './**2' 4 | -------------------------------------------------------------------------------- /after/ftplugin/qf.vim: -------------------------------------------------------------------------------- 1 | if has('patch-8.1.1372') " Has g:statusline_winid 2 | fun! LFQuickfixNumLines() 3 | return winwidth(0) >= 60 ? printf(" %d line%s", line("$"), line("$") > 1 ? "s " : " ") : "" 4 | endf 5 | 6 | fun! LFBuildQuickfixStatusLine() 7 | return '%#'.LFStlHighlight().'# ' 8 | \ . (get(getwininfo(win_getid())[0], "loclist", 0) ? "LOCLIST" : "QUICKFIX") 9 | \ . ' %* %{winnr()} %<%{get(w:, "quickfix_title", "")} %= %q %{LFQuickfixNumLines()}' 10 | endf 11 | else 12 | call legacy#statusline#quickfix() 13 | endif 14 | 15 | if exists("g:default_stl") 16 | setlocal statusline=%!LFBuildQuickfixStatusLine() 17 | endif 18 | 19 | -------------------------------------------------------------------------------- /after/ftplugin/sql.vim: -------------------------------------------------------------------------------- 1 | setlocal commentstring=--\ %s 2 | 3 | call local#text#load_snippets() 4 | -------------------------------------------------------------------------------- /after/ftplugin/tex.vim: -------------------------------------------------------------------------------- 1 | compiler tex 2 | 3 | " Better auto-completion for things like '\label{fig:...' 4 | setlocal iskeyword+=: 5 | " See :h tex-conceal 6 | setlocal conceallevel=0 7 | 8 | " Typeset with lualatex/pdflatex 9 | nnoremap tt :update:LuaLaTeX 10 | nnoremap tp :update:PdfLaTeX 11 | " Clean generated files: 12 | nnoremap tc :call local#tex#clean() 13 | " Open PDF previewer (Skim): 14 | nnoremap tv :call local#tex#preview() 15 | " Forward search using Skim: 16 | nnoremap ts :call local#tex#forward_search() 17 | 18 | let s:tp_regex = '^$\|^\s*\(\\item\|\\begin\|\\end\|\\\(sub\)*section\|\\label\|\(small\|med\|big\)skip\)' 19 | 20 | fun! s:tp() 21 | call cursor(search(s:tp_regex, 'bcW')+1, 1) 22 | normal! V 23 | call cursor(search(s:tp_regex, 'W')-1, 1) 24 | endf 25 | 26 | " Reflow paragraph with gqtp ("gq TeX paragraph") 27 | omap tp :call tp() 28 | " Select TeX paragraph 29 | vnoremap tp :call tp() 30 | 31 | " $...$ text object 32 | onoremap i$ :normal! T$vt$ 33 | onoremap a$ :normal! F$vf$ 34 | vnoremap i$ T$ot$ 35 | vnoremap a$ F$of$ 36 | 37 | command! -buffer -nargs=? -complete=file LuaLaTeX call local#tex#typeset({'latexmk': ['-lualatex']}, ) 38 | command! -buffer -nargs=? -complete=file PdfLaTeX call local#tex#typeset({'latexmk': ['-pdf']}, ) 39 | command! -nargs=0 LaTeXJobStatus call local#tex#job_status() 40 | command! -nargs=0 LatexStopJobs call local#tex#stop_jobs() 41 | 42 | call local#text#load_snippets() 43 | -------------------------------------------------------------------------------- /after/ftplugin/undotree.vim: -------------------------------------------------------------------------------- 1 | if has('patch-8.1.1372') " Has g:statusline_winid 2 | fun! LFBuildUndotreeStatusLine() 3 | return '%#'.LFStlHighlight().'# Undotree %* %<%{t:undotree.GetStatusLine()} %*' 4 | endf 5 | else 6 | call legacy#statusline#undotree() 7 | endif 8 | 9 | if exists("g:default_stl") 10 | setlocal statusline=%!LFBuildUndotreeStatusLine() 11 | endif 12 | 13 | -------------------------------------------------------------------------------- /after/indent/mp.vim: -------------------------------------------------------------------------------- 1 | " Metappeal keywords 2 | let g:mp_open_tag .= '\|\' 3 | let g:mp_close_tag .= '\|\' 4 | 5 | -------------------------------------------------------------------------------- /after/syntax/markdown.vim: -------------------------------------------------------------------------------- 1 | syn region markdownInternalLink matchgroup=Delimiter start=/\[\[/ end=/\]\]/ oneline 2 | syn match markdownTag /#\S\+/ 3 | 4 | hi link markdownInternalLink Underlined 5 | hi link markdownTag Constant 6 | hi link markdownCode PreProc 7 | hi link markdownCodeBlock PreProc 8 | -------------------------------------------------------------------------------- /autoload/legacy/statusline.vim: -------------------------------------------------------------------------------- 1 | " Build the status line the way I want - no fat light plugins! 2 | " For the keys, see :h mode() 3 | let g:lf_stlh = { 4 | \ 'n': 'NormalMode', 'i': 'InsertMode', 'R': 'ReplaceMode', 5 | \ 'v': 'VisualMode', 'V': 'VisualMode', "\": 'VisualMode', 6 | \ 's': 'VisualMode', 'S': 'VisualMode', "\": 'VisualMode', 7 | \ 'c': 'CommandMode', 'r': 'CommandMode', 't': 'CommandMode', 8 | \ '!': 'CommandMode' 9 | \ } 10 | 11 | let g:lf_stlm = { 12 | \ 'n': 'N', 'i': 'I', 'R': 'R', 13 | \ 'v': 'V', 'V': 'V', "\": 'V', 14 | \ 's': 'S', 'S': 'S', "\": 'S', 15 | \ 'c': 'C', 'r': 'P', 't': 'T', 16 | \ '!': '!'} 17 | 18 | " curwin is always the number of the currently active window. In a %{} 19 | " context, winnr() always refers to the window to which the status line 20 | " being drawn belongs. Since this function is invoked in a %{} context, 21 | " winnr() may be different from a:curwin. We use this fact to detect 22 | " whether we are drawing in the active window or in an inactive window. 23 | fun! SetupStl(curwin) 24 | return get(extend(w:, { 'lf_active': winnr() ==# a:curwin }), '', '') 25 | endf 26 | 27 | fun! LFBuildStatusLine() 28 | return '%{SetupStl('.winnr().')}%#'.get(g:lf_stlh, mode(), 'Warnings')."# 29 | \%{w:['lf_active'] 30 | \?' '.get(g:lf_stlm,mode(),mode()).(&paste?' PASTE ':' ') 31 | \:''}%* 32 | \ %{(w:['lf_active']?'':' ').winnr()} 33 | \ %{&mod?'◦':' '} %t (%n) %{&ma?(&ro?'▪':' '):'✗'} 34 | \ %<%{empty(&bt) 35 | \?(winwidth(0)<80 36 | \?(winwidth(0)<50?'':expand('%:p:h:t')) 37 | \:expand('%:p:~:h')) 38 | \:''} 39 | \ %= 40 | \ %a %w %{&ft} %{winwidth(0)<80 41 | \?'' 42 | \:' '.(strlen(&fenc)?&fenc:&enc).(&bomb?',BOM ':' ').&ff.(&et?'':' ⇥ ')} 43 | \ %l:%v %P 44 | \ %#Warnings#%{w:['lf_active']?get(b:,'lf_stl_warnings',''):''}%*" 45 | endf 46 | 47 | fun! legacy#statusline#init() 48 | endf 49 | 50 | " Local status lines 51 | 52 | fun! legacy#statusline#help() 53 | fun! LFBuildHelpStatusLine() 54 | return '%{SetupStl('.winnr().')}%#' 55 | \ . get(g:lf_stlh, mode(), 'Warnings') 56 | \ .'#%{w:["lf_active"] ? " HELP " : ""}%* 57 | \%{w:["lf_active"] ? "" : " HELP "} %{winnr()} %t (%n) %= %l:%v %P ' 58 | endf 59 | endf 60 | 61 | fun! legacy#statusline#undotree() 62 | fun! LFBuildUndotreeStatusLine() 63 | return '%{SetupStl('.winnr().')}%#' 64 | \ . get(g:lf_stlh, mode(), 'Warnings') 65 | \ . '#%{w:["lf_active"] ? " Undotree " : ""}%* 66 | \%{w:["lf_active"] ? "" : " Undotree"} 67 | \ %<%{t:undotree.GetStatusLine()} %*' 68 | endf 69 | endf 70 | 71 | fun! legacy#statusline#dirvish() 72 | fun! LFBuildDirvishStatusLine() 73 | return '%{SetupStl('.winnr().')}%#' 74 | \ . get(g:lf_stlh, mode(), 'Warnings') 75 | \ . '#%{w:["lf_active"] ? " BROWSE " : ""}%* 76 | \%{w:["lf_active"] ? "" : " BROWSE "} %{winnr()} %f %= %l:%v %P ' 77 | endf 78 | endf 79 | 80 | if exists("*getwininfo") 81 | fun! LFQuickfixTag() 82 | return get(getwininfo(win_getid())[0], "loclist", 0) ? " LOCLIST " : " QUICKFIX " 83 | endf 84 | else 85 | fun! LFQuickfixTag() 86 | return 'QUICKFIX' 87 | endf 88 | endif 89 | 90 | fun! LFQuickfixNumLines() 91 | return winwidth(0) >= 60 ? printf(" %d line%s", line("$"), line("$") > 1 ? "s " : " ") : "" 92 | endf 93 | 94 | fun! legacy#statusline#quickfix() 95 | fun! LFBuildQuickfixStatusLine() 96 | return '%{SetupStl('.winnr().')}%#' 97 | \ . get(g:lf_stlh, mode(), 'Warnings') 98 | \ .'#%{w:["lf_active"] ? LFQuickfixTag() : "" }%* 99 | \%{w:["lf_active"] ? "" : LFQuickfixTag()} 100 | \ %{winnr()} %<%{get(w:, "quickfix_title", "")} 101 | \ %= %q %{LFQuickfixNumLines()}' 102 | endf 103 | endf 104 | 105 | -------------------------------------------------------------------------------- /autoload/local/buffer.vim: -------------------------------------------------------------------------------- 1 | " Ignore syntax highlighting, filetype, etc… 2 | " See also: https://vim.fandom.com/wiki/Faster_loading_of_large_files 3 | fun! local#buffer#large(name) 4 | let b:lf_large_file = 1 5 | syntax clear 6 | set eventignore+=FileType 7 | let &backupskip ..= ',' .. a:name 8 | setlocal foldmethod=manual nofoldenable noswapfile noundofile 9 | augroup lf_large_buffer 10 | autocmd! 11 | autocmd BufWinEnter call restore_eventignore() 12 | augroup END 13 | endf 14 | 15 | fun! s:restore_eventignore() 16 | set eventignore-=FileType 17 | autocmd! lf_large_buffer 18 | augroup! lf_large_buffer 19 | endf 20 | 21 | " Delete all buffers except the current one 22 | fun! local#buffer#delete_others() 23 | let l:bl = filter(range(1, bufnr('$')), 'buflisted(v:val)') 24 | execute (bufnr('') > l:bl[0] ? 'confirm ' .. l:bl[0] .. ',.-bd' : '') (bufnr('') < l:bl[-1] ? '|confirm .+,$bd' : '') 25 | endf 26 | 27 | " Wipe all buffers except the current one 28 | fun! local#buffer#wipe_others() 29 | let l:min = min(filter(range(1, bufnr('$')), 'bufexists(v:val)')) 30 | execute (bufnr('') > l:min ? 'confirm ' .. l:min .. ',.-bw' : '') (bufnr('') < bufnr('$') ? '|confirm .+,$bw' : '') 31 | endf 32 | 33 | " Clear (delete the content) of the given buffer. 34 | fun! local#buffer#clear(file_pattern) 35 | let l:view = winsaveview() 36 | if bufnr(a:file_pattern) > -1 37 | silent execute bufnr(a:file_pattern) 'bufdo' '1,$delete' 38 | silent execute 'buffer' bufnr(@#) 39 | endif 40 | call winrestview(l:view) 41 | endf 42 | 43 | fun! local#buffer#swap_exists(file) 44 | " Note that glob() honors 'wildignore', which contains *.swp (see my vimrc) 45 | " So, l:n is the number of *additional* swap files. 46 | let l:n = len(split(glob(fnamemodify(v:swapname, ":r") .. ".*"), "\n")) 47 | echohl WarningMsg 48 | echon 'A swap file exists: ' .. fnamemodify(v:swapname, ":t") .. 49 | \ (l:n > 0 ? ' (and ' .. l:n .. ' more)' : '') 50 | echohl None 51 | echon "\n" .. 'read-(o)nly (e)dit (r)ecover (d)elete (q)uit (a)bort (ENTER for details) ' 52 | let v:swapchoice = nr2char(getchar()) 53 | echo "\r" 54 | endf 55 | 56 | -------------------------------------------------------------------------------- /autoload/local/fossil.vim: -------------------------------------------------------------------------------- 1 | " Diff between the current buffer and the version currently checked out. 2 | fun! local#fossil#diff() abort 3 | let l:ft = getbufvar("%", '&ft') 4 | let l:fn = expand('%:t') 5 | call local#run#cmd(['fossil', 'cat', l:fn], { 'pos': 'rightbelow vertical'}) 6 | let &l:filetype = l:ft 7 | execute 'silent file' l:fn '[CHECKOUT]' 8 | setlocal nomodifiable 9 | diffthis 10 | autocmd BufWinLeave diffoff! 11 | wincmd p 12 | diffthis 13 | endf 14 | 15 | fun! local#fossil#three_way_diff() abort 16 | let l:ft = getbufvar("%", "&ft") " Get the file type 17 | " Show the version from the current branch on the left 18 | execute 'leftabove vsplit' expand("%:p") .. '-baseline' 19 | let &l:filetype = l:ft 20 | diffthis 21 | autocmd BufWinLeave diffoff! 22 | wincmd p 23 | " Show version from the other branch on the right 24 | execute 'rightbelow vsplit' expand("%:p") .. '-merge' 25 | let &l:filetype = l:ft 26 | diffthis 27 | autocmd BufWinLeave diffoff! 28 | wincmd p 29 | diffthis 30 | endf 31 | 32 | -------------------------------------------------------------------------------- /autoload/local/git.vim: -------------------------------------------------------------------------------- 1 | " Synchronously execute a non-interactive Git command in the directory 2 | " containing the file of the current buffer, and send the output to a new 3 | " buffer. 4 | " 5 | " args: a List providing the arguments for git 6 | " where: a Vim command specifying where the window should be opened 7 | fun! s:git(args, where) abort 8 | call local#run#cmd(['git'] + a:args, {'pos': a:where}) 9 | setlocal nomodifiable 10 | endf 11 | 12 | " Show a vertical diff between the current buffer and its last committed 13 | " version. 14 | fun! local#git#diff() abort 15 | let l:ft = getbufvar("%", '&ft') " Get the file type 16 | let l:fn = expand('%:t') 17 | call s:git(['show', 'HEAD:./' .. l:fn], 'rightbelow vertical') 18 | let &l:filetype = l:ft 19 | execute 'silent file' l:fn '[HEAD]' 20 | diffthis 21 | autocmd BufWinLeave diffoff! 22 | wincmd p 23 | diffthis 24 | endf 25 | 26 | " Show a three-way diff. Useful for fixing merge conflicts. 27 | " This assumes that the current file is the working copy, of course. 28 | fun! local#git#three_way_diff() abort 29 | let l:ft = getbufvar("%", "&ft") " Get the file type 30 | let l:fn = expand('%:t') 31 | " Show the version from the current branch on the left 32 | call s:git(['show', ':2:./' .. l:fn], "leftabove vertical") 33 | let &l:filetype = l:ft 34 | execute 'silent file' l:fn '[OURS]' 35 | diffthis 36 | autocmd BufWinLeave diffoff! 37 | wincmd p 38 | " Show version from the other branch on the right 39 | call s:git(['show', ':3:./' .. l:fn], 'rightbelow vertical') 40 | let &l:filetype = l:ft 41 | execute 'silent file' l:fn '[OTHER]' 42 | diffthis 43 | autocmd BufWinLeave diffoff! 44 | wincmd p 45 | diffthis 46 | endf 47 | 48 | -------------------------------------------------------------------------------- /autoload/local/markdown.vim: -------------------------------------------------------------------------------- 1 | " Partially inspired by https://vimways.org/2019/personal-notetaking-in-vim/ 2 | 3 | fun! local#markdown#set_arglist(result) 4 | execute "args" join(map(a:result, 'fnameescape(v:val) .. ".md"')) 5 | endf 6 | 7 | fun! local#markdown#notes(base) 8 | return map(glob(printf('**/%s*.md', a:base), 1, 1, 0), 'fnamemodify(v:val, ":r")') 9 | endf 10 | 11 | " Search for #tags matching base in the Markdown files inside the current directory. 12 | " NOTE: ctags doesn't cut it here, because it would return at most one tag per line. 13 | fun! local#markdown#tags(base) 14 | let l:pattern = "' " .. (a:base == '#' ? '#[a-z]' : a:base) .. "[a-z0-9]*'" 15 | silent return systemlist(executable('rg') 16 | \ ? "rg -o --no-line-number --no-heading --trim -I " .. l:pattern .. " **/*.md|sort|uniq" 17 | \ : "grep -h -o " .. l:pattern .. " **/*.md|sed 's/^ //'|sort|uniq") 18 | endf 19 | 20 | " Suggest notes (i.e., Markdown files) in the current directory after [[ or tags after #. 21 | fun! local#markdown#complete(findstart, base) 22 | if a:findstart 23 | let l:col = match(getline('.'), '\%(#[a-z]*\|[[\zs\S*\)\%' .. col('.') .. 'c') 24 | return l:col == -1 ? -3 : l:col 25 | else 26 | if a:base =~# '^#' 27 | return local#markdown#tags(a:base) 28 | else 29 | let s:matches = local#markdown#notes(a:base) 30 | return map(s:matches, '{"word": fnamemodify(v:val, ":t"), "abbr": v:val}') 31 | endif 32 | endif 33 | endf 34 | 35 | -------------------------------------------------------------------------------- /autoload/local/msg.vim: -------------------------------------------------------------------------------- 1 | fun! s:msg(mode, message) 2 | redraw 3 | echo "\r" 4 | execute 'echohl' a:mode 5 | echomsg a:message 6 | echohl None 7 | endf 8 | 9 | fun! local#msg#notice(m) 10 | call s:msg('ModeMsg', a:m) 11 | endf 12 | 13 | fun! local#msg#warn(m) 14 | call s:msg('WarningMsg', a:m) 15 | endf 16 | 17 | fun! local#msg#err(m) 18 | call s:msg('Error', a:m) 19 | endf 20 | 21 | -------------------------------------------------------------------------------- /autoload/local/run.vim: -------------------------------------------------------------------------------- 1 | " Send the output of a Vim command to a new scratch buffer. 2 | " 3 | " Example: :call local#run#vim_cmd('digraphs') 4 | fun! local#run#vim_cmd(cmd) 5 | botright 10new 6 | setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile nowrap 7 | call append(0, split(execute(a:cmd), "\n")) 8 | endf 9 | 10 | " Synchronously run a non-interactive shell command and send its output to 11 | " a new scratch buffer. By default, the command is run in the directory of the 12 | " current buffer. 13 | " 14 | " cmd: a List whose first item is the command name and the remaining items are 15 | " the command's arguments 16 | " ...: a Dictionary with additional options: 17 | " 'cwd': change to this directory before running the command 18 | " 'pos': Vim command specifying where the output window should be opened. 19 | fun! local#run#cmd(cmd, ...) abort 20 | let l:opt = get(a:000, 0, {}) 21 | if !has_key(l:opt, 'cwd') 22 | let l:opt['cwd'] = fnameescape(expand('%:p:h')) 23 | endif 24 | let l:cmd = join(map(a:cmd, 'v:val !~# "\\v^[%#<]" || expand(v:val) == "" ? v:val : shellescape(expand(v:val))')) 25 | execute get(l:opt, "pos", "botright") "new" 26 | setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile nowrap 27 | nnoremap q c 28 | execute 'lcd' l:opt['cwd'] 29 | execute '%!' l:cmd 30 | endf 31 | 32 | if exists("*job_start") 33 | 34 | " Run a background job, connecting its stdout to a buffer called STDOUT and 35 | " its stderr to a buffer called STDERR (create or clear the buffers before 36 | " running the job). Optionally, invoke a callback when the job is done. This 37 | " is useful for stuff like TeX typesetting or any other command that does 38 | " not require user interaction. 39 | " 40 | " cmd: the command to be execute 41 | " ...: an optional Dictionary with any of the following keys: 42 | " 'cwd': specify the working directory (default: getcwd()); 43 | " 'cb': an exit callback; 44 | " 'args': optional List of arguments for the callback; 45 | " 'fg': if set to 1, show the stdout buffer (default: 0). 46 | fun! local#run#job(cmd, ...) 47 | for l:buf in ['^STDOUT$', '^STDERR$'] 48 | call local#buffer#clear(l:buf) 49 | endfor 50 | let l:opt = get(a:000, 0, {}) 51 | let l:job = job_start(a:cmd, { 52 | \ "cwd" : get(l:opt, "cwd", getcwd()), 53 | \ "close_cb": "local#run#close_cb", 54 | \ "exit_cb" : function(get(l:opt, "cb", "local#run#callback"), 55 | \ get(l:opt, 'args', [bufnr('%')])), 56 | \ "in_io" : "null", 57 | \ "out_io" : "buffer", 58 | \ "out_name": "STDOUT", 59 | \ "err_io" : "buffer", 60 | \ "err_name": "STDERR" }) 61 | if get(l:opt, 'fg', 0) && bufwinnr(ch_getbufnr(l:job, "out")) < 0 " If the buffer is not visible 62 | execute "botright 10split +buffer".ch_getbufnr(l:job, "out") 63 | nnoremap q c 64 | wincmd p 65 | endif 66 | return l:job 67 | endf 68 | 69 | fun! local#run#close_cb(channel) 70 | call job_status(ch_getjob(a:channel)) " Trigger exit_cb's callback 71 | endf 72 | 73 | fun! local#run#callback(bufnr, job, status) 74 | if a:status == 0 75 | call local#msg#notice("Success!") 76 | else 77 | call local#msg#err("Job failed.") 78 | endif 79 | endf 80 | 81 | else 82 | 83 | fun! local#run#job(cmd, ...) 84 | call local#msg#err('Function not implemented.') 85 | endf 86 | 87 | endif 88 | 89 | -------------------------------------------------------------------------------- /autoload/local/search.vim: -------------------------------------------------------------------------------- 1 | " Find all occurrences of a pattern in the current buffer. 2 | " Collect the result in the location list. 3 | fun! local#search#buffer(pattern) 4 | if getbufvar(winbufnr(winnr()), "&ft") ==# "qf" 5 | call local#msg#warn("Cannot search the quickfix window") 6 | return 7 | endif 8 | try 9 | silent noautocmd execute printf("lvimgrep /%s/gj %s", a:pattern, fnameescape(expand("%"))) 10 | catch /^Vim\%((\a\+)\)\=:E480/ " Pattern not found 11 | call local#msg#warn("No match") 12 | endtry 13 | botright lwindow 14 | endf 15 | 16 | " Find all occurrences of a pattern in all open buffers. 17 | " Show the result in the quickfix window. 18 | fun! local#search#all_buffers(pattern) 19 | " Get the list of open files 20 | let l:files = map(filter(range(1, bufnr('$')), 'buflisted(v:val) && !empty(bufname(v:val))'), 'fnameescape(bufname(v:val))') 21 | cexpr [] " Clear quickfix list 22 | try 23 | silent noautocmd execute printf("vimgrepadd /%s/gj %s", a:pattern, join(l:files)) 24 | catch /^Vim\%((\a\+)\)\=:E480/ " Pattern not found 25 | call local#msg#warn("No match") 26 | endtry 27 | botright cwindow 28 | endf 29 | 30 | fun! local#search#choose_dir(...) " ... is an optional prompt 31 | let l:idx = inputlist([get(a:000, 0, "Change directory to:"), "1. ".getcwd(), "2. ".expand("%:p:h"), "3. Other"]) 32 | let l:dir = (l:idx == 1 ? getcwd() : (l:idx == 2 ? expand("%:p:h") : (l:idx == 3 ? fnamemodify(input("Directory: ", "", "file"), ':p') : ""))) 33 | if strlen(l:dir) <= 0 34 | call local#msg#notice("Cancelled.") 35 | return '' 36 | endif 37 | return l:dir 38 | endf 39 | 40 | fun! local#search#grep(args) 41 | if getcwd() != expand("%:p:h") 42 | let l:dir = local#search#choose_dir() 43 | if empty(l:dir) | return | endif 44 | execute 'lcd' l:dir 45 | endif 46 | let l:chars = (&grepprg =~# 'internal' ? '#' : '#%') 47 | let l:rg = (&grepprg =~# '^\f*rg\s') 48 | execute 'silent grep!' shellescape(escape(a:args, l:chars)) (l:rg ? '': '**/*') 49 | botright cwindow 50 | redraw! 51 | endf 52 | 53 | fun! s:get_ff_output(inpath, outpath, callback, channel, status) 54 | let l:output = filereadable(a:outpath) ? readfile(a:outpath) : [] 55 | silent! call delete(a:outpath) 56 | silent! call delete(a:inpath) 57 | call function(a:callback)(l:output) 58 | endf 59 | 60 | for s:ff_bin in ['sk', 'fzf', 'fzy', 'selecta', 'pick', ''] " Sort according to your preference 61 | if executable(s:ff_bin) 62 | break 63 | endif 64 | endfor 65 | 66 | " Fuzzy filter a list and return a List of selected items. 67 | " 'input' is either a shell command that sends its output, one item per line, 68 | " to stdout, or a List of items to be filtered. 69 | fun! local#search#fuzzy(input, callback, prompt) 70 | if empty(s:ff_bin) " Fallback 71 | return zeef#open(type(a:input)) == 1 ? systemlist(a:input) : a:input, a:callback, a:prompt) 72 | endif 73 | 74 | let l:ff_cmds = { 75 | \ 'fzf': "|fzf -m --height 15 --prompt '" .. a:prompt .. "> ' 2>/dev/tty", 76 | \ 'fzy': "|fzy --lines=15 --prompt='" .. a:prompt .. "> ' 2>/dev/tty", 77 | \ 'pick': "|pick -X", 78 | \ 'selecta': "|selecta 2>/dev/tty", 79 | \ 'sk': "|sk -m --height 15 --prompt '" .. a:prompt .. "> '" 80 | \ } 81 | 82 | let l:ff_cmd = l:ff_cmds[s:ff_bin] 83 | 84 | if type(a:input) ==# 1 " v:t_string 85 | let l:inpath = '' 86 | let l:cmd = a:input .. l:ff_cmd 87 | else " Assume List 88 | let l:inpath = tempname() 89 | call writefile(a:input, l:inpath) 90 | let l:cmd = 'cat ' .. fnameescape(l:inpath) .. l:ff_cmd 91 | endif 92 | 93 | if !has('gui_running') && executable('tput') && filereadable('/dev/tty') 94 | let l:output = systemlist(printf('tput cup %d >/dev/tty; tput cnorm >/dev/tty; %s', &lines, l:cmd)) 95 | redraw! 96 | silent! call delete(a:inpath) 97 | call function(a:callback)(l:output) 98 | return 99 | endif 100 | 101 | let l:outpath = tempname() 102 | let l:cmd ..= " >" .. fnameescape(l:outpath) 103 | 104 | if has('terminal') 105 | botright 15split 106 | call term_start([&shell, &shellcmdflag, l:cmd], { 107 | \ "term_name": a:prompt, 108 | \ "curwin": 1, 109 | \ "term_finish": "close", 110 | \ "exit_cb": function('s:get_ff_output', [l:inpath, l:outpath, a:callback]) 111 | \ }) 112 | else 113 | silent execute '!' .. l:cmd 114 | redraw! 115 | call s:get_ff_output(l:inpath, l:outpath, a:callback, -1, v:shell_error) 116 | endif 117 | endf 118 | 119 | " Fuzzy filter a list of paths and populate the arglist with the selected items. 120 | fun! local#search#fuzzy_arglist(input) 121 | call local#search#fuzzy(a:input, 'zeef#set_arglist', 'Choose files') 122 | endf 123 | 124 | fun! local#search#fuzzy_files(...) " ... is an optional directory 125 | let l:dir = (a:0 > 0 ? ' ' .. a:1 : ' .') 126 | call local#search#fuzzy_arglist(executable('rg') ? 'rg --files' .. l:dir : 'find' .. l:dir .. ' -type f') 127 | endf 128 | 129 | -------------------------------------------------------------------------------- /autoload/local/tags.vim: -------------------------------------------------------------------------------- 1 | " Jump from the current file to related files based on file suffixes (e.g., 2 | " from Foo.cpp to Foo.hpp and vice versa). 3 | " 4 | " Note: Requires a `tags` file created by `ctags` with the option `--extra=+f`. 5 | " 6 | " The dictionary below maps the suffix of the current file to a list of 7 | " candidate suffixes for alternate files. 8 | fun! local#tags#alt_file() 9 | execute "tjump" '/^'.expand("%:t:r") .. '\.\(' .. join(get( 10 | \ { 11 | \ 'c': ['h'], 12 | \ 'cpp': ['h','hpp'], 13 | \ 'h': ['c','cpp'], 14 | \ 'hpp': ['cpp'], 15 | \ 'vim': ['vim'] 16 | \ }, 17 | \ expand("%:e"), ['UNKNOWN EXTENSION']), '\\|') .. '\)$' 18 | endf 19 | 20 | fun! local#tags#ctags(args) 21 | if empty(tagfiles()) " New tags file 22 | let l:dir = local#search#choose_dir('Create tags in:') 23 | if empty(l:dir) | return | endif 24 | let l:dirs = [fnamemodify(l:dir, ':p')] 25 | else 26 | let l:dirs = map(tagfiles(), { i,v -> fnamemodify(v, ':p:h') }) 27 | endif 28 | for l:tagdir in l:dirs 29 | if !isdirectory(l:tagdir) 30 | call local#msg#warn("Directory " .. l:tagdir .. " does not exist.") 31 | return 32 | endif 33 | call local#msg#notice('Tagging ' .. l:tagdir) 34 | let s:res = local#run#job(['ctags', 35 | \ '-R', 36 | \ '--sort=foldcase', 37 | \ '--extra=+fq', 38 | \ '--fields=+iaS', 39 | \ '--c++-kinds=+p', 40 | \ '--exclude=cache', 41 | \ '--exclude=third_party', 42 | \ '--exclude=tmp', 43 | \ '--exclude=*.html'] + split(a:args), 44 | \ { 'cwd': l:tagdir }) 45 | endfor 46 | endf 47 | 48 | fun! local#tags#cscope(args) 49 | let l:dir = getcwd() 50 | if l:dir != expand("%:p:h") 51 | let l:dir = local#search#choose_dir() 52 | if empty(l:dir) | return | endif 53 | endif 54 | let s:res = local#run#job(['cscope', '-R', '-q', '-b'] + split(a:args), {'cwd': l:dir}) 55 | endf 56 | 57 | fun! local#tags#load_cscope_db() 58 | let l:db = findfile("cscope.out", ".;") " See :h findfile() 59 | if !empty(l:db) 60 | execute "cscope add" fnameescape(l:db) 61 | endif 62 | return !empty(l:db) 63 | endf 64 | 65 | " Adapted from CtrlP's buffertag.vim 66 | let s:types = { 67 | \ 'ant': '%sant', 68 | \ 'asm': '%sasm', 69 | \ 'aspperl': '%sasp', 70 | \ 'aspvbs': '%sasp', 71 | \ 'awk': '%sawk', 72 | \ 'beta': '%sbeta', 73 | \ 'c': '%sc', 74 | \ 'cpp': '%sc++', 75 | \ 'cs': '%sc#', 76 | \ 'cobol': '%scobol', 77 | \ 'context': '%scontext', 78 | \ 'delphi': '%spascal', 79 | \ 'dosbatch': '%sdosbatch', 80 | \ 'eiffel': '%seiffel', 81 | \ 'erlang': '%serlang', 82 | \ 'expect': '%stcl', 83 | \ 'fortran': '%sfortran', 84 | \ 'go': '%sgo', 85 | \ 'html': '%shtml', 86 | \ 'java': '%sjava', 87 | \ 'javascript': '%sjavascript', 88 | \ 'lisp': '%slisp', 89 | \ 'lua': '%slua', 90 | \ 'make': '%smake', 91 | \ 'markdown': '%smarkdown', 92 | \ 'matlab': '%smatlab', 93 | \ 'mf': '%smetapost', 94 | \ 'mp': '%smetapost', 95 | \ 'ocaml': '%socaml', 96 | \ 'pascal': '%spascal', 97 | \ 'perl': '%sperl', 98 | \ 'php': '%sphp', 99 | \ 'python': '%spython', 100 | \ 'rexx': '%srexx', 101 | \ 'rmd': '%srmarkdown', 102 | \ 'ruby': '%sruby', 103 | \ 'rust': '%srust', 104 | \ 'scheme': '%sscheme', 105 | \ 'sh': '%ssh', 106 | \ 'csh': '%ssh', 107 | \ 'zsh': '%ssh', 108 | \ 'scala': '%sscala', 109 | \ 'slang': '%sslang', 110 | \ 'sml': '%ssml', 111 | \ 'sql': '%spgsql', 112 | \ 'tex': '%slatex', 113 | \ 'tcl': '%stcl', 114 | \ 'vera': '%svera', 115 | \ 'verilog': '%sverilog', 116 | \ 'vhdl': '%svhdl', 117 | \ 'vim': '%svim', 118 | \ 'yacc': '%syacc', 119 | \ } 120 | 121 | call map(s:types, 'printf(v:val, "--language-force=")') 122 | 123 | fun! local#tags#file_tags(path, ft) 124 | return systemlist('ctags -f - --sort=no --excmd=number --fields= --extra= --file-scope=yes ' 125 | \ .. get(s:types, a:ft, '') .. ' ' 126 | \ .. shellescape(expand(a:path))) 127 | endf 128 | -------------------------------------------------------------------------------- /autoload/local/term.vim: -------------------------------------------------------------------------------- 1 | if has('terminal') " Vim 8 or later, MacVim 2 | 3 | " Asynchronously run an interactive shell command in a terminal window. 4 | " By default, the command is run in the directory of the current buffer, and 5 | " the terminal window is closed as soon as the job is done. 6 | " 7 | " cmd: the command to be run with its arguments, as a List 8 | " ...: an optional Dictionary of additional options, which are passed to 9 | " term_start(). 10 | " 11 | " Returns: the buffer number of the terminal window. 12 | fun! local#term#run(cmd, ...) abort 13 | let l:bufnr = term_start(a:cmd, extend({ 14 | \ 'cwd': expand('%:p:h'), 15 | \ 'term_rows': 20, 16 | \ }, get(a:000, 0, {}))) 17 | return l:bufnr 18 | endf 19 | 20 | fun! local#term#send_keys(what) 21 | call term_sendkeys('', a:what) 22 | return '' 23 | endf 24 | 25 | " Open a new terminal buffer and bind it to the current buffer 26 | fun! local#term#open() 27 | let l:term_id = term_start(&shell, {'term_name': 'Terminal'}) 28 | wincmd p 29 | let b:lf_bound_terminal = l:term_id 30 | endf 31 | 32 | fun! local#term#send(lines) 33 | if empty(get(b:, 'lf_bound_terminal', '')) || !bufexists(b:lf_bound_terminal) 34 | let b:lf_bound_terminal = str2nr(input('Terminal buffer: ')) 35 | endif 36 | for l:line in a:lines 37 | if &ft == 'outlaw' 38 | let l:line = substitute(l:line, '^\s*|\s*', '', '') " Remove leading | 39 | endif 40 | call term_sendkeys(b:lf_bound_terminal, l:line .. "\r") 41 | call s:term_wait(b:lf_bound_terminal) 42 | endfor 43 | call cursor(line('.') + len(a:lines), 1) 44 | endf 45 | 46 | if has('gui_running') 47 | fun! s:term_wait(bn) 48 | endf 49 | else 50 | fun! s:term_wait(bn) 51 | call term_wait(a:bn) 52 | endf 53 | endif 54 | 55 | fun! local#term#enter_normal_mode() 56 | return &buftype ==# 'terminal' && mode('') ==# 't' ? "\N\" : '' 57 | endf 58 | 59 | fun! local#term#toggle_scrollwheelup() 60 | if maparg('', 't') ==# '' 61 | tnoremap local#term#enter_normal_mode() 62 | else 63 | tunmap 64 | endif 65 | endf 66 | 67 | else 68 | 69 | fun! local#term#run(cmd, ...) 70 | call local#msg#err("Function non implemented") 71 | endf 72 | 73 | if !empty($TMUX) 74 | 75 | fun! local#term#open() 76 | call system('tmux split-window') 77 | call system('tmux last-pane') 78 | endf 79 | 80 | " Send the given text to a tmux pane 81 | fun! local#term#send(lines) 82 | if !exists('b:lf_bound_terminal') || empty(b:lf_bound_terminal) 83 | let b:lf_bound_terminal = input('Tmux pane number: ') 84 | endif 85 | for line in a:lines 86 | if &ft == 'outlaw' 87 | let l:line = substitute(l:line, '^\s*|\s*', '', '') " Remove leading | 88 | endif 89 | call system('tmux -u send-keys -l -t ' .. b:lf_bound_terminal .. ' "" ' .. shellescape(line .. "\r")) 90 | endfor 91 | endf 92 | 93 | else 94 | 95 | fun! local#term#open() 96 | call local#msg#err("Function non implemented") 97 | endf 98 | 99 | fun! local#term#send(lines) 100 | call local#msg#err("Function non implemented") 101 | endf 102 | 103 | endif 104 | 105 | endif 106 | 107 | -------------------------------------------------------------------------------- /autoload/local/tex.vim: -------------------------------------------------------------------------------- 1 | fun! local#tex#file(suffix) 2 | return expand('%:p:r') . '.' . a:suffix 3 | endf 4 | 5 | if has('mac') 6 | let s:default_viewer = 'TeXShop' 7 | else 8 | let s:default_viewer = 'Okular' 9 | endif 10 | 11 | fun! s:view_in_texshop(f) 12 | silent execute '!open -a TeXShop.app ' .. a:f .. ' >/dev/null 2>&1' 13 | endf 14 | 15 | " To make forward/backward search work in TeXShop, you need the following: 16 | " 17 | " 1. Create /usr/local/bin/othereditor (the name cannot be changed) with the 18 | " following content: 19 | " 20 | " #!/bin/sh 21 | " /usr/local/bin/mvim --remote-silent +"$1" "$2" 22 | " 23 | " 2. Create /usr/local/bin/texshop.sh, with the following content 24 | " (see TeXShop.app > Help > Changes and search for 'sync_preview'): 25 | " 26 | " #!/bin/bash 27 | " MyShellVar=$1 28 | " MyShellVas=$2 29 | " MyShellVat=$3 30 | " osascript <