簡素にfzfしたい
対話的にfindの記事も書きましたが、動的に検索結果が出るのはやはり魅力的
難点
・nmap問題
ノーマルモードでのpopupを使用しているため、n系のmap全てを独自関数(以下ソースではs:my_key_map
)にまとめておかないといけない
まぁvimrcにまとめられるようにしてんだから一応問題はない
→ exe "mapclear "
で解決.
cal win_execute(winid, "mapclear ")を追加しました
・findキャッシュ問題 →job_startによる非同期処理で対応
結局はfindコマンド結果が必要なため、全件findを別で行う。
検索対象外のオプション変更や、別ディレクトリを検索したい場合、一度キャッシュしたfind結果を更新しなくてはいけない。
これはfindが重いことを考慮して別関数にしているので、わざわざ呼び出さないといけない。
fzfコマンドに劣るが、まぁ流石にしょうがない。ちゃんと.gitフォルダとかを除外してれば、だいたいの場合かかっても3秒で、以降キャッシュ見るからノータイム。
簡単な仕組み解説
ノーマルモードで行う
n系のmapをクリア
popupを作成し、「検索ワード入力」
検索結果が動的に表示される
検索対象は、MRUとファイルを切り替えられる
★popupを2個作成しており
・文字入力用ポップアップ:入力キーを、文字入力とみなし、表示する
・検索結果表示用ポップアップ:入力された時に、検索結果を表示する
エンターキーで入力を終了し、検索結果をjkで選択する
デモ
ソース置いておきます
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 win_execute(g:fzf_enter_win, "mapclear <buffer>")
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') })
cal win_execute(g:fzf_enter_win, "mapclear <buffer>")
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)
return 1
elseif a:key is# "\<CR>"
call popup_close(g:fzf_enter_win)
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