多くのプラグイン
便利なものがとても多いですが、割とソース量的に大したことないものも多い。
汎用的な部分をなくせば、vimrcでよくね?というものがある。
あと個人的にプラグインなしが好きなので、「それvimrcでよくね?」を紹介していきます。
私のvimrcレポ
vim-airline
スマートタブラインやアイコンなどは除きますが、デフォルトで十分にステータスラインを整備できます。
" status line
set ruler
set laststatus=2
let ff_table = {'dos' : 'CRLF', 'unix' : 'LF', 'mac' : 'CR'}
fu! SetStatusLine()
hi User1 cterm=bold ctermfg=7 ctermbg=4 gui=bold guibg=#70a040 guifg=#ffffff
hi User2 cterm=bold ctermfg=2 ctermbg=0 gui=bold guibg=#4070a0 guifg=#ffffff
hi User3 cterm=bold ctermbg=5 ctermfg=0
hi User4 cterm=bold ctermfg=7 ctermbg=56 gui=bold guibg=#a0b0c0 guifg=black
hi User5 cterm=bold ctermfg=7 ctermbg=5 gui=bold guibg=#0070e0 guifg=#ffffff
let dict = {'i': '1* INSERT', 'n': '2* NORMAL', 'R': '3* REPLACE', 'c': '4* COMMAND', 't': '4* TERMIAL', 'v': '5* VISUAL', 'V': '5* VISUAL', "\<C-v>": '5* VISUAL'}
let mode = match(keys(dict), mode()) != -1 ? dict[mode()] : '5* SP'
retu '%' . mode . ' %* %<%F%m%r%h%w%=%2* %p%% %l/%L %02v [%{&fenc!=""?&fenc:&enc}][%{ff_table[&ff]}] %*'
endf
set statusline=%!SetStatusLine()
fzf
無理だろと思うかも知れませんが、できます。matchfuzzyやpopupがvim組み込みになったことでかなりいい感じに再現できます
別で記事も書いてますのでぜひ
100行でfzf
nnoremap <leader>f :call FzfStart()<CR>
let g:fzf_find_cmd = 'find . -type f -name "*" -not -path "*.git/*" -not -path "*.class"'
let g:fzf_searched_dir = execute('pwd')[1:] " first char is ^@, so trim
let g:fzf_find_result_tmp = []
fu! FzfStart()
" clear map to escape
nmapclear
if stridx(execute('pwd')[1:], g:fzf_searched_dir) == -1 || len(g:fzf_find_result_tmp) == 0
let g:fzf_find_result_tmp = []
let g:fzf_searched_dir = execute('pwd')[1:]
echo 'find files in ['.g:fzf_searched_dir.'] and chache ...'
cal job_start(g:fzf_find_cmd, {'out_cb': function('s:fzf_find_start'), 'close_cb': function('s:fzf_find_end')})
endif
let g:fzf_mode = 'his'
let g:fzf_searching_zone = '(*^-^) BUF & MRU'
let g:fzf_pwd_prefix = 'pwd:[' . execute('pwd')[1:] . ']>>'
let g:fzf_enter_keyword = []
let g:fzf_his_result = map(split(execute('ls'), '\n'), { i,v -> split(filter(split(v, ' '), { i,v -> v != '' })[2], '"')[0] }) + map(split(execute('oldfiles'), '\n'), { i,v -> split(v, ': ')[1] })
let g:fzf_find_result = g:fzf_his_result
let g:fzf_enter_win = popup_create(g:fzf_pwd_prefix, #{ title: 'Type or <BS> / past:<Space> / MRU<>FZF:<Tab> / choose:<Enter> / end:<Esc> / chache refresh:<C-f>', border: [], zindex: 99, minwidth: &columns/2, maxwidth: &columns/2, maxheight: 1, line: &columns/4-&columns/24, filter: function('s:fzf_refresh_result') })
cal s:fzf_create_choose_win()
endf
fu! s:fzf_create_choose_win()
let g:fzf_c_idx = 0
let g:fzf_choose_win = popup_menu(g:fzf_find_result, #{ title: g:fzf_searching_zone, border: [], zindex: 98, minwidth: &columns/2, maxwidth: &columns/2, minheight: 2, maxheight: &lines/2, filter: function('s:fzf_choose') })
endf
fu! s:fzf_find_start(ch, msg) abort
let g:fzf_find_result_tmp = add(g:fzf_find_result_tmp, a:msg)
endf
fu! s:fzf_find_end(ch) abort
echo 'find files in ['.g:fzf_searched_dir.'] and chache is complete!!'
endf
fu! s:fzf_refresh_result(winid, key) abort
if a:key is# "\<Esc>"
call popup_close(g:fzf_enter_win)
call popup_close(g:fzf_choose_win)
cal s:my_key_map()
return 1
elseif a:key is# "\<CR>"
call popup_close(g:fzf_enter_win)
cal s:my_key_map()
return 1
elseif a:key is# "\<C-f>"
let g:fzf_searched_dir = execute('pwd')[1:]
echo 'find files in ['.g:fzf_searched_dir.'] and chache ...'
cal job_start(g:fzf_find_cmd, {'out_cb': function('s:fzf_find_start'), 'close_cb': function('s:fzf_find_end')})
elseif a:key is# "\<Space>"
for i in range(0,strlen(@")-1)
let g:fzf_enter_keyword = add(g:fzf_enter_keyword, strpart(@",i,1))
endfor
elseif a:key is# "\<Tab>"
let g:fzf_mode = g:fzf_mode == 'his' ? 'fzf' : 'his'
let g:fzf_searching_zone = g:fzf_mode == 'his' ? '(*^-^) BUF & MRU' : '(*^-^) FZF [' . g:fzf_searched_dir . ']'
cal popup_close(g:fzf_choose_win)
cal timer_start(0, { -> s:fzf_create_choose_win() })
elseif a:key is# "\<BS>" && len(g:fzf_enter_keyword) > 0
unlet g:fzf_enter_keyword[len(g:fzf_enter_keyword)-1]
elseif a:key is# "\<BS>" && len(g:fzf_enter_keyword) == 0
" noop
else
let g:fzf_enter_keyword = add(g:fzf_enter_keyword, a:key)
endif
if g:fzf_mode == 'his'
let g:fzf_find_result = len(g:fzf_enter_keyword) != 0 ? matchfuzzy(g:fzf_his_result, join(g:fzf_enter_keyword, '')) : g:fzf_his_result
else
let g:fzf_find_result = len(g:fzf_enter_keyword) != 0 ? matchfuzzy(g:fzf_find_result_tmp, join(g:fzf_enter_keyword, '')) : g:fzf_find_result_tmp
endif
cal setbufline(winbufnr(g:fzf_enter_win), 1, g:fzf_pwd_prefix . join(g:fzf_enter_keyword, ''))
cal setbufline(winbufnr(g:fzf_choose_win), 1, map(range(1,30), { i,v -> '' }))
cal setbufline(winbufnr(g:fzf_choose_win), 1, g:fzf_find_result[0:29]) " re view only first 30 files
return a:key is# "x" || a:key is# "\<Space>" ? 1 : popup_filter_menu(a:winid, a:key)
endf
fu! s:fzf_choose(winid, key) abort
if a:key is# 'j'
let g:fzf_c_idx = g:fzf_c_idx == len(g:fzf_find_result)-1 ? len(g:fzf_find_result)-1 : g:fzf_c_idx + 1
elseif a:key is# 'k'
let g:fzf_c_idx = g:fzf_c_idx == 0 ? 0 : g:fzf_c_idx - 1
elseif a:key is# "\<CR>"
return s:fzf_open(a:winid, 'e', g:fzf_find_result[g:fzf_c_idx])
elseif a:key is# "\<C-v>"
return s:fzf_open(a:winid, 'vnew', g:fzf_find_result[g:fzf_c_idx])
elseif a:key is# "\<C-t>"
return s:fzf_open(a:winid, 'tabnew', g:fzf_find_result[g:fzf_c_idx])
endif
return popup_filter_menu(a:winid, a:key)
endf
fu! s:fzf_open(winid, op, f) abort
cal popup_close(a:winid)
exe a:op a:f
return 1
endf
comfortable-motion
ぬるぬるスクロールできるやつですね
細かい物理演算でなく、まぁまぁ適当なスパンでよければ以下でできます
チラつくので、スクロール中はカーソルライン/カラムを非表示にするのも入れてます
nnoremap <silent><C-u> :cal Scroll(0, 25)<CR>
nnoremap <silent><C-d> :cal Scroll(1, 25)<CR>
nnoremap <silent><C-b> :cal Scroll(0, 10)<CR>
nnoremap <silent><C-f> :cal Scroll(1, 10)<CR>
" scroll ----------------------------------------
fu! Scroll(vector, delta)
cal CursorToggle()
let vec = a:vector == 0 ? "\<C-y>" : "\<C-e>"
let tmp = timer_start(a:delta, { -> feedkeys(vec) }, {'repeat': -1})
cal timer_start(600, { -> timer_stop(tmp) })
cal timer_start(600, { -> CursorToggle() })
endf
fu! NormalExe(aa)
execute "normal! " . a:aa
endf
fu! CursorToggle()
set cursorcolumn!
set cursorline!
endf
winresizer
これは何ていうか、windowフォーカス移動とリサイズだけならキーマップのみでできる
ウィンドウの入れ替えは、個人的に使ったことないのでなくていいかなと。。
nnoremap <C-h> <C-w>h
nnoremap <C-l> <C-w>l
nnoremap <C-k> <C-w>k
nnoremap <C-j> <C-w>j
nnoremap <Left> 4<C-w><
nnoremap <Right> 4<C-w>>
nnoremap <Up> 4<C-w>-
nnoremap <Down> 4<C-w>+
" :bo terminal ++row=10などで下部にVSCodeっぽくterminalよく開くと思いますが
" コマンドモードからもフォーカルを移動する場合は以下も必要
tnoremap <C-h> <C-w>h
tnoremap <C-l> <C-w>l
tnoremap <C-k> <C-w>k
tnoremap <C-j> <C-w>j
vim-signature
vimはマークが視覚的な要素0なので、さくらエディタのようにしたいと思ってます。
マークリストをポップアップにしたり
マークを適当な文字でできるようにしたいり
次/前のマーク行に移動、を実装しました
"nnoremap <Leader>m :marks abcdefghijklmnopqrstuvwxyz<CR>:normal! `
nnoremap <Leader>m :call MarkMenu()<CR>
nnoremap mm :cal Marking()<CR>
nnoremap mj :cal MarkHank("up")<CR>
nnoremap mk :cal MarkHank("down")<CR>
" mark ----------------------------------------
let g:mark_words = 'abcdefghijklmnopqrstuvwxyz'
fu! MarkMenu()
let markdicarr = []
let get_marks = ''
try
let get_marks = execute('marks ' . g:mark_words)
catch
echo 'no marks'
retu
endtry
for v in split(get_marks , '\n')[1:]
cal add(markdicarr, {'linenum': str2nr(filter(split(v, ' '), { i,v -> v != '' })[1]), 'val': v})
endfor
cal sort(markdicarr, { x, y -> x['linenum'] - y['linenum'] })
let marks_this = map(markdicarr, { i,v -> v['val'] })
cal popup_menu(marks_this, #{ title: 'choose marks', border: [], zindex: 100, minwidth: &columns/2, maxwidth: &columns/2, minheight: 2, maxheight: &lines/2, filter: function('MarkChoose', [{'idx': 0, 'files': marks_this}]) })
endf
fu! MarkChoose(ctx, winid, key) abort
if a:key is# 'j' && a:ctx.idx < len(a:ctx.files)-1
let a:ctx.idx = a:ctx.idx+1
elseif a:key is# 'k' && a:ctx.idx > 0
let a:ctx.idx = a:ctx.idx-1
elseif a:key is# "\<CR>"
execute('normal!`' . a:ctx.files[a:ctx.idx][1])
endif
return popup_filter_menu(a:winid, a:key)
endf
fu! Marking() abort
try
let get_marks = execute('marks ' . g:mark_words)
catch
execute('mark a')
cal MarkShow()
echo 'marked'
retu
endtry
let l:now_marks = []
let l:warr = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
for row in split(get_marks , '\n')[1:]
let l:r = filter(split(row, ' '), {i, v -> v != ''})
if stridx(g:mark_words, r[0]) != -1 && r[1] == line('.')
execute('delmarks ' . r[0])
cal MarkShow()
echo 'delete mark'
retu
endif
let l:now_marks = add(now_marks, r[0])
endfor
let l:can_use = filter(warr, {i, v -> stridx(join(now_marks, ''), v) == -1})
if len(can_use) != 0
execute('mark ' . can_use[0])
cal MarkShow()
echo 'marked'
else
echo 'over limit markable char'
endif
endf
fu! MarkShow() abort
cal sign_undefine()
try
let get_marks = execute('marks ' . g:mark_words)
catch
retu
endtry
let l:marks = split(get_marks, '\n')
if len(marks) == 0
retu
endif
cal remove(marks, 0)
let mark_dict = {}
let rownums = []
for row in marks
let l:r = filter(split(row, ' '), {i, v -> v != ''})
let mark_dict[r[1]] = r[0]
let rownums = add(rownums, r[1])
endfor
for row in rownums
exe "sign define " . row . " text=" . mark_dict[row] . " texthl=ErrorMsg"
exe "sign place " . row . " line=" . row . " name=" . row . " file=" . expand("%:p")
endfor
endf
aug sig_aus
au!
au BufEnter,CmdwinEnter * cal MarkShow()
aug END
fu! MarkHank(vector) abort
try
let get_marks = execute('marks ' . g:mark_words)
catch
echo 'no marks'
retu
endtry
let l:marks = split(get_marks, '\n')
cal remove(marks, 0)
let mark_dict = {}
let rownums = []
for row in marks
let l:r = filter(split(row, ' '), {i, v -> v != ''})
let mark_dict[r[1]] = r[0]
let rownums = add(rownums, r[1])
endfor
if a:vector == 'up'
cal sort(rownums, {x, y -> x - y})
endif
if a:vector == 'down'
cal sort(rownums, {x, y -> y - x})
endif
for rownum in rownums
if a:vector == 'up' && rownum > line('.')
exe "normal! `" . mark_dict[rownum]
echo index(rownums, rownum) + 1 . "/" . len(rownums)
retu
endif
if a:vector == 'down' && rownum < line('.')
exe "normal! `" . mark_dict[rownum]
echo len(rownums) - index(rownums, rownum) . "/" . len(rownums)
retu
endif
endfor
echo "last mark"
endf
vim-quickhl
ハイライトつけまくると今何検索してるかわからなくなるのでついでにvim-hitspopも。
これも検索した際にハイライトつけるだけなので簡単
ポップアップは適当に実装しました。。ソース量圧縮を重視で。。
set shortmess-=S
nnoremap <silent>* *N:cal HiSet()<CR>:cal Hitpop()<CR>
nnoremap <silent># *N:cal HiSet()<CR>:cal Hitpop()<CR>
nnoremap <silent><Leader>q :noh<CR>:cal clearmatches()<CR>:cal popup_clear(g:hitpopid)<CR>
" highlight -----------------------------------
" on word, bright
aug auto_hl_cword
au!
au BufEnter,CmdwinEnter * cal HiCwordStart()
aug END
fu! HiCwordStart()
aug QuickhlCword
au!
au! CursorMoved <buffer> cal HiCwordR()
au! ColorScheme * execute("hi link QuickhlCword CursorLine")
aug END
execute("hi link QuickhlCword CursorLine")
endf
fu! HiCwordR()
silent! 2match none
exe "2mat QuickhlCword /\\\<". escape(expand('<cword>'), '\/~ .*^[''$') . "\\\>/"
endf
" on search, multi highlight
aug QuickhlManual
au!
au! ColorScheme * cal HlIni()
aug END
let g:search_hl= [
\ "cterm=bold ctermfg=16 ctermbg=153 gui=bold guifg=#ffffff guibg=#0a7383",
\ "cterm=bold ctermfg=7 ctermbg=1 gui=bold guibg=#a07040 guifg=#ffffff",
\ "cterm=bold ctermfg=7 ctermbg=2 gui=bold guibg=#4070a0 guifg=#ffffff",
\ "cterm=bold ctermfg=7 ctermbg=3 gui=bold guibg=#40a070 guifg=#ffffff",
\ "cterm=bold ctermfg=7 ctermbg=4 gui=bold guibg=#70a040 guifg=#ffffff",
\ "cterm=bold ctermfg=7 ctermbg=5 gui=bold guibg=#0070e0 guifg=#ffffff",
\ "cterm=bold ctermfg=7 ctermbg=6 gui=bold guibg=#007020 guifg=#ffffff",
\ "cterm=bold ctermfg=7 ctermbg=21 gui=bold guibg=#d4a00d guifg=#ffffff",
\ "cterm=bold ctermfg=7 ctermbg=22 gui=bold guibg=#06287e guifg=#ffffff",
\ "cterm=bold ctermfg=7 ctermbg=45 gui=bold guibg=#5b3674 guifg=#ffffff",
\ "cterm=bold ctermfg=7 ctermbg=16 gui=bold guibg=#4c8f2f guifg=#ffffff",
\ "cterm=bold ctermfg=7 ctermbg=50 gui=bold guibg=#1060a0 guifg=#ffffff",
\ "cterm=bold ctermfg=7 ctermbg=56 gui=bold guibg=#a0b0c0 guifg=black",
\ ]
let g:now_hi = 0
fu! HlIni()
for v in g:search_hl
exe "hi UserSearchHi" . index(g:search_hl, v) . " " . v
endfor
endf
cal HlIni()
fu! HiSet() abort
let cw = expand('<cword>')
let already = filter(getmatches(), {i, v -> v['pattern'] == cw})
if len(already) > 0
cal matchdelete(already[0]['id'])
retu
endif
cal matchadd("UserSearchHi" . g:now_hi, cw)
let g:now_hi = g:now_hi >= len(g:search_hl)-1 ? 0 : g:now_hi + 1
endf
" hitspop
let g:hitpopid = ''
fu! Hitpop()
cal popup_clear(g:hitpopid)
let g:hitpopid = popup_create(expand('<cword>'), #{ border: [], pos: "topleft", line: 1, col: &columns - 15 })
endf
quick-scope
fで移動する先をハイライト。まだ右方向だけですが… まぁ0すればいいよね。
本家同様、先頭文字だけでなく、1撃で行ける文字を剪定しています。
どうしても1回で行けない時はセカンダリのハイライトになります。
aug qs_colors
au!
au ColorScheme * highlight QuickScopePrimary cterm=bold ctermfg=196 ctermbg=0 guifg=#66D9EF guibg=#000000
au ColorScheme * highlight QuickScopeSecondary ctermfg=161 ctermbg=0 guifg=#66D9EF guibg=#000000
au CursorMoved * cal HiFLine()
aug END
let g:fmode_flg = 1
fu! FModeToggle(...)
if a:0 == 1
let g:fmode_flg = a:1
else
let g:fmode_flg = g:fmode_flg == 1 ? 0 : 1
endif
endf
fu! HiFLine()
if g:fmode_flg == 0
retu
endif
cal HiReset('QuickScopePrimary')
cal HiReset('QuickScopeSecondary')
let line = line('.')
let now_line = getline('.')
let target_arr = []
let target_arr_second = []
let col = col('.')
let offset = col('.')
while offset != -1
let start = matchstrpos(now_line, '\<.', offset)
let ashiato = now_line[col:start[1]-1]
if stridx(ashiato, start[0]) == -1
cal add(target_arr, [line, start[2]]) " start char col
elseif start[2] > 0
let next_char = now_line[start[2]:start[2]]
cal add(stridx(ashiato, next_char) == -1 ? target_arr : target_arr_second , [line, start[2]+1])
endif
let offset = matchstrpos(now_line, '.\>', offset)[2]
endwhile
cal matchaddpos("QuickScopePrimary", target_arr, 16)
cal matchaddpos("QuickScopeSecondary", target_arr_second, 16)
endf
これから
あとよく使われるものって、
lsp系
snippet系
easymotion
だと思います。
さすがにlsp系は、そもそも別でlsをインストールする必要がありますし
スニペットも別途定義ファイルが必要で、cocとかみたいなlsp対応もありますので
完全独立vim単体であとeasymotionを、ミニマムに再現したいなと思ってます。
2022-11-22 quickscopeの再現を追加