vim plugin介绍之LeaderF
LeaderF现在基本是Vim最好的模糊查找插件. 其基本功能和演示可以参考作者Yggdroot的几篇文章介绍:
本文是用来记录个人对工具的理解和学习过程. 记录各种应用场景里如何使用LeaderF, 如何配置.
什么是模糊查找
模糊查找的应用场景特别常见. 就是在海量信息中查找你想要的信息.
传统的办法是, 首先你要确定你要找的信息的内容, 然后遍历所有信息, 找到你要的. 比如说,你在一个有上千个文件的文件夹中找到你需要的文件, 首先你需要记住文件名, 然后文件夹中的 文件按照字母顺序排序, 然后你按照字母顺序找到你要的文件.
模糊查找的解决方案是这样的, 你不需要全部记住你要找的信息, 只需要记住里面的一部分, 甚至几个字母. 然后你按照你的记忆敲入你记住的部分, 然后查找软件会把所有匹配上的信息列出来, 你输入的越多, 列出来的越少. 最后知道你看到你要找的信息, 直接选中就行. 这种方法特别符合人性, 机械记忆弱爆(内存少), 但图像记忆特别强. 所以记不住全部, 但一看见就认识.
LeaderF基础功能就是及其快速的模糊查找. 但跟很多别的模糊查找软件不同, LeaderF对于刚才说的海量信息提供给用户多种选择, 既可以是海量文件, 也可以是海量文本. 后面会介绍各种使用场景, 和配置方法.
文件检索
就是前面说的那个场景, 有一大堆文件, 输入几个字母就可以筛选出少数几个文件, 然后把你想要的那个挑出来. VIM过去有一个流行的插件叫CtrlP. 就是实现这个功能. 不过根据作者的说法, 那个插件效率比较差.
快捷键配置
只需要设置 1
let g:Lf_ShortcutF = '<c-p>'
根目录配置
但这里还有个问题, 你需要告诉LeaderF到底在哪些目录中查找文件.
这里就需要配置两个全局变量g:Lf_RootMarkers
和g:Lf_WorkingDirectoryMode
.
前者是告诉LeaderF什么样的目录是你项目的根目录,
后者是告诉LeaderF按照什么规则找根目录. 我一般配置成下面这样:
1
2let g:Lf_WorkingDirectoryMode = 'AF'
let g:Lf_RootMarkers = ['.git', '.svn', '.hg', '.project', '.root']
通常这么配置完了, 如果你工作在任何SCM管理的项目上(比如git, subversion, hg), 都无需做额外配置, 否则可以自己手动在根目录下建立一个空的.root文件或者.project文件就可以正常使用了
索引方式
LeaderF提供给用户3种不同的文件索引方式.
- 如果LeaderF发现根目录是一个git或者hg的仓库(repository),
使用git或者hg作为索引文件的工具. 这个是LeaderF的默认配置.
选项
g:Lf_UseVersionControlTool
如果被用户设置成0
, 则LeaderF不会使用git或者hg. - 如果第一条不满足,
即或者选项
g:Lf_UseVersionControlTool==0
, 或者根目录不是一个git仓库, 或者hg仓库. 则LeaderF会检查是否有配置外部第三方工具. 选项g:Lf_DefaultExternalTool
是用来配置这项的. 这个选项可以是'rg', 'pt', 'ag', 'find'之一, 顺序代表优先级. 关于这些工具, 下面一节有介绍. LeaderF默认是不配置的, 即默认不使用第三方工具. - 使用python脚本索引文件, 这个比前两个来得慢, 但因为都是异步操作, 如果文件量不大的话基本无感.
- 如果
g:Lf_ExternalCommand
被设置了, 前面所有的选项都作废, 只根据这个命令来索引. 这个一般用不到.
我的配置是 1
2let g:Lf_UseVersionControlTool=1 "这个是默认选项, 可以不写
let g:Lf_DefaultExternalTool='rg'
忽略文件
有一个应用场景也很常见, 如果索引的文件里有很多文件是你不想要关注的. 但是你一检索, 他们会跳出来产生干扰. 比如编译生成的object文件. 比如某些SCM工具的辅助文件等等, 有什么办法可以通过提前设置而忽略掉这些文件么.
如果LeaderF使用前面说的第一种索引, 即用git或者hg索引,
这时LeaderF会通过repo的.gitignore
或者.hgignore
来忽略文件.
如果用其他的索引方法,
就要通过一个选项g:Lf_WildIgnore
来设置忽略的文件. 当然,
对于不同的项目需要忽略的文件可能不一样.
这时候就可以用类似下面的autocmd
命令来为不同的项目配置不同的忽略文件.
1 | autocmd BufNewFile,BufRead X:/yourdir* let g:Lf_WildIgnore={'file':['*.vcproj', '*.vcxproj'],'dir':[]} |
注意, 如果配置了之后还不工作,
可以尝试在现实文件列表之后按F5
刷新一下缓存.
运行LeaderF之后的操作
可以通过help leaderf-prompt
查看所有的操作,
比较常用的我列在下面: 1
2
3
4
5
6
7
8
9
10
11
12
13<C-C>, <ESC> : 退出
<C-R> : 在模糊查询和正则表达式模式间切换
<C-F> : 在全路径搜索和名字搜索模式间切换
<Tab> : 切换成normal模式
<C-V>, <S-Insert> : 从剪切板里copy字符串进行查询
<C-U> : 清除已经打出的字符
<C-J>, <C-K> : 在结果列表中移动
<Up>, <Down> : 从历史记录里调出上一次/下一次的输入pattern
<2-LeftMouse> or <CR> : 打开在光标处的文件或者被选择的多个文件
<F5> : 刷新缓存
<C-P> : 预览选中结果
<C-Up> : 在预览popup窗口里滚动向上
<C-Down> : 在预览popup窗口里滚动向下tab
,
则会进入normal模式, 可以使用大部分normal模式命令,
但是还是有一些normal模式下的LeaderF命令,
你可以在normal模式下按F1
来显示这些命令. 支持选择多个文件,
也可以分裂窗口打开文件.
其他有用选项
1 | g:Lf_ShowHidden 设置1则显示隐藏文件. 默认值0. |
我的配置是 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29if has('nvim')
let s:cachedir = expand(stdpath('cache'))
let s:configdir = expand(stdpath('config'))
else
"vim will share same folder with nvim
if has('win32')
let s:cachedir = expand('~/AppData/Local/Temp/cache/nvim')
let s:configdir = expand('~/AppData/Local/nvim')
else
let s:cachedir = expand('~/.cache/nvim')
let s:configdir = expand('~/.config/nvim')
endif
endif
let g:Lf_PreviewInPopup = 1
let g:Lf_WindowHeight = 0.30
let g:Lf_CacheDirectory = s:cachedir
let g:Lf_StlColorscheme = 'powerline'
let g:Lf_PreviewResult = {
\ 'File': 0,
\ 'Buffer': 0,
\ 'Mru': 0,
\ 'Tag': 0,
\ 'BufTag': 1,
\ 'Function': 1,
\ 'Line': 1,
\ 'Colorscheme': 0,
\ 'Rg': 0,
\ 'Gtags': 0
\}
关于popup preview窗口, 这个是使用了vim8.1和neovim的floating window的新功能, 我试用了一下, gvim上表现还不错. neovim-qt上 popup窗口的颜色在某些colorscheme下会非常难以辨识字符, 而且不确定是neovim本身的问题, 还是LeaderF的问题. 但popup窗口的优点也难以割舍, 它可以让你不变动当前位置的情况下预览.
注意, 这里讲的选项, 有些对本文后面讲的其他功能部分也有用.
正则表达式/字符串检索
正则表达式或者字符串检索, 也即是常说的grep操作, 就是在指定的文件集合中查找那些包含已知的字符串或者包含能匹配上正则表达式字符串的行.
如果是读/写代码, 这个应用场景明显比查找文件使用率还高. 但这种查找一旦文件多了计算量将会非常大. 比如在linux源代码中查找一个变量. 所以通常用一种类grep的专门的软件实现.
这里LeaderF无法直接实现这个功能. 而是需要一个外援. 能够实现这个功能的软件很多, 包扩GNU grep, ack, ag, rg, tp等等. 访问这里Feature comparison of ack, ag, git-grep, GNU grep and ripgrep可以比较这些软件的功能. 而这篇ripgrep is faster than {grep, ag, git grep, ucg, pt, sift}有人对这些软件的速度进行过测试. LeaderF的外援就是ripgrep, 简称rg.
rg检索的结果会导入到LeaderF里再进行模糊查找.
rg
首先需要安装rg, 下载地址在这里, 并且让rg所在目录在系统路径下. 然后可以用的如下命令来调用rg.
1 | Leaderf rg ... |
调用之后, rg的结果就会在LeaderF窗口里列出,
之后就可以继续用前面提到的模糊匹配, 进一步筛选出你想要的行.
这里rg后面需要接的参数都是rg自身的参数,
可以用Leaderf rg --help
来查看rg怎么使用.
与rg相关的按键映射
自己记忆参数用起来肯定不方便. 用得最多的场景是, 检索光标所在位置的词, 或者在visual模式下选中的词. 对于这种场景LeaderF提供了一系列可以映射的句柄. 下面是我自己的映射.
1 | nmap <unique> <leader>fr <Plug>LeaderfRgPrompt |
快捷键主要是看自己的习惯, 配置完以后, 就可以光速查找当前词. 解释一下:
快捷键 | 解释 |
---|---|
<leader>fr |
将会在命令行显示 :Leaderf rg -e ,
然后等待用户输入想要查询的正则表达式. |
<leader>fra |
将会直接查询光标所在的词语或者visual模式下选中的词语, 但是没有边界.
没有边界的意思是说如果你查word ,
包含abcwordxyz 的行也会被搜到. |
<leader>frb |
将会直接查询光标所在的词语,或者visual模式下选中的词语 但是有边界.
有边界的意思是说如果你查word , 包含abcwordxyz
的行不会被搜到,
只有包含类似abc word xyz 字段的行才会被搜到. |
<leader>frc |
将会直接把光标所在的词语或者visual模式下选中的词语作为正则表达式检索, 但是没有边界. |
<leader>frd |
将会直接把光标所在的词语或者visual模式下选中的词语作为正则表达式检索, 但是有边界. |
注意敲完快捷键之后需要按一下回车,
这个是LeaderF作者给用户留下了编辑命令的机会. 这几个快捷方式,
只使用了下面几个rg
选项:
-i
忽略大小写.-e <PATTERN>
正则表达式搜索.-F
搜索字符串而不是正则表达式.-w
搜索只匹配有边界的词.
rg的其他有用的选项
还有一些常见的场景前面的几个快捷方式无法实现, 比如想要只检索某些特定的文件或者排除掉某些文件.
rg选项 | 解释 |
---|---|
-g, --glob <GLOB> |
用通配符限定或者排除检索的文件或者目录. 可以使用多次.
通配符的使用跟.gitignore 一致. 具体语法参考这里.
在glob前加! 表示排除. |
-iglob <GLOB> |
跟前一个一样, 但是忽略大小写. |
-t, --type <TYPE> |
文件类型检索, rg内置了很多不同的文件类型,
可以使用--type-list 来查看其支持的所有文件类型.
比如-t cpp 等于*.[chH], *.[chH].in, *.cats .
一个特例是-t all 代表检索所有支持的文件类型里匹配的文件. |
LeaderF选项 | 解释 |
---|---|
g:Lf_RgConfig |
可以提前制定一系列rg选项 |
g:Lf_RgStorePattern |
可以指定一个vim的寄存器, 一旦检索,
-e 后面的内容会被存储进入这个寄存器并且转换成vim的搜索格式.
这个寄存器可以在vim的/ 或? 搜索命令中直接使用. |
注意:
前一部分讲的文件搜索里用到的LeaderF选项g:Lf_WildIgnore
在这里不起作用,
所以只能用-g "!xxx"
来排除文件.
但是用选项g:Lf_RgConfig
可以把rg的选项提前设置好.
特别是针对于某个项目用autocmd
配置特别的检索选项可以减少误报和漏报.
静态符号索引+模糊查找
前面的rg是通过字符串或者正则表达式进行查找, 更进一步的检索就是静态符号索引. 这篇描述vim默认的标签系统的 -- vim标签系统可供参考.
基础用法
LeaderF的作者考虑到静态符号检索出来的结果也可以用来模糊查询. 所以也集成了静态符号索引到中. 下面是LeaderF作者君对基础用法的介绍.
Leaderf gtags:模糊匹配与最强静态符号索引工具的完美结合
作者放弃了传统的ctags
,
而是使用了另外一个功能更强大的静态符号索引软件gtags
这一篇是介绍gtags的使用的.
这篇文章中介绍了另外一个插件vim-gutentags,
这个插件是用来管理静态符号索引文件的, 可以同时支持ctags和gtags.
Leaderf也可以管理标签文件, 只要打开如下开关: 1
2
3
4let g:Lf_GtagsAutoGenerate = 1
let g:Lf_GtagsSource = 1
let g:Lf_Gtagsconf = '<gtags_root_path>/share/gtags/gtags.conf'
let g:Lf_Gtagslabel = 'native-pygments'
解释一下,
g:Lf_GtagsAutoGenerate
是用来告诉LeaderF在打开文件后自动生成gtags数据库.
g:Lf_GtagsSource
可以配置3个值, [0,1,2], 默认值0.
用来配置gtags的需要分析的文件有哪些. 默认值0意思是gtags自己解决,
也就是需要自己添加gtags的选项, 不配置的话, 我理解就是所有文件. 设置成1,
将使用本文第一部分讲的文件索引方式.
设置成2, 就需要自己通过配置g:Lf_GtagsfilesCmd
.
总结就是如果是小项目, 不用管这个选项. 如果是大项目,
你前面文件检索已经配置过了, 就设置成1. 如果你项目特殊, 设置成2,
然后自己动手配置文件来源.
g:Lf_Gtagsconf
是用来指定gtags.conf文件的位置,
Lf_Gtagslabel
是说c/c++用gtags自己的解析器,
其他语言以来于pygments
. pygments
是一个python
module, 可以通过pip install pygments
安装.
但为了让两个插件共享标签数据库, 可以如下设置: 1
2
3let g:Lf_GtagsAutoGenerate = 0
let g:Lf_GtagsGutentags = 1
let g:gutentags_cache_dir = expand(g:Lf_CacheDirectory.'\.LfCache\gtags')gutentags
生成的,
同时把gutentags
的标签生成目录设置成leaderf的标签目录缓存.
leaderf+gtags快捷方式
下面是我的相关配置:
1 | nmap <unique> <leader>fgd <Plug>LeaderfGtagsDefinition |
关于快捷键, 配置完后,
<leader>fgd
约等于g_Ctrl-]
,
区别是这里加入了模糊搜索和异步执行.
<leader>fgr
是ctags不提供的找引用的功能,
<leader>fgs
约等于前面的<leader>frb
,
<leader>fgg
约等于前面的<leader>fra
,
之所以这么配置, 主要是我打算根据长期使用比较一下哪个rg和gtags更好用.
<leader>fgo
打开上一次检索的结果,
<leader>fgn
和<leader>fgp
基本等于:tn
,
:tp
, 在匹配列表中上下跳转.
这里我发现了一个疑似bug, 提交给了作者: issue#533
其他搜索
模糊搜索和异步执行作为LeaderF的核心武器, 可以扩展到所有的信息搜索上, LeaderF的作者在这里下了很多功夫, 以下是LeaderF提供的所有搜索功能
功能 | 解释 |
---|---|
:LeaderfFile | 本文第一部分讲的就是这个命令 |
:LeaderfBuffer | 在所有打开的buffer里根据buffer的名字搜索相应的buffer, 这个可以用来替代:ls |
:LeaderfBufferAll | 同上, 除了加入隐藏的buffer, 比如帮助文档等. |
:LeaderfMru | 搜索most recently used file, 默认显示100个, 可以配置数量. |
:LeaderfMruCwd | 同上, 但只在当前的工作路径下搜索 |
:LeaderfTag | 在tag文件中检索, 注意这个是ctags的tag文件. |
:LeaderfBufTag | 同上, 但只检索当前buffer的tags |
:LeaderfBufTagAll | 同上, 但查找所有listed buffers的tags |
:LeaderfFunction | 查找当前文件的函数或者方法, 这个基本可以用来替代tarbar |
:LeaderfFunctionAll | 同上, 但列出所有listed buffers的函数和方法 |
:LeaderfLine | 在当前文件搜索行, 可以用来替代/ 和? |
:LeaderfLineAll | 同上, 但搜索所有的listed buffers的行. |
:LeaderfHistoryCmd | 检索所有执行过的vim命令 |
:LeaderfHistorySearch | 检索所有执行过的vim搜索 |
:LeaderfSelf | 列出所有LeaderF的可执行命令, 供用户检索, 有了这个, 可以不用记忆所有其他命令了. |
:LeaderfHelp | 在help tags检索 |
:LeaderfColorscheme | 在所有可用的colorscheme里检索 |
:LeaderfFiletype | 在所有可用的文件类型里检索 |
:LeaderfCommand | 在所有可用的vim命令(包括内置的和用户定义的)里检索 |
:LeaderfWindow | 在所有vim的windows里检索 |
:LeaderfRgInteractive | 以交互模式执行Leaderf rg |
:LeaderfRgRecall | 执行执行过的最后次Leaderf rg 命令 |
我把里面最常用的定义了一些快捷键
1 | let g:Lf_ShortcutF = '<c-p>' |
这里有个窍门是, 一旦想不起快捷键,
<leader>f
就会直接列出所有可用的Leaderf命令.
总结
总结一下, 有了一个LeaderF, 几乎所有用来检索的插件甚至内部命令都可以不要了. 这里得除了根据语言语法的动态检索以外. 这是另外的话题, 以后再说.
LeaderF的配置还是需要打磨一下, 文件检索基本是傻瓜模式, 但其他, 尤其是配合rg和gtags还是需要花功夫配置一下, 否则用起来不会有完美体验.
下面是我所有的配置总结.
1 | "-------------- |
返回vim技巧总结