はじめに
この記事はクソコードお焚き上げの会 Advent Calendar 2023の15日目の記事です。
vimrcにたまった機能の掃除ついでに、入れてるプラグイン一覧紹介します。
環境 | |
---|---|
PC | MacBook Pro M2 |
OS | macOS 14.1.1(23B81) |
editor | vim 9.0 |
🧹大掃除🧹
クソコードの中でも、なげぇコードを供養しました。
まずはこちらのレポのファイルをご覧ください。
こちらが対象のファイルです。
3666 lines (3356 loc) · 136 KB
さ、さささ三千行!!?!?
おおすぎる!!!😣😣😣
これは『プラグインが使えない環境』を想定し
たとえGitBash
のMinGW
だとしても、プラグインありみたいなvimを使いたい!
というわがままから来ています。
様々なプラグイン機能をvimscript
で真似してつくり、ぶち込みまくっていました。
独自実装で置き換えていたもの
- simeji/winresizer
- jiangmiao/auto-pairs
- tpope/vim-surround
- vim-airline/vim-airline
- junegunn/fzf.vim
- junegunn/fzf
- preservim/nerdtree
- yuttie/comfortable-motion.vim
- easymotion/vim-easymotion
- unblevable/quick-scope
- t9md/vim-quickhl
- MattesGroeger/vim-bookmarks
- junegunn/goyo.vim
- mhinz/vim-startify
- markonm/traces.vim
- thinca/vim-quickrun
- joshdick/onedark.vim
独自で作っていた機能
- Running Cat (猫が走るアニメーションする)
- Popup terminal/git (ポップアップでterminalやlazygitする)
- Tab Anchor (5行移動と、アンカーをサイン)
- Search Current File fzf (現在バッファ内をfzfポップアップ検索)
- Grep Recursive Interactive (対話で範囲等を選択しGrep)
- IDE menu (IDE系のあんま使わない機能をポップアップメニュー)
- Completion (補完リソースは全範囲、重い)
- PlugInstall / PlugUnInstall (junegunn/vim-plugの真似ではある)
- Training Wheels Protocol (チュートリアルアニメーション)
供養する
一部を紹介していきます。長いので基本的にソースは折りたたみます。
vim-airline/vim-airline
airlineもですが、vim-fugitiveのようにgit statusを入れ込むこともしています。
が、なんだかうまくいってない部分もあります。
我らがtpop
氏はさすがですね、スター送りましょう。
プログラム
" {{{
let g:right_arrow = ''
let g:left_arrow = ''
let powerline_chk_mac = system('fc-list | grep powerline | wc -l')->trim()
let powerline_chk_win = system('cd /C/Windows/Fonts && ls | grep powerline | wc -l')->trim()
if !powerline_chk_mac+0 && !powerline_chk_win+0
let g:right_arrow = ' '
let g:left_arrow = ' '
endif
let g:modes = {
\ 'i': ['#User_blackfg_bluebg_bold#', '#User_bluefg_blackbg#', 'INSERT'],
\ 'n': ['#User_blackfg_greenbg_bold#', '#User_greenfg_blackbg#', 'NORMAL'],
\ 'R': ['#User_blackfg_redbg_bold#', '#User_redfg_blackbg#', 'REPLACE'],
\ 'c': ['#User_blackfg_greenbg_bold#', '#User_greenfg_blackbg#', 'COMMAND'],
\ 't': ['#User_blackfg_redbg_bold#', '#User_redfg_blackbg#', 'TERMIAL'],
\ 'v': ['#User_blackfg_pinkbg_bold#', '#User_pinkfg_blackbg#', 'VISUAL'],
\ 'V': ['#User_blackfg_pinkbg_bold#', '#User_pinkfg_blackbg#', 'VISUAL'],
\ "\<C-v>": ['#User_blackfg_pinkbg_bold#', '#User_pinkfg_blackbg#', 'VISUAL'],
\ }
let g:ff_table = {'dos' : 'CRLF', 'unix' : 'LF', 'mac' : 'CR'}
let g:gitinf = 'no git '
fu! s:gitinfo() abort
if !executable('git')
retu
endif
let status = system('cd '.expand('%:h').' && git status')->trim()
if status =~ "^fatal:"
let g:gitinf = 'no repo '
retu
endif
" TODO airline gitstatus結果を使いまわした方が早い? -> いいだろべつに
" TODO airline ls-filesに変える
" TODO airline async refresh
" TODO airline once get system cmd, trim it on vimscript
" TODO terminalから戻った時とかに、表示変になるぞ
let cmd = "cd ".expand('%:h')." && git status --short | awk -F ' ' '{print($1)}' | grep -c "
let a = trim(system(cmd."'A'"))
let aa = a !='0'?'+'.a :''
let m = trim(system(cmd."-e 'M' -e 'D'"))
let mm = m !='0'?'!'.m :''
let nw = trim(system(cmd."'??'"))
let nwnw = nw !='0'?'?'.nw :''
let er = trim(system(cmd."'U'"))
let ee = er !='0'?'✗'.er :''
let g:gitinf = trim(system("cd ".expand('%:h')." && git branch | awk -F '*' '{print($2)}'")).join([aa,mm,nwnw,ee],' ')
endf
fu! g:SetStatusLine() abort
let mode = get(g:modes, mode(), ['#User_blackfg_redbg_bold#', '#User_redfg_blackbg#', 'SP'])
" start menu BTR
if &filetype == 'Bocchi_The_Rock'
let mode = ['#User_blackfg_greenbg_bold#', '#User_greenfg_blackbg#', 'BTR_start_menu']
endif
retu '%'.mode[0].' '.mode[2].' '.'%'.mode[1].g:right_arrow.'%#User_blackfg_graybg#'.g:right_arrow
\ .'%#User_greenfg_graybg# %<%f%m%r%h%w %#User_grayfg_blackbg#'.g:right_arrow
\ .'%#User_greenfg_blackbg# %{g:gitinf}%*'.g:right_arrow
\ .'%* %='
\ .'%*'.g:left_arrow.'%#User_greenfg_blackbg# %{&filetype}'
\ .' %#User_grayfg_blackbg#'.g:left_arrow.'%#User_greenfg_graybg# %p%% %l/%L %02v%#User_blackfg_graybg#'.g:left_arrow
\ .'%'.mode[1].g:left_arrow.'%'.mode[0].' [%{&fenc!=""?&fenc:&enc}][%{g:ff_table[&ff]}] %*'
endf
set stl=%!g:SetStatusLine()
" tabline
fu! s:buffers_label() abort
" airline TODO リファクタ
let b = ''
for v in split(execute('ls'), '\n')->map({ _,v -> split(v, ' ')})
let x = copy(v)->filter({ _,v -> !empty(v) })
if stridx(x[1], 'F') == -1 && stridx(x[1], 'R') == -1
let hi = stridx(x[1], '%') != -1 ? '%#User_blackfg_greenbg#' : '%#User_greenfg_graybg#'
let hiar = stridx(x[1], '%') != -1 ? '%#User_greenfg_blackbg#' : '%#User_grayfg_blackbg#'
let hiarb = stridx(x[1], '%') != -1 ? '%#User_blackfg_greenbg#' : '%#User_blackfg_graybg#'
if x[2] == '+'
let hi = '%#User_blackfg_bluebg#'
let hiar = '%#User_bluefg_blackbg#'
let hiarb = '%#User_blackfg_bluebg#'
endif
"[^/]*$
let f = x[2] == '+' ? '✗'.matchstr(join(split(x[3],'"'),''),'[^/]*$') : matchstr(join(split(x[2],'"'),''),'[^/]*$')
let b = b.'%'.x[0].'T'.hiarb.g:right_arrow.hi.f.hiar.g:right_arrow
endif
endfor
retu b
endf
fu! s:tabpage_label(n) abort
let hi = a:n is tabpagenr() ? '%#User_blackfg_greenbg#' : '%#User_greenfg_graybg#'
let bufnrs = tabpagebuflist(a:n)
let no = len(bufnrs)
if no is 1
let no = ''
endif
let mod = len(filter(copy(bufnrs), 'getbufvar(v:val, "&modified")')) ? '✗' : ''
let fname = pathshorten(bufname(bufnrs[tabpagewinnr(a:n) - 1]))
retu '%'.a:n.'T'.hi.no.mod.fname.' ⁍|'.'%T%#TabLineFill#'
endf
fu! g:SetTabLine() abort
if tabpagenr('$') == 1
retu s:buffers_label()
endif
retu range(1,tabpagenr('$'))->map('s:tabpage_label(v:val)')->join(' ').' %#TabLineFill#%T'
endf
set tabline=%!g:SetTabLine()
fu! s:moveBuf(flg) abort
let current_id = ''
let buf_arr = []
for v in split(execute('ls'), '\n')->map({ _,v -> split(v, ' ')})
let x = copy(v)->filter({ _,v -> !empty(v) })
if stridx(x[1], 'F') == -1 && stridx(x[1], 'R') == -1
cal add(buf_arr, x[0])
if stridx(x[1], '%') != -1
let current_id = x[0]
endif
endif
endfor
let buf_idx = a:flg == 'next' ? match(buf_arr, current_id) + 1 : match(buf_arr, current_id) - 1
let buf_id = buf_idx == len(buf_arr) ? buf_arr[0] : buf_arr[buf_idx]
exe 'b '.buf_id
endf
fu! s:closeBuf() abort
let now_b = bufnr('%')
" TODO airline not key, you should call function
execute("norm \<C-n>")
execute('bd ' . now_b)
endf
aug user_onedark
au!
au ColorScheme * hi User_greenfg_blackbg ctermfg=114 ctermbg=235
au ColorScheme * hi User_greenfg_graybg ctermfg=114 ctermbg=238
au ColorScheme * hi User_bluefg_blackbg ctermfg=39 ctermbg=235
au ColorScheme * hi User_pinkfg_blackbg ctermfg=169 ctermbg=235
au ColorScheme * hi User_redfg_blackbg ctermfg=203 ctermbg=235
au ColorScheme * hi User_grayfg_blackbg ctermfg=238 ctermbg=235
au ColorScheme * hi User_blackfg_graybg ctermfg=235 ctermbg=238
au ColorScheme * hi User_blackfg_greenbg ctermfg=235 ctermbg=114
au ColorScheme * hi User_blackfg_greenbg_bold cterm=bold ctermfg=234 ctermbg=114
au ColorScheme * hi User_blackfg_bluebg ctermfg=235 ctermbg=39
au ColorScheme * hi User_blackfg_bluebg_bold cterm=bold ctermfg=234 ctermbg=39
au ColorScheme * hi User_blackfg_pinkbg_bold cterm=bold ctermfg=234 ctermbg=170
au ColorScheme * hi User_blackfg_redbg_bold cterm=bold ctermfg=234 ctermbg=204
aug END
aug status_tabLine
au!
au BufWinEnter,BufWritePost * cal s:gitinfo()
aug END
noremap <silent><Plug>(buf-prev) :<C-u>cal <SID>moveBuf('prev')<CR>
noremap <silent><Plug>(buf-next) :<C-u>cal <SID>moveBuf('next')<CR>
noremap <silent><Plug>(buf-close) :<C-u>cal <SID>closeBuf()<CR>
" }}}
これは、ステータスラインに表示するgit status
を、VSCodeチックにしたりと様々工夫をしましたが
gitレポでないときのハンドリングが不十分だったり、vimモードの別ウィンドウのモードが反映されちゃったりといろいろ中途半端でした。
やっぱ本家のairlineはすごいんだなと思わされた逸品。
junegunn/fzf.vim
および、yuki-yano/fzf-preview.vimを真似しました。
正直、完成度はかなり高いと思っていますが、本家がterminal
でのポップアップなのに対し
vimのpopup
で無理やり再現しているため、もろもろの小さな手の届かなさが…。
さすがはvim-plugやgoyo(禅モード)なども手掛けているjunegunn
さんの最高傑作(私談)です。スター送りましょう。
プログラム
" ===================================================================
" junegunn/fzf.vim
" ===================================================================
" {{{
" usage
" let arguments_def = #{
" \ title: popup title as String,
" \ list: search targets as List,
" \ type: format in list. f, lm, flm (file, line, msg),
" \ eprfx: enter zone prefix text as String (0 to default value),
" \ }
" and call like this.
" cal s:fzsearch.popup(arguments_def)
let s:fzsearch = #{bwid: 0, ewid: 0, rwid: 0, pwid: 0, tid: 0, res: [],
\ ffdict: #{vimrc: 'vim', zshrc: 'sh', js: 'javascript', py: 'python', md: 'markdown',
\ ts: 'typescript', tsx: 'typescriptreact',
\ },
\ }
let s:fzsearch.allow_exts = glob($VIMRUNTIME.'/ftplugin/*.vim')->split('\n')
\ ->map({_,v->matchstr(v,'[^/]\+\.vim')->split('\.')[0]})
fu! s:fzsearch.popup(v) abort
if empty(a:v.list) || (len(a:v.list) == 1 && empty(a:v.list[0]))
cal s:echoE('no data')
retu
endif
let self.list = a:v.list
let self.type = a:v.type
" TODO depends on type
let self.prr = a:v.eprfx
let self.wd = ''
let self.wda = []
let self.ridx = 0
let self.max = 30
let self.mode = 'Fuzzy'
let self.pr = '['.self.mode.']'.(self.prr ?? '>>')
let self.res = a:v.list[0:self.max]
let self.bwid = popup_create([], #{title: ' '.a:v.title.' ',
\ zindex: 50, mapping: 0, scrollbar: 0,
\ border: [], borderchars: ['─','│','─','│','╭','╮','╯','╰'], borderhighlight: ['FzBWin'],
\ minwidth: &columns*9/12, maxwidth: &columns*9/12,
\ minheight: &lines/2+6, maxheight: &lines/2+6,
\ line: &lines/4-2, col: &columns/8+1,
\ })
let self.ewid = popup_create(self.pr, #{title: ' Search Mode: <Tab> | ClearText: <C-w> ',
\ zindex: 100, mapping: 0,
\ border: [], borderchars: ['─','│','─','│','╭','╮','╯','╰'], borderhighlight: ['FzEWin'],
\ minwidth: &columns/3, maxwidth: &columns/3,
\ minheight: 1, maxheight: 1,
\ line: &lines*3/4+2, col: &columns/7+1,
\ filter: function(self.fil, [#{wd: []}]),
\ })
cal matchaddpos('FzEWin', [[1, 1, len(self.pr)]], 16, -1, #{window: self.ewid})
cal matchadd('DarkRed', '\[.*\]', 17, -1, #{window: self.ewid})
let self.rwid = popup_menu(self.res, #{title: ' Choose: <C-n/p> <CR> | QuickFix: <C-q> ',
\ zindex: 99, mapping: 0, scrollbar: 1,
\ border: [], borderchars: ['─','│','─','│','╭','╮','╯','╰'], borderhighlight: ['FzWin'],
\ minwidth: &columns/3, maxwidth: &columns/3,
\ minheight: &lines/2, maxheight: &lines/2,
\ pos: 'topleft', line: &lines/4, col: &columns/7,
\ callback: 'Fzsearch_confirm',
\ filter: function(self.jk, [0]),
\ })
" result list syntax
if self.type == 'lm'
cal setbufvar(winbufnr(self.rwid), '&filetype', &filetype)
elseif self.type == 'flm'
cal setbufvar(winbufnr(self.rwid), '&filetype', 'txt')
endif
" get preview file
let path = bufname('%')
if self.type =~ 'flm'
let path = split(self.res[0], '|')[0]
elseif self.type =~ 'f'
let path = substitute(self.res[0], '\~', $HOME, 'g')
endif
let read = ['Cannot open file.', 'please check this file path.', path]
try
let read = readfile(path)
catch
endtry
" preview
let self.pwid = popup_menu(read, #{title: ' File Preview | Scroll: <C-d/u> ',
\ zindex: 98, mapping: 0, scrollbar: 1,
\ border: [], borderchars: ['─','│','─','│','╭','╮','╯','╰'], borderhighlight: ['FzWin'],
\ minwidth: &columns/3, maxwidth: &columns/3,
\ minheight: &lines/2+3, maxheight: &lines/2+3,
\ pos: 'topleft', line: &lines/4, col: &columns/2+1,
\ firstline: 1,
\ filter: function(self.pv, [0]),
\ })
" get preview ext for syntax highlight
let ext = matchstr(path, '[^\.]\+$')
let ext = ext !~ '^[a-zA-Z0-9]\+$' ? '' : ext
let ext = get(self.ffdict, ext, ext)
cal setbufvar(winbufnr(self.pwid), '&filetype', self.type =~ 'f' ? ext : &filetype)
" first line
let lnm = 1
if self.type == 'flm'
let sep = split(self.res[0], '|')
let lnm = len(sep) < 2 ? 1 : split(sep[1], ' ')[0]
elseif self.type == 'lm'
let lnm = split(self.res[0], ':')[0]
endif
" cursor line top from 10row
cal popup_setoptions(self.pwid, #{firstline: (lnm-10 > 0 ? lnm-10 : 1)})
cal win_execute(self.pwid, 'exe '.lnm)
endf
" on confirm
fu! Fzsearch_confirm(wid, idx) abort
if a:idx == -1
cal s:echoE('cancel')
cal s:fzsearch.finalize()
retu
endif
let result = s:fzsearch.res[a:idx-1]
if s:fzsearch.type == 'f'
exe 'e '.result
elseif s:fzsearch.type == 'lm'
let l = split(result, ':')[0]
cal s:echoI('jump to '.l)
exe l
if s:fzsearch.mode == 'Simply'
let @/ = s:fzsearch.wd
norm! n
cal s:quickhl.set()
endif
elseif s:fzsearch.type == 'flm'
let sep = split(result, '|')
let fnm = substitute(sep[0], $HOME, '~', 'g')
let lnm = len(sep) < 2 ? 1 : split(sep[1], ' ')[0]
" if quickfix window
if expand('%')->empty()
wincmd w
endif
exe 'e '.fnm
exe lnm
cal s:echoI('jump to '.fnm.' line:'.lnm)
endif
cal s:fzsearch.finalize()
endf
" create quickfix
fu! s:fzsearch.quickfix() abort
let tmp = &errorformat
let resource = self.res
let ef = '%f: %l: %m'
if self.type == 'lm'
let fnm = expand('%')
let resource = deepcopy(self.res)->map({_,v->fnm.': '.v})
elseif self.type == 'f'
let resource = deepcopy(self.res)->map({_,v->v.': 1: open'})
elseif self.type == 'flm'
" sample
" filename|100 col 10-15|message
" want-> filename, 100, message
let resource = deepcopy(self.res)->map({_,v
\ ->split(v,'|')[0].': '
\ .split(split(v,'|')[1], ' ')[0].': '
\ .join(split(v,'|')[2:], '')})
endif
let &errorformat = ef
cgetexpr resource | cw
let &errorformat = tmp
cal s:fzsearch.finalize()
endf
" finalize
fu! s:fzsearch.finalize() abort
let s:fzsearch.list = []
let s:fzsearch.res = []
cal s:runcat.stop()
cal popup_clear()
endf
" preview scroll
fu! s:fzsearch.scroll(wid, vector) abort
if self.tid
retu
endif
cal timer_stop(self.tid)
let vec = a:vector ? "\<C-n>" : "\<C-p>"
let self.tid = timer_start(10, { -> popup_filter_menu(a:wid, vec) }, #{repeat: -1})
let delay = 600
cal timer_start(delay, self.scstop)
endf
fu! s:fzsearch.scstop(_) abort
cal timer_stop(self.tid)
let self.tid = 0
endf
" preview draw update
fu! s:fzsearch.pvupd() abort
let win = winbufnr(self.pwid)
if empty(self.res) && self.type == 'lm'
retu
endif
if self.type == 'lm'
let lnm = split(self.res[self.ridx], ':')[0]
cal popup_setoptions(self.pwid, #{firstline: (lnm-10 > 0 ? lnm-10 : 1)})
cal win_execute(self.pwid, 'exe '.lnm)
retu
endif
" del
sil! cal deletebufline(win, 1, getbufinfo(win)[0].linecount)
" put
if empty(self.res)
retu
endif
let path = self.type == 'flm'
\ ? split(self.res[self.ridx], '|')[0]
\ : substitute(self.res[self.ridx], '\~', $HOME, 'g')
let read = ['Cannot open file.', 'please check this file path.', path]
try
let read = readfile(path)
catch
endtry
cal setbufline(win, 1, read)
" syntax
let ext = matchstr(path, '[^\.]\+$')
let ext = ext !~ '^[a-zA-Z0-9]\+$' ? '' : ext
let ext = get(self.ffdict, ext, ext)
cal setbufvar(win, '&filetype', ext)
" line
let sep = split(self.res[self.ridx], '|')
let lnm = len(sep) < 2 ? 1 : split(sep[1], ' ')[0]
cal popup_setoptions(self.pwid, #{firstline: (lnm-10 > 0 ? lnm-10 : 1)})
cal win_execute(self.pwid, 'exe '.lnm)
endf
" search result update
fu! s:fzsearch.list_upd() abort
" upd match result list
let filterd = empty(self.wd) ? self.list :
\ self.mode == 'Fuzzy' ? matchfuzzy(self.list, self.wd)
\ : copy(self.list)->filter({_,v->match(v, self.wd)!=-1})
""let filterd = copy(self.list)->filter({_,v->match(v, self.wd)!=-1})
let self.res = filterd[0:self.max]
let self.ridx = len(self.res)-1 < self.ridx ? len(self.res)-1 : self.ridx
cal setbufline(winbufnr(self.ewid), 1, self.pr . self.wd)
cal deletebufline(winbufnr(self.rwid), 1, self.max)
echo ''
cal setbufline(winbufnr(self.rwid), 1, self.res)
" highlight match char
cal clearmatches(self.rwid)
if !empty(self.wd)
cal matchadd('FzMatch', self.mode == 'Fuzzy'
\ ? printf('\c[%s]', escape(self.wd, '\[\]\-\.\*'))
\ : '\c'.self.wd,
\ 16, -1, #{window: self.rwid})
endif
" upd preview
cal self.pvupd()
endf
fu! s:fzsearch.togglemode() abort
let self.mode = self.mode == 'Fuzzy' ? 'Simply' : 'Fuzzy'
let self.pr = '['.self.mode.']'.(self.prr ?? '>>')
cal clearmatches(self.ewid)
cal matchaddpos('FzEWin', [[1, 1, len(self.pr)]], 16, -1, #{window: self.ewid})
cal matchadd('DarkRed', '\[.*\]', 17, -1, #{window: self.ewid})
cal self.list_upd()
endf
" enter search word
fu! s:fzsearch.fil(ctx, wid, key) abort
if a:key is# "\<Esc>"
cal popup_close(self.bwid)
cal popup_close(self.pwid)
cal popup_close(self.ewid)
cal feedkeys("\<C-c>")
retu 1
elseif a:key is# "\<C-n>" || a:key is# "\<C-p>" || a:key is# "\<CR>"
" only largest zindex window is active.
cal popup_setoptions(self.rwid, #{zindex: 100})
cal popup_setoptions(self.ewid, #{zindex: 99})
cal popup_setoptions(self.pwid, #{zindex: 98})
cal feedkeys(a:key)
retu 1
elseif a:key is# "\<C-d>" || a:key is# "\<C-u>"
cal popup_setoptions(self.pwid, #{zindex: 100})
cal popup_setoptions(self.ewid, #{zindex: 99})
cal popup_setoptions(self.rwid, #{zindex: 98})
cal feedkeys(a:key)
retu 1
elseif a:key is# "\<Tab>"
cal self.togglemode()
retu 1
elseif a:key is# "\<C-q>"
cal self.quickfix()
cal feedkeys("\<Esc>")
retu 1
" TODO fzf.vim copy shold D-v only or Shift Insert. C-v want split
" TODO fzf.vim add feature: refresh cache (or re search)
elseif a:key is# "\<C-v>" || a:key is# "\<D-v>"
for i in range(0, strlen(@+)-1)
cal add(self.wda, strpart(@+, i, 1))
endfor
" TODO see self
elseif a:key is# "\<BS>" && !empty(self.wda)
unlet self.wda[len(self.wda)-1]
elseif a:key is# "\<BS>" && len(self.wda) == 0
" noop
retu 1
elseif a:key is# "\<C-w>"
let self.wda = []
elseif strtrans(a:key) == "<80><fd>`"
" noop (for polyglot bug adhoc)
retu 1
else
cal add(self.wda, a:key)
endif
let self.wd = join(self.wda, '')
cal self.list_upd()
retu a:key is# "x" || a:key is# "\<Space>" ? 1 : popup_filter_menu(a:wid, a:key)
endf
" choose result
fu! s:fzsearch.jk(_, wid, key) abort
if a:key is# "\<Esc>"
cal popup_close(self.bwid)
cal popup_close(self.pwid)
cal popup_close(self.ewid)
cal feedkeys("\<C-c>")
elseif a:key is# "\<C-n>"
let self.ridx = self.ridx == len(self.res)-1 ? len(self.res)-1 : self.ridx+1
cal self.pvupd()
retu popup_filter_menu(a:wid, a:key)
elseif a:key is# "\<C-p>"
let self.ridx = self.ridx ? self.ridx-1 : 0
cal self.pvupd()
retu popup_filter_menu(a:wid, a:key)
elseif a:key is# "\<CR>"
cal popup_close(self.ewid)
cal popup_close(self.pwid)
cal popup_close(self.bwid)
retu popup_filter_menu(a:wid, empty(self.res) ? "\<C-c>" : a:key)
elseif a:key is# "\<C-d>" || a:key is# "\<C-u>"
cal popup_setoptions(self.pwid, #{zindex: 100})
cal popup_setoptions(self.ewid, #{zindex: 99})
cal popup_setoptions(self.rwid, #{zindex: 98})
cal feedkeys(a:key)
else
cal popup_setoptions(self.ewid, #{zindex: 100})
cal popup_setoptions(self.rwid, #{zindex: 99})
cal popup_setoptions(self.pwid, #{zindex: 98})
cal feedkeys(a:key)
endif
retu 1
endf
" scroll preview
fu! s:fzsearch.pv(_, wid, key) abort
if a:key is# "\<Esc>"
cal popup_close(self.bwid)
cal popup_close(self.pwid)
cal popup_close(self.ewid)
cal feedkeys("\<C-c>")
elseif a:key is# "\<C-d>"
cal self.scroll(a:wid, 1)
elseif a:key is# "\<C-u>"
cal self.scroll(a:wid, 0)
elseif a:key is# "\<C-n>" || a:key is# "\<C-p>" || a:key is# "\<CR>"
cal popup_setoptions(self.rwid, #{zindex: 100})
cal popup_setoptions(self.ewid, #{zindex: 99})
cal popup_setoptions(self.pwid, #{zindex: 98})
cal feedkeys(a:key)
else
cal popup_setoptions(self.ewid, #{zindex: 100})
cal popup_setoptions(self.rwid, #{zindex: 99})
cal popup_setoptions(self.pwid, #{zindex: 98})
cal feedkeys(a:key)
endif
retu 1
endf
" =====================
fu! s:fzf_histories()
cal s:fzsearch.popup(#{title: 'Histories', list: execute('ol')->split('\n')->map({_,v -> split(v, ': ')[1]}),
\ type: 'f', eprfx: '['.substitute(getcwd(), $HOME, '~', 'g').']>>'})
endf
fu! s:fzf_buffers()
cal s:fzsearch.popup(#{title: 'Buffers', list: execute('ls')->split('\n')->map({_,v -> split(v, '"')[1]})
\ ->filter({_,v -> v != '[No Name]' && v != '[無名]' && v !~ '!.*'}),
\ type: 'f', eprfx: '['.substitute(getcwd(), $HOME, '~', 'g').']>>'})
endf
" =====================
aug FzColor
au!
au ColorScheme * hi FzMatch cterm=BOLD cterm=underline ctermfg=196 ctermbg=237
au ColorScheme * hi FzWin ctermfg=114 ctermbg=237
au ColorScheme * hi FzBWin cterm=BOLD ctermfg=145 ctermbg=238
au ColorScheme * hi FzEWin ctermfg=39 ctermbg=237
aug END
noremap <silent><Plug>(fzf-histories) :<C-u>cal <SID>fzf_histories()<CR>
noremap <silent><Plug>(fzf-buffers) :<C-u>cal <SID>fzf_buffers()<CR>
" }}}
junegunn/fzf
作者同様のGo製のコマンドですね。
こんな神コマンドを真似なんて本来できないんですが、gitレポじゃなくてgit ls-files
が使えない場合にファイラが開けないのは致命的なので
find
コマンドでどうにか似たような挙動を再現しました。(使えなくはないレベル。)
やっていることは、find結果を1階層ごとに非同期プロセスで返し次第、fzf.vim
のポップアップにわたす感じです。
プログラム
" ===================================================================
" junegunn/fzf
" ===================================================================
" {{{
let s:fzf = #{cache: [], maxdepth: 4, gcache: [],
\ not_path_arr: [
\'"*/.**/*"',
\'"*node_modules/*"', '"*target/*"'
\'"*Applications/*"', '"*AppData/*"', '"*Library/*"',
\'"*Music/*"', '"*Pictures/*"', '"*Movies/*"', '"*Videos/*"'
\'"*OneDrive/*"',
\ ],
\}
" TODO fzf delete
fu! TestFzfMaxDepth(n) abort
let s:fzf.maxdepth = a:n
endf
" TODO fzf maxdepth かえるコマンド作るか
let s:fzf.postfix = ' -type f -not -path '.join(s:fzf.not_path_arr, ' -not -path ')
let s:fzf.searched = getcwd()
let s:fzf.get_gitls = {-> system('git ls-files -c')->split('\n')->filter({_,v->!empty(v)}) }
let s:fzf.get_file_d1 = {-> system('find . -mindepth 1 -maxdepth 1'.s:fzf.postfix)->split('\n')->filter({_,v->!empty(v)}) }
let s:fzf.get_file = { v -> 'find . -mindepth '.v.' -maxdepth '.v.s:fzf.postfix }
fu! s:fzf.files() abort
let pwd = getcwd()
let chk = system('git status')
let self.is_git = v:shell_error ? 0 : 1
" moved or first
if stridx(pwd, self.searched) == -1
"\ || (self.is_git && empty(self.gcache))
\ || (self.is_git)
\ || (!self.is_git && empty(self.cache))
let self.searched = pwd
if self.is_git
" TODO fzf no need cache?
let self.gcache = self.get_gitls()
cal s:echoI('cache: git ls-files -c')
else
let self.cache = self.get_file_d1()
cal self.asyncfind()
endif
endif
cal s:fzsearch.popup(#{title: self.is_git ? 'Project Files' : 'Files',
\ list: self.is_git ? self.gcache : self.cache,
\ type: 'f', eprfx: '['.substitute(pwd, $HOME, '~', 'g').']>>'})
endf
fu! s:fzf.asyncfind() abort
cal s:runcat.start()
let self.notwid = popup_notification('find files in ['.s:fzf.searched.'] and caching ...', #{zindex: 51, line: &lines, col: 5})
let self.jobcnt = self.maxdepth-1
let self.endjobcnt = 0
for depth in range(2, self.maxdepth)
cal job_start(self.get_file(depth), #{out_cb: self.asyncfind_start, close_cb: self.asyncfind_end})
endfor
endf
fu! s:fzf.asyncfind_start(ch, msg) abort
cal add(self.cache, a:msg)
endf
fu! s:fzf.asyncfind_end(ch) abort
let s:fzsearch.list = self.cache
cal s:fzsearch.list_upd()
let self.endjobcnt += 1
if self.endjobcnt == self.jobcnt
cal s:runcat.stop()
cal popup_close(self.notwid)
cal popup_notification('find files cached !', #{zindex: 51, line: &lines, col: 5})
endif
endf
fu! s:fzfexe() abort
cal s:fzf.files()
endf
noremap <silent><Plug>(fzf-smartfiles) :<C-u>cal <SID>fzfexe()<CR>
" }}}
余談ですが、fzf以外にも似たようにalternativeコマンドとしてRust製の
- ls -> exa
- cd -> zoxide
- cat -> bat
- find -> fd
- grep -> rg (ripgrep)
- sed -> sd
- ps, top -> proc
- git -> Go製のlazygit
- docker -> Go製のlazydocker
を使ってます。vimとかじゃなくて普通にCLIで意味わかんないほど便利なのでおすすめ。
他にもGithubCLIやQiitaCLI、テーマのOh My Zshやstarshipなどがおすすめです。
以下の記事の#cli-tools
セクションに良くまとまっているのでどうぞ。
Rust製ツールは以下にまとまってます。
easymotion/vim-easymotion
いわずと知れた神プラグインですが、100行(ほど)で再現しました。
が、WindowsのGitBashだとバッファを表示するのがすごく重いのか
easymotion描画ようのダミーバッファを表示するだけで2秒ほどかかり、なんにも思考のスピードじゃなかったので
ダミーバッファでなく、本ファイルのバッファに無理やり描画してundoするとかいう強引技を使ってました。
プログラム
" ===================================================================
" easymotion/vim-easymotion
" ===================================================================
" {{{
" m, g read some function doesn't work just as I want
let s:emotion = #{keypos: [], klen: 1, keys: ['s', 'w', 'a', 'd', 'j', 'k', 'h', 'l'], popid: 0}
fu! s:emotion.exe() abort
" fold all open
norm zR
" get target chars in current window without empty line
" [{'row': row number, 'col': [ col number, ... ]}...]
let self.keypos = []
let self.klen = 1
let wininfo = []
let tarcnt = 0
let rn = line('w0')
let self.cl = line('.')
let self.cc = col('.')
let self.sl = line('w0')
let self.ctx = getline('w0', 'w$')
for l in self.ctx
" loop row without 'including MultiByte' and 'empty', get head chars
" 日本語は1文字でマルチバイト3文字分だが、カーソル幅は2なのでめんどい、日本語を含む行は弾く
if l !~ '^[ -~|\t]\+$'
let rn+=1
continue
endif
let chars = []
let ofst = 0
while ofst != -1
let st = matchstrpos(l, '\<.', ofst)
let ofst = matchstrpos(l, '.\>', ofst)[2]
if st[0] != ''
cal add(chars, st[2])
endif
endwhile
if !empty(chars)
cal add(wininfo, #{row: rn, col: chars})
endif
let tarcnt = tarcnt+len(chars)
let rn+=1
endfor
if tarcnt==0
retu
endif
" calc key stroke length, keyOrder is 'ssw' = [0,0,1]
while tarcnt > pow(len(self.keys), self.klen)
let self.klen+=1
endwhile
let keyOrder = range(1, self.klen)->map({->0})
" sort near current line, create 'self_keypos' map like this
" [{'row': 1000, 'col': [{'key': 'ssw', 'pos': 7}, ... ]}, ... ]
for r in sort(deepcopy(wininfo), { x,y -> abs(x.row-self.cl) - abs(y.row-self.cl) })
let tmp = []
for col in r.col
cal add(tmp, #{key: copy(keyOrder)->map({i,v->self.keys[v]})->join(''), pos: col})
let keyOrder = self.incrementNOrder(len(self.keys)-1, keyOrder)
endfor
cal add(self.keypos, #{row: r.row, col: tmp})
endfor
" create preview window
sil! e 'emotion'
setl buftype=nofile bufhidden=wipe nobuflisted
" fill blank
cal setline(1, range(1, self.sl))
cal self.previewini()
" disable diagnostic
if exists('*CocAction')
cal CocAction('diagnosticToggle')
endif
" draw
cal matchadd('EmotionBase', '.', 98)
cal self.draw(self.keypos)
cal popup_close(self.popid)
let self.popid = popup_create('e-motion', #{line: &lines, col: &columns*-1, mapping: 0, filter: self.char_enter})
cal setwinvar(self.popid, '&wincolor', 'DarkBlue')
echo ''
endf
fu! s:emotion.previewini() abort
" restore contents
cal setline(self.sl, self.ctx)
" restore cursor position
cal cursor(self.sl+5, self.cc)
norm! zt
cal cursor(self.cl, self.cc)
endf
" function: increment N order
" 配列をN進法とみなし、1増やす. 使うキーがssf → sws と繰り上がる仕組み
fu! s:emotion.incrementNOrder(nOrder, keyOrder) abort
if len(a:keyOrder) == 1
retu [a:keyOrder[0]+1]
endif
let tmp = []
let overflow = 0
for idx in reverse(range(0, len(a:keyOrder)-1))
" 1. increment last digit
if idx == len(a:keyOrder)-1
cal insert(tmp, a:keyOrder[idx] == a:nOrder ? 0 : a:keyOrder[idx]+1)
if tmp[0] == 0
let overflow = 1
endif
continue
endif
" 2. check next digit
if overflow
cal insert(tmp, a:keyOrder[idx] == a:nOrder ? 0 : a:keyOrder[idx]+1)
let overflow = a:keyOrder[idx] == a:nOrder ? 1 : 0
else
cal insert(tmp, a:keyOrder[idx])
endif
endfor
retu tmp
endf
" draw keystroke
" 日本語は1文字でマルチバイト3文字分だが、カーソル幅は2なのでめんどいから弾いてある
" posの次文字がマルチバイトだと、strokeが2回以上残ってる時、変に文字を書き換えてカラム数変わる
fu! s:emotion.draw(keypos) abort
cal self.previewini()
cal self.hl_del(['EmotionFin', 'EmotionWip'])
let hlpos_wip = []
let hlpos_fin = []
for r in a:keypos
let line = getline(r.row)
for c in r.col
let colidx = c.pos-1
let view_keystroke = c.key[:0]
let offset = colidx-1
cal add(hlpos_fin, [r.row, c.pos])
if len(c.key)>=2
let view_keystroke = c.key[:1]
cal add(hlpos_wip, [r.row, c.pos, 2])
endif
let line = colidx == 0
\ ? view_keystroke.line[len(view_keystroke):]
\ : line[0:offset].view_keystroke.line[colidx+len(view_keystroke):]
endfor
cal setline(r.row, line)
endfor
for t in hlpos_fin
cal matchaddpos('EmotionFin', [t], 99)
endfor
for t in hlpos_wip
cal matchaddpos('EmotionWip', [t], 100)
endfor
endf
fu! s:emotion.char_enter(winid, key) abort
" noop (for polyglot bug adhoc)
if strtrans(a:key) == "<80><fd>`"
retu 1
endif
" only accept defined emotion key
if self.keys->index(a:key) == -1
" go out e-motion
cal popup_close(self.popid)
let p = getpos('.')
" close preview
b #
cal cursor(p[1],p[2])
cal self.hl_del(['EmotionFin', 'EmotionWip', 'EmotionBase'])
" restore diagnostic
if exists('*CocAction')
cal CocAction('diagnosticToggle')
endif
cal s:echoE('e-motion: go out')
retu 1
endif
" upd emotion.keypos
let tmp = self.keypos->deepcopy()->map({ _,r -> #{row: r.row,
\ col: r.col->filter({_,v->v.key[0]==a:key})->map({_,v->#{key: v.key[1:], pos: v.pos}})} })
\ ->filter({_,v->!empty(v.col)})
" nomatch -> noop
if empty(tmp)
retu 1
else
let self.keypos = tmp
endif
" if last match -> end e-motion
if len(self.keypos) == 1 && len(self.keypos[0].col) == 1
cal popup_close(self.popid)
" close previeew
b #
norm! zR
cal cursor(self.keypos[0].row, self.keypos[0].col[0].pos)
cal self.hl_del(['EmotionFin', 'EmotionWip', 'EmotionBase'])
" restore diagnostic
if exists('*CocAction')
cal CocAction('diagnosticToggle')
endif
cal s:echoI('e-motion: finish')
retu 1
endif
" redraw
cal self.draw(self.keypos)
retu 1
endf
" about highlight setting
fu! s:emotion.hl_del(group_name_list) abort
cal getmatches()->filter({ _,v -> match(a:group_name_list, v.group) != -1 })->map({ _,v -> matchdelete(v.id) })
endf
aug emotion_hl
au!
au ColorScheme * hi EmotionBase ctermfg=59
au ColorScheme * hi EmotionWip ctermfg=166 cterm=bold
au ColorScheme * hi EmotionFin ctermfg=196 cterm=bold
aug END
fu! s:emotion() abort
cal s:emotion.exe()
endf
noremap <silent><Plug>(emotion) :<C-u>cal <SID>emotion()<CR>
" }}}
まぁでもこの神プラグインを簡易的とはいえvimrcに入れ込めたのは結果気持ちよかったです。
mhinz/vim-startify
どうしてもスタート画面に『ぼっちざろっく!』を入れたくて、セッション管理以外の部分を真似しました。
ついでにチートシートも作成して、vim-which-keyの劣化版みたいなこともしました。
プログラム
" ===================================================================
" mhinz/vim-startify
" ===================================================================
" {{{
let s:start = #{}
" ぼっちざろっく{{{
let s:start.btr_logo = [
\' dN',
\' .. JMF',
\' ..gMMMM% JMF',
\' .MM9^ .MF (MF',
\' .(, (" .M# .g, .MF',
\' ., dN gg,,M@ .M# .M#! (M>',
\' JM} M# .MNgg. .g, ?M[ 7B .MMg+gg,. .MM" ."',
\' ...gNMN,.Mb MN .gMM9! .(MN, .= .MMM9= ?MN, (WN, .MM ',
\' jN- ..gMMN#! (Mp(M} .+MMYMF ..kMMWM% ,M#^ dN. .WNJ, JM',
\' MM .MM9^ dN, dNB^ (M% ?M"! ,M\ ., .M# .&MMMN, ?" M#',
\' MN .MN#^ dM: ..(J-, ,B .TM .M# ,M@ .MF',
\' MN. ..MMBMN_ dN_.MM@"!?MN. TMm .a, (M@ MM^',
\' MN. .MM" JMb.... .. dMMM= .Mb ?HNgJ.., .MM^',
\' dM{ -MMM#7"T"" .dN#TMo ? .MM^ ?! +gM#=',
\' (M] .MN(N# .M@ .MF .MM^ ~',
\' .MN ?""" MM! .MMD',
\' ?N[ 7"',
\' TMe',
\' ?MN,',
\' TMNg,'
\]
"}}}
" cheat sheet {{{
let s:start.cheat_sheet_win = [
\' ╭── Window ──────────────────────────────────────────╮ ╭── Search ───────────────────────────────────────╮',
\' │ C-n / p | (buffer tab)(next / prev) │ │ Space e | (explorer) │',
\' │ Space x | (buffer tab)(close) │ │ Space f | (fzf)(files / projectfiles auto) │',
\' │ C-w v / s | (window split)(vertical / horizontal) │ │ Space h | (fzf)(histories) │',
\' │ ←↑↓→ | (window)(resize) │ │ Space b | (fzf)(buffers) │',
\' │ C-hjkl | (window)(forcus) │ │ Space m | (marks) │',
\' │ Space t/g | (terminal)/(lazygit) │ │ Space s | (fuzzy search in file / quickfix) │',
\' │ Space z | (Zen Mode) │ │ Space*2 s | (grep interactive) │',
\' ╰────────────────────────────────────────────────────╯ │ Space q | (clear search highlight) │',
\' ╰─────────────────────────────────────────────────╯',
\'',
\' ╭── Motion ───────────────────────────────────────────╮ ╭── Command ──────────────────────────────────────────╮',
\' │ Space v | (IDE Action Menu) │ │ :PlugInstall | (plugins install) │',
\' │ Space w | (f-scope toggle) │ │ :PlugUnInstall | (plugins uninstall) │',
\' │ Tab S-Tab | (jump 5rows) │ │ :RunCat [option] | (running cat) │',
\' │ s | (easymotion) │ │ :RunCatStop | (running cat stop) │',
\' │ mm | (mark toggle) │ │ :TrainingWheelsProtocol | [option] (training vim) │',
\' │ mn / mp | (mark next / prev) │ ╰─────────────────────────────────────────────────────╯',
\' │ mc / mx | (mark clear file / delete all file) │',
\' │ INSERT C-hjkl | (cursor move) │',
\' │ VISUAL C-jk | (blok up / down) │',
\' ╰─────────────────────────────────────────────────────╯',
\]
" }}}
fu! s:start.exe() abort
let fopen = execute('ls')->split('\n')->map({_,v -> split(v, '"')[1]})
\ ->filter({_,v -> v != '[No Name]' && v != '[無名]'})->len()
if fopen
cal self.move()
retu
endif
" preview window
sil! exe 'e _cheat_cheet_'
setl buftype=nofile bufhidden=wipe nobuflisted modifiable
setl nonumber norelativenumber nocursorline nocursorcolumn signcolumn=no
let &filetype = 'Bocchi_The_Rock'
nmap <buffer>i \<Esc>
nmap <buffer>I \<Esc>
nmap <buffer>a \<Esc>
nmap <buffer>A \<Esc>
nmap <buffer>s \<Esc>
" draw
cal append('$', self.btr_logo)
cal append('$', ['',''])
cal append('$', self.cheat_sheet_win)
hi BTR ctermfg=218 cterm=bold
cal matchaddpos('BTR', range(2,9)->map({_,v->[v]}), 999)
cal matchaddpos('BTR', range(10,17)->map({_,v->[v]}), 999)
cal matchaddpos('BTR', range(18,21)->map({_,v->[v]}), 999)
cal matchadd('User_greenfg_blackbg', '[─│╰╯╭╮]', 20)
cal matchadd('DarkOrange', '\(Window\|Search\|Motion\|Command\)')
cal matchadd('DarkBlue', '│.\{-,25}|', 19)
cal matchadd('DarkRed', '(.*)', 18)
" fix
setl nomodifiable nomodified
endf
fu! s:start.move() abort
cal clearmatches()
cal s:fmode.activate()
" deactivate
aug start_vim
au!
aug END
endf
" only first call
aug start_vim
au!
au VimEnter * cal s:start.exe()
au BufReadPre * cal s:start.move()
aug END
" }}}
joshdick/onedark.vim
カラースキームです。私が人生で初めて使ったエディタが、サ終してしまったAtomでして、どうしてもonedark
が好きでずっと使っています。
ちなみに復活してるらしい。
ちなみに中身はほぼコピペです。。。
Popup terminal/git
コンパイルエラーを放置してます!
LSPのcoc-vimlsp
を使っているのですが
E125: Illegal argument: hidden:1 (vimlsp)
と怒られてるのに無視してます。
あと途中からメンテが面倒になって適当になっています、、
最悪ですね。
Completion
補完系プラグインの挙動を真似したかったのですが、とりあえず全範囲をソースで補完だ!としたところ
一文字打つたびに重たい検索が走り出すもっさりエディタになりました。
補完はかなりシビアに非同期処理の遅延とかをいじった方がいいので、流石に既存プラグイン使った方がいいです。
あと正規表現機能とかぶってエラーになるの放置してました。。
プログラム
" completion {{{
" TODO completion リファクタ
" TODO completion ~の後ろで正規表現やりに行っちゃう。=は大丈夫
""let s:completion = #{exclude: [" ()[]{}<>'`".'"'], confirmed: 0, done: {-> execute('let s:completion.confirmed = 1')}}
let s:completion = #{exclude: [" ~()[]{}<>'`".'"'], opened: 0, confirmed: 0}
fu! s:completion.exe() abort
if self.confirmed
let self.confirmed = 0
let self.opend = 0
retu
endif
if col('.') == 1 || match(self.exclude, getline('.')[col('.')-2]) != -1
retu
endif
if !pumvisible()
" 開いてたはずだが、確定されずに閉じてるなら何もしない
if self.opened
retu
endif
let self.opened = 1
cal feedkeys("\<C-n>")
endif
endf
fu! s:completion.done() abort
let self.confirmed = 1
let self.opened = 0
endf
" TODO completion <BS>のあとに補完だしたくない
if glob('~/.vim/pack/plugins/start/coc.nvim') == ''
au TextChangedI,TextChangedP * cal s:completion.exe()
au CompleteDone * cal s:completion.done()
endif
" }}}
現在のvimrc
さて、模倣した3000行バージョンはそれとしてとっておき
現在私は既存プラグインにしっかり頼った版を使っています。
自作したいものは、ちゃんとプラグインを自作して、入れて使っているので
vimrc自体はとてもスッキリしました。
プラグイン部分はこうなってます。46件入れています。
call plug#begin()
" ### Enhanced vim motion
Plug 'serna37/vim-modern-basic'
Plug 'serna37/vim-anchor5'
Plug 'easymotion/vim-easymotion'
Plug 'haya14busa/vim-edgemotion'
Plug 'serna37/edgemotion-vertical'
Plug 'rhysd/clever-f.vim'
Plug 'serna37/vim-fscope-around'
Plug 't9md/vim-quickhl'
Plug 'haya14busa/vim-asterisk'
Plug 'MattesGroeger/vim-bookmarks'
Plug 'simeji/winresizer'
Plug 'yuttie/comfortable-motion.vim'
let g:EasyMotion_do_mapping = 0
nnoremap s <Plug>(easymotion-sn)
nnoremap <Leader><Leader>w <Plug>(easymotion-bd-w)
nnoremap <C-j> <Plug>(edgemotion-j)<Plug>(anchor)
nnoremap <C-k> <Plug>(edgemotion-k)<Plug>(anchor)
let g:clever_f_smart_case = 1
let g:clever_f_timeout_ms = 2000
let g:clever_f_highlight_timeout_ms = 2000
aug cleaver_f
au!
au ColorScheme * hi CleverFDefaultLabel cterm=BOLD,underline ctermfg=40 ctermbg=0
aug END
nnoremap <leader>w <Plug>(fscope-around-toggle)<Plug>(clever-f-reset)
nnoremap # <Plug>(asterisk-z*)<Plug>(quickhl-manual-this)
nnoremap <silent><Leader>q <Plug>(quickhl-manual-reset)<Plug>(clever-f-reset):noh<CR>
let g:comfortable_motion_no_default_key_mappings = 1
let g:comfortable_motion_interval = 1000.0 / 60
let g:comfortable_motion_friction = 70.0
let g:comfortable_motion_air_drag = 5.0
nnoremap <silent><C-f> :cal comfortable_motion#flick(200)<CR>
nnoremap <silent><C-b> :cal comfortable_motion#flick(-200)<CR>
" ### Enhanced Visualization
Plug 'mhinz/vim-startify'
Plug 'vim-airline/vim-airline'
Plug 'vim-airline/vim-airline-themes'
Plug 'sheerun/vim-polyglot'
Plug 'joshdick/onedark.vim'
Plug 'junegunn/goyo.vim'
Plug 'liuchengxu/vim-which-key'
let g:airline_theme = 'onedark'
let g:airline#extensions#tabline#enabled = 1
let g:airline_powerline_fonts = 1
let g:airline_highlighting_cache = 1
fu! s:moveBuf(flg) abort
let current_id = ''
let buf_arr = []
for v in split(execute('ls'), '\n')->map({ _,v -> split(v, ' ')})
let x = copy(v)->filter({ _,v -> !empty(v) })
if stridx(x[1], 'F') == -1 && stridx(x[1], 'R') == -1
cal add(buf_arr, x[0])
if stridx(x[1], '%') != -1
let current_id = x[0]
endif
endif
endfor
let buf_idx = a:flg == 'next' ? match(buf_arr, current_id) + 1 : match(buf_arr, current_id) - 1
let buf_id = buf_idx == len(buf_arr) ? buf_arr[0] : buf_arr[buf_idx]
exe 'b '.buf_id
endf
fu! s:closeBuf() abort
let now_b = bufnr('%')
cal s:moveBuf('prev')
execute('bd ' . now_b)
endf
noremap <silent><Plug>(buf-prev) :<C-u>cal <SID>moveBuf('prev')<CR>
noremap <silent><Plug>(buf-next) :<C-u>cal <SID>moveBuf('next')<CR>
noremap <silent><Plug>(buf-close) :<C-u>cal <SID>closeBuf()<CR>
nnoremap <C-n> <Plug>(buf-prev)
nnoremap <C-p> <Plug>(buf-next)
nnoremap <Leader>x <Plug>(buf-close)
nnoremap <silent><Leader>z :Goyo<CR>
nnoremap <silent><Leader> :WhichKey '<Space>'<CR>
set timeoutlen=500
" ### Filer
Plug 'junegunn/fzf', {'do': { -> fzf#install() }}
Plug 'junegunn/fzf.vim'
let $FZF_PREVIEW_PREVIEW_BAT_THEME = 'TwoDark'
nnoremap <silent><Leader>f :cal execute('CocCommand fzf-preview.'.(system('git rev-parse --is-inside-work-tree') =~ 'fatal'?'DirectoryFiles':'ProjectFiles'))<CR>
nnoremap <silent><Leader>b :CocCommand fzf-preview.Buffers<CR>
nnoremap <silent><Leader>hf :CocCommand fzf-preview.MruFiles<CR>
nnoremap <silent><Leader>e :CocCommand explorer --width 30<CR>
nnoremap <silent><Leader>s :CocCommand fzf-preview.Lines<CR>
nnoremap <silent><Leader><Leader>s :CocCommand fzf-preview.ProjectGrep .<CR>
nnoremap <silent><Leader>m :CocCommand fzf-preview.Bookmarks<CR>
nnoremap <silent><Leader>nn :CocCommand fzf-preview.MemoList<CR>
nnoremap <silent><Leader>ng :CocCommand fzf-preview.MemoListGrep .<CR>
au DirChanged * cal execute('CocCommand explorer --no-focus --width 30')
" ### Git
Plug 'tpope/vim-fugitive'
Plug 'airblade/vim-gitgutter'
" ### Reading
Plug 'junegunn/rainbow_parentheses.vim'
Plug 'andymass/vim-matchup'
Plug 'preservim/vim-indent-guides'
Plug 'liuchengxu/vista.vim'
Plug 'wfxr/minimap.vim'
au VimEnter * RainbowParentheses
let g:indent_guides_enable_on_vim_startup = 1
let g:indent_guides_guide_size = 1
let g:indent_guides_start_level = 2
let g:indent_guides_exclude_filetypes = ['help', 'coc-explorer', 'startify']
let g:indent_guides_auto_colors = 0
aug indent_guide
au!
au ColorScheme * hi IndentGuidesOdd ctermbg=236
au ColorScheme * hi IndentGuidesEven ctermbg=234
aug END
let g:vista_sidebar_width = 15
nnoremap <silent><Leader><Leader>o :Vista!!<CR>
let g:minimap_git_colors = 1
nnoremap <silent><Leader><Leader>m :MinimapToggle<CR>
" ### Writing
Plug 'SirVer/ultisnips'
Plug 'markonm/traces.vim'
Plug 'scrooloose/nerdcommenter'
Plug 'lfilho/cosco.vim'
Plug 'matze/vim-move'
Plug 'jiangmiao/auto-pairs'
Plug 'tpope/vim-surround'
Plug 'tpope/vim-repeat'
let g:UltiSnipsExpandTrigger="<C-s>"
" o -> A+CR (adhoc for snippet tabstop bug...)
nnoremap o A<CR>
let g:cosco_filetype_whitelist = ['cpp', 'rust']
" return normal & save
inoremap jj <Esc>:CommaOrSemiColon<CR>:w<CR>
let g:move_key_modifier_visualmode = 'C'
let g:AutoPairsMapCh = 0
" ### LSP IDE
Plug 'neoclide/coc.nvim', {'branch': 'release'}
Plug 'thinca/vim-quickrun'
Plug 'puremourning/vimspector'
Plug 'serna37/vim-IDE-menu'
let g:coc_global_extensions = ['coc-explorer', 'coc-snippets', 'coc-fzf-preview',
\ 'coc-sh', 'coc-vimlsp', 'coc-json', 'coc-sql',
\ 'coc-html', 'coc-css', 'coc-tsserver',
\ 'coc-clangd', 'coc-rust-analyzer', 'coc-go',
\ 'coc-pyright', 'coc-java',
\ ]
let g:coc_snippet_next = '<Tab>'
let g:coc_snippet_prev = '<S-Tab>'
au CursorHold * sil cal CocActionAsync('highlight')
inoremap <silent><expr> <CR> coc#pum#visible() ? coc#pum#confirm() : "\<CR>"
inoremap <silent><expr> <Tab> coc#pum#visible() ? coc#pum#next(1) : "\<Tab>"
inoremap <silent><expr> <S-Tab> coc#pum#visible() ? coc#pum#prev(1) : "\<S-Tab>"
nnoremap <Leader>d <Plug>(coc-definition)
nnoremap <silent><Leader>r :CocCommand fzf-preview.CocReferences<CR>
nnoremap <silent><Leader>o :CocCommand fzf-preview.CocOutline<CR>
nnoremap <silent><Leader>? :cal CocAction('doHover')<CR>
nnoremap <Leader>, <plug>(coc-diagnostic-next)
nnoremap <Leader>. <plug>(coc-diagnostic-prev)
nnoremap <silent><nowait><expr> <C-d> coc#float#has_scroll() ? coc#float#scroll(1) : comfortable_motion#flick(100)
nnoremap <silent><nowait><expr> <C-u> coc#float#has_scroll() ? coc#float#scroll(0) : comfortable_motion#flick(-100)
" ### util
Plug 'serna37/vim-tutorial'
Plug 'soywod/unfog.vim'
Plug 'glidenote/memolist.vim'
Plug 'segeljakt/vim-silicon'
Plug 'nanotee/zoxide.vim'
Plug 'serna37/vim-atcoder-menu'
call plug#end()
その他のお気に入りプラグイン
全部気に入ってますが、せっかくなので紹介していないものも含めて
特に気に入っているプラグインをピックアップします。
移動系まとめ
- serna37/vim-anchor5: 5行移動、前後にマーカー
- haya14busa/vim-edgemotion: 縦方向に、行があるとこまで一気にジャンプ
- serna37/edgemotion-vertical: 横方向版(0 ^ $とかわらんがedgemotionと似たキーで飛べるので快適)
- rhysd/clever-f.vim: f移動最強
- serna37/vim-fscope-around: f移動先を一瞬で見れるように
3/5が自作で恐縮ですが、これらをまとめてガンガン移動しています。
見た目系
-
sheerun/vim-polyglot
ほぼ全部の言語のシンタックスハイライトがあります。これだけ入れればOK
Reading
- junegunn/rainbow_parentheses.vim: 対応カッコを光らせる。綺麗
-
andymass/vim-matchup: 対応文字を光らせる。
if fi
とか - preservim/vim-indent-guides: インデントに縦線、偶奇で色変えられるので見やすい
- liuchengxu/vista.vim: メンバの階層一覧を表示、VSCodeみたい
- wfxr/minimap.vim: ミニマップだす、ミニマップでスクロールもgit差分色もつく、まじVSCode
Writing
- SirVer/ultisnips: 最強スニペット
- markonm/traces.vim: 置換プレビュー
-
lfilho/cosco.vim: 行末の
;
を自動で - matze/vim-move: (矩形もok)選択した文字たちをグングン移動
LSP系
- neoclide/coc.nvim: LSPクライアント. 人によって好き付きあるが私はこれ. これ入れたらIDEになる
- thinca/vim-quickrun: プログラム実行するやつ
- puremourning/vimspector: DAPでデバッグする. vimでデバッグできないなんてことはない!VSCodeにあってvimにできないこともないんじゃないかな
util系
- soywod/unfog.vim: 簡易タスクリスト
- glidenote/memolist.vim: とりあえずmarkdownでメモできるしfzfで探せる
- segeljakt/vim-silicon: コードをスクショに変換できる、面白い
- nanotee/zoxide.vim: zoxideをvim内で使える
unfogコマンドやrustの依存が必要ですが、気に入ってます。
最終的に吐き出されたもの
既存に置き換える以外で吐き出したプラグイン7つがこちら。
自分用カスタムをまぁまぁ汎用化したつもりなので、vimrcから設定を上書きできるようにも作りました。
一部READMEが古かったりしますが
最後に
今回はあえて1枚に納めてポータビリティを高めたい、(vim-plugのように)curl
1行でインストールしたい等の目的があったとはいえ
大掃除後のvimはやっぱり世界が違いました。
皆さんも、既存のものを作り直すことなく、長すぎるコードを書くことなく
適切な粒度で開発していきましょう!(戒め)