KEY/EVENT BINDINGS
--bind option allows you to bind a key or an event to one or more actions. You can use it to customize key bindings or implement dynamic behaviors.
--bind takes a comma-separated list of binding expressions. Each binding expression is KEY:ACTION or EVENT:ACTION.
e.g.
fzf --bind=ctrl-j:accept,ctrl-k:kill-line
RELOAD INPUT
reload(...) action is used to dynamically update the input list without restarting fzf. I
takes the same command template with placeholder expressions as execute(...).
See https://github.com/junegunn/fzf/issues/1750 for more info.
e.g.
# Update the list of processes by pressing CTRL-R
ps -ef | fzf --bind 'ctrl-r:reload(ps -ef)' --header 'Press CTRL-R to reload' \
--header-lines=1 --layout=reverse
# Integration with ripgrep
RG_PREFIX="rg --column --line-number --no-heading --color=always --smart-case "
INITIAL_QUERY="foobar"
FZF_DEFAULT_COMMAND="$RG_PREFIX '$INITIAL_QUERY'" \
fzf --bind "change:reload:$RG_PREFIX {q} || true" \
--ansi --disabled --query "$INITIAL_QUERY"
不要点开, 博客网站用的
一、工具介绍
※ 主角
fzf:本文的主角,具体是什么我就不多介绍了,相信点进来文章的读者都知道。
※ 辅助工具
以下是用于说明如何实现这一动态切换的辅助工具,非必须,只是为了方便说明,必须的只有 fzf。
二、原理说明
2.1. mru 和 files 列表的获取
※ mru
首先你需要一个保存最近打开文件列表的文件实现,例如结合 neovim 和 coc.nvim,我们可以在 neovim 中打开一个文件的时候 coc.nvim 会自动触发一个保存事件,把当前打开的文件的绝对路径保存到一个文件中,这个文件就是 mru 文件列表,格式如下:
~/.config/coc/mru
文件:※ files
其次你需要一个文件搜索工具,例如 fd,它可以搜索当前目录下的所有文件,然后把搜索结果输出到 stdout。例如 fd 的
fd --type f --hidden
或 find 的find . -type f
。结合 fzf,我们可以把 fd 的搜索结果作为 fzf 的输入,然后 fzf 会把搜索结果展示到 fzf 的界面上,然后我们就可以通过 fzf 的交互来选择我们想要的文件。例如:
相信大家都知道如何使用这些工具,这里就不多说了。
2.2. fzf 的 KEY/EVENT BINDINGS
通过
man fzf
我们可以查到和 key/event 相关的配置,其中有这么一段话:这段话的意思是说我们可以通过
--bind
来绑定一个 key 或者 event 到一个或者多个 action,这样我们就可以实现一些自定义的功能,比如我们可以绑定一个 key 到一个 shell 脚本,这样当我们按下这个 key 的时候就会执行这个 shell 脚本,这个 shell 脚本可以是任何我们想要执行的命令,比如打开一个文件、执行一个命令等等。同理通过
--bind
绑定一个 event 到一个 action 也是一样的,只不过 event 是 fzf 内部的事件,比如change
事件,当搜索输入框的值发生变化的时候就会触发这个事件,我们可以通过绑定这个事件到一个 shell 脚本来实现当搜索输入框的值发生变化的时候执行这个 shell 脚本。而我们要实现的动态切换 mru 和 files 就是通过绑定
change
事件来实现的,当搜索输入框的值发生变化的时候,我们就会执行一个 shell 脚本,这个 shell 脚本会根据搜索输入框的值来判断是展示 mru 列表还是 files 列表。change
的文档说明是:而 action 也是指 fzf 内部的一些动作,比如
accept
,当我们按下回车的时候就会触发这个 action,这个 action 会把当前选中的结果输出到 stdout,然后退出 fzf。这里我们需要用的 action 就是reload
,文档的说明是:那么我们就可以通过
fzf --bind change:reload(...)
来实现当搜索输入框的值发生变化的时候重新加载输入列表,这样我们就可以在重新加载输入列表的时候根据搜索输入框的值来判断是展示 mru 列表还是 files 列表了。三、实现
3.1. 初步实现
首先实现一个初步的版本,只在终端中实现切换,不考虑在 neovim 中的实现,主要是为了方便说明。
※ 首先事前工作
详细的上面已经说过了,这里就不再赘述。
※ 切换脚本
~/dynamic_fzf_source.sh
切换脚本:这段脚本的作用就是当没有任何输入的时候显示 mru 文件列表,当有输入的时候搜索当前目录下的所有文件。
而中间那一段 perl 脚本则是用于处理过滤掉 mru 文件中的重复行,因为 mru 文件中可能会有重复的行,这样就会导致 fzf 中展示的列表中有重复的行,所以我们需要过滤掉重复的行,这里使用的是 perl 脚本,当然你也可以使用其他的工具,比如 awk、sort 等等。
※ 绑定 fzf 的 change 事件
这个命令意思就是当搜索输入框的值发生变化的时候重新加载输入列表,这里的输入列表就是
~/dynamic_fzf_source.sh
脚本的输出,然后把搜索输入框的值作为参数传递给~/dynamic_fzf_source.sh
脚本,这样我们就可以在~/dynamic_fzf_source.sh
脚本中根据搜索输入框的值来判断是展示 mru 列表还是 files 列表了。其中
{q}
是 fzf 用于获取搜索输入框的值的占位符,详细的可以查看man fzf
。而
< <($HOME/dynamic_fzf_source.sh)
这一段则是把~/dynamic_fzf_source.sh
脚本的输出作为 fzf 的输入。意思就是先直接执行~/dynamic_fzf_source.sh
脚本(且不传任何参数),然后把它的输出作为 fzf 的输入。※ 效果截图
当前 mru 列表文件展示:
无输入值时,fzf mru 展示:
有输入值时,fzf files 展示:
3.2. 在 neovim 中实现
※ 首先事前工作
※ 切换脚本
该脚本与上面的脚本几乎一样,但是我额外进行了对
cwd
(当前工作目录)的处理,把不在当前工作目录下的文件移除掉,这样就可以使 mru 列表看起来更加简洁。~/.vim/dynamic_fzf_source.sh
切换脚本:基本逻辑和上面的脚本一致,只多了一步对
cwd
的处理。※ 绑定 fzf 的 change 事件
这里需要先在 neovim 中加载 fzf 插件,然后使用以下 vim 代码来构造一个 vim 方法(习惯使用 lua 的同学可以自行转换):
这样我们就可以在 neovim 中使用
<leader>f
来打开 fzf mru/files 了,当然你也可以使用其他的快捷键。※ 效果截图
无输入值时,fzf mru 展示:
有输入值时,fzf files 展示:
四、总结
上面介绍了如何在 fzf 中根据搜索输入框的值来动态切换 mru 和 files 列表,这样我们就可以在 fzf 中实现一个类似 VSCode 中的 ctrl-p 的功能了,当然这只是一个简单的实现,你可以根据自己的需求来进行扩展。
其基本原理使用了 fzf 的
--bind change:reload(...)
来实现。以上就是本文的全部内容,希望对你有所帮助,如果有任何问题欢迎在评论区留言,我会尽快回复:)。