我想实现一个vim命令来选择具有以下行为的缓冲区:
- 调用时,它会向用户显示当前目录中加载的缓冲区和其他最近使用的缓冲区的列表(这与
fzf.vim
提供的:History
命令不同,因为我们只列出当前目录中最近使用的缓冲器,fzf.vim列出所有最近的缓冲区(。用户可以搜索他们想要加载的文件名 - 如果没有一个选项与用户的搜索项匹配,则通过列出当前目录中的所有文件并让用户模糊搜索来扩大搜索范围
这就是我目前所拥有的(假设junegunn/fzf.vim
已经安装(:
nnoremap <silent> <space><space> :call <SID>recent_files()<CR>
function! s:recent_files_sink(items)
if len(a:items) == 2
execute "edit" a:items[1]
return
endif
call fzf#vim#files("", {'options': ['--query', a:items[0]]})
endfunction
" deduped is a list of items without duplicates, this
" function inserts elements from items into deduped
function! s:add_unique(deduped, items)
let dict = {}
for item in a:deduped
let dict[item] = ''
endfor
for f in a:items
if has_key(dict, f) | continue | endif
let dict[f] = ''
call add(a:deduped, f)
endfor
return a:deduped
endfunction
function! s:recent_files()
let regex = '^' . fnamemodify(getcwd(), ":p")
let buffers = filter(map(
getbufinfo({'buflisted':1}), {_, b -> fnamemodify(b.name, ":p")}),
{_, f -> filereadable(f)}
)
let recent = filter(
map(copy(v:oldfiles), {_, f -> fnamemodify(f, ":p")}),
{_, f -> filereadable(f) && f =~# regex})
let combined = s:add_unique(buffers, recent)
call fzf#run(fzf#wrap({
'source': map(combined, {_, f -> fnamemodify(f, ":~:.")}),
'sink*': function('s:recent_files_sink'),
'options': '--print-query --exit-0 --prompt "Recent> "'
}))
endfunction
SpaceSpace调用s:recent_files()
,该CCD_4列出了从viminfo加载的缓冲区和最近使用的文件。这里有趣的一点是对fzf#run
的调用中的sink*
选项(底部第4行(。水槽是另一个功能。如果选择了文件名,sink函数将加载该文件名进行编辑,否则,它将调用列出目录内容的fzf#vim#files
。
这非常接近我想要的,但有几个问题:
- 当在最近的文件中没有找到匹配项时,用户必须按Return来触发回退。(人们很容易认为这是正确的行为(
- 当加载回退fzf窗口时,它以正常模式而不是插入模式启动
用户必须在新的fzf窗口中再次输入搜索查询(已解决(
我正在寻找如何解决这些问题的建议,特别是第2和第3个问题。我也对那些不完全符合规范但能提供良好用户体验的解决方案持开放态度。
EDIT:我想出了另一种方法来实现这一点,将其作为fzf 的来源
cat recent_files.txt fifo.txt
其中CCD_ 8是最近文件的列表,而CCD_。可以向缓冲区添加映射,该映射触发向fifo写入的命令。这样,用户可以使用该映射来包括所有文件的列表,以防在最近的文件中找不到匹配的文件。在用户找到最近的文件并按enter键的情况下,这种方法会出现问题。由于fzf一直在等待从fifo读取,因此它不会退出https://github.com/junegunn/fzf/issues/2288
我终于能够使用fzf的reload
功能找到一个非常接近的解决方案。(感谢junegunn(
事情就是这样发展的:
nnoremap <silent> <space><space> :call <SID>recent_files()<CR>
" Initialize fzf with a list of loaded buffers and recent files from
" the current directory. If <space> is pressed, we load a list of all
" the files in the current directory
function! s:recent_files()
let regex = '^' . fnamemodify(getcwd(), ":p")
let buffers = filter(map(
getbufinfo({'buflisted':1}), {_, b -> fnamemodify(b.name, ":p")}),
{_, f -> filereadable(f)}
)
let recent = filter(
map(copy(v:oldfiles), {_, f -> fnamemodify(f, ":p")}),
{_, f -> filereadable(f) && f =~# regex})
let combined = <SID>add_unique(buffers, recent)
"-------------------------------------------
" This is the key piece that makes it work
"-------------------------------------------
let options = [
'--bind', 'space:reload:git ls-files',
]
call fzf#run(fzf#wrap({
'source': map(combined, {_, f -> fnamemodify(f, ":~:.")}),
'options': options
}))
endfunction
" deduped is a list of items without duplicates, this
" function inserts elements from items into deduped
function! s:add_unique(deduped, items)
let dict = {}
for item in a:deduped
let dict[item] = ''
endfor
for f in a:items
if has_key(dict, f) | continue | endif
let dict[f] = ''
call add(a:deduped, f)
endfor
return a:deduped
endfunction
我使用<space><space>
启动FZF。此FZF窗口仅包含当前目录中的最新文件。如果我按下<space>
,FZF窗口将更新为从git ls-files
获得的新文件列表。
更新:2023年4月
FZF现在支持zero
事件。当找不到匹配项时,它会被触发。这可以用于触发reload
,就像上面对space
所做的示例一样