我想实现一个 vim 命令来选择具有以下行为的缓冲区:
- 调用时,它会向用户显示当前目录中加载的缓冲区和其他最近使用的缓冲区的列表(这与
:History
提供的命令不同fzf.vim
,因为我们只列出当前目录中最近使用的缓冲区,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
SpaceSpaces:recent_files()
从 viminfo 列出加载的缓冲区和最近使用的文件的调用。这里有趣的一点是sink*
调用中的选项fzf#run
(从底部开始的第 4 行)。水槽是另一个功能。如果选择了文件名,sink 函数会加载它以进行编辑,否则,它会调用fzf#vim#files
列出目录的内容。
这非常接近我想要的,但有几个问题:
- 当在最近的文件中没有找到匹配项时,用户必须按下Return以触发回退。(人们很容易争辩说这是正确的行为)
- 加载后备 fzf 窗口时,它以正常模式而不是插入模式启动
用户必须在新的 fzf 窗口中再次输入搜索查询(已解决)
我正在寻找有关如何解决这些问题的建议,尤其是 2 和 3。我也对不完全符合规范但提供良好用户体验的解决方案持开放态度。
编辑:我想出了另一种方法来实现这一点,使用它作为 fzf 的源
cat recent_files.txt fifo.txt
whererecent_files.txt
是最近文件的列表,并且fifo.txt
是使用mkfifo
. 可以将映射添加到缓冲区,触发写入 fifo 的命令。这样,用户可以使用该映射来包含所有文件的列表,以防他们在最近的文件中找不到匹配项。如果用户在最近的文件中找到文件并按 Enter 键,这种方法会出现问题。由于 fzf 直到等待从 fifo 读取,所以它不会退出https://github.com/junegunn/fzf/issues/2288