VIM与模糊搜索神器FZF的集成用法 - 从简单到高级

harriszh

FZF and VIM

前言

fzf本身并不是一个vim 插件,本来作者只提供了基本的wrapper函数(比如fzf#run). 但后来作者发现很多人并不熟悉VIMScript, 所以就创建一个默认的vim plugin.

为什么在VIM里用fzf?

fzf可以异步地运行,不影响vim操作,比同类的其他插件都快得多。

如何安装

有两种安装方式vundle或vim-plug

vundle

set rtp+=/home/harriszh/.fzf/
...
Plugin 'junegunn/fzf.vim'

vim-plug

Plug '/usr/local/opt/fzf'
Plug 'junegunn/fzf.vim'

如果你希望通过vim-plug来安装fzf, 那么使用下面设置

Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' }
Plug 'junegunn/fzf.vim'

vim下支持的命令

这些命令都是FZF调用某个工具产生文件,文件内容, tag, comment, command,然后FZF用一个小窗口把它们显示出来,用户就可以用模糊搜索的方式来选择一个或多个选项,按下enter键后就可以用VIM打开它们或跳转到相应的行。
如Files针对的就是文件, GFiles针对的就是git文件

Command List
Files [PATH] 普通文件查找 (similar to :FZF)
GFiles [OPTS] git文件查找 (git ls-files)
GFiles? git文件查找 (git status)
Buffers buffer文件切换
Colors Color schemes
Ag [PATTERN] ag search result (ALT-A to select all, ALT-D to deselect all)
Lines [QUERY] 加载的所有buffer里查找
BLines [QUERY] 在当前buffer里查找包含某关键词的行
Tags [QUERY] 以Tag查找 (ctags -R)
BTags [QUERY] Tags in the current buffer
Marks Marks
Windows Windows
Locate PATTERN locate command output
History v:oldfiles and open buffers
History: 命令历史查找
History/ Search history
Snippets Snippets (UltiSnips)
Commits Git commits (requires fugitive.vim)
BCommits Git commits for the current buffer
Commands Commands
Maps Normal mode mappings
Helptags Help tags 1
Filetypes File types

例子

FilesFZF一样的作用,它会列出所有文件,选中后vim会打开选中的文件
fzf-files

Buffers用于在存在于buffer中的文件间切换
fzf-buffers

Lines <keyword>用于在存在于buffer里的文件中寻找含有某个关键词的行
fzf-lines

BLines <keyword>Lines类似,只不过它只在当前buffer里查找

因为ripgrep是目前性能最好的文本内容搜索工具,所以我们可以自己定义一个命令

command! -bang -nargs=* Rg
  \ call fzf#vim#grep(
  \   'rg --column --line-number --no-heading --color=always --smart-case '.shellescape(<q-args>), 1,
  \   <bang>0 ? fzf#vim#with_preview('up:60%')
  \           : fzf#vim#with_preview('right:50%:hidden', '?'),
  \   <bang>0)

这样输入:Rg <keyword>会调用ripgrep来递归搜索当前目录
fzf-rg

定制化

按键绑定

上面的命令都可以通过ctrl-t, ctrl-x, ctrl-v来在new tab, new split, new vsplit窗口打开

" This is the default extra key bindings
let g:fzf_action = {
  \ 'ctrl-t': 'tab split',
  \ 'ctrl-x': 'split',
  \ 'ctrl-v': 'vsplit' }

" Default fzf layout
" - down / up / left / right
let g:fzf_layout = { 'down': '~40%' }

" In Neovim, you can set up fzf window using a Vim command
let g:fzf_layout = { 'window': 'enew' }
let g:fzf_layout = { 'window': '-tabnew' }
let g:fzf_layout = { 'window': '10split enew' }

" Customize fzf colors to match your color scheme
let g:fzf_colors =
\ { 'fg':      ['fg', 'Normal'],
  \ 'bg':      ['bg', 'Normal'],
  \ 'hl':      ['fg', 'Comment'],
  \ 'fg+':     ['fg', 'CursorLine', 'CursorColumn', 'Normal'],
  \ 'bg+':     ['bg', 'CursorLine', 'CursorColumn'],
  \ 'hl+':     ['fg', 'Statement'],
  \ 'info':    ['fg', 'PreProc'],
  \ 'border':  ['fg', 'Ignore'],
  \ 'prompt':  ['fg', 'Conditional'],
  \ 'pointer': ['fg', 'Exception'],
  \ 'marker':  ['fg', 'Keyword'],
  \ 'spinner': ['fg', 'Label'],
  \ 'header':  ['fg', 'Comment'] }

" Enable per-command history.
" CTRL-N and CTRL-P will be automatically bound to next-history and
" previous-history instead of down and up. If you don't like the change,
" explicitly bind the keys to down and up in your $FZF_DEFAULT_OPTS.

let g:fzf_history_dir = '~/.local/share/fzf-history'

本地设定

" [Buffers] 如果可能跳到已存在窗口
let g:fzf_buffers_jump = 1

" [[B]Commits] 自定义被'git log'使用的选项
let g:fzf_commits_log_options = '--graph --color=always --format="%C(auto)%h%d %s %C(black)%C(bold)%cr"'

" [Tags] 定义用来产生tag的命令
let g:fzf_tags_command = 'ctags -R'

" [Commands] --expect expression for directly executing the command
let g:fzf_commands_expect = 'alt-enter,ctrl-x'

高级定制

也可以使用autoload函数来定义自己的命令

" Command for git grep
" - fzf#vim#grep(command, with_column, [options], [fullscreen])
command! -bang -nargs=* GGrep
  \ call fzf#vim#grep(
  \   'git grep --line-number '.shellescape(<q-args>), 0,
  \   { 'dir': systemlist('git rev-parse --show-toplevel')[0] }, <bang>0)

" Override Colors command. You can safely do this in your .vimrc as fzf.vim
" will not override existing commands.
command! -bang Colors
  \ call fzf#vim#colors({'left': '15%', 'options': '--reverse --margin 30%,0'}, <bang>0)

" Augmenting Ag command using fzf#vim#with_preview function
"   * fzf#vim#with_preview([[options], preview window, [toggle keys...]])
"     * For syntax-highlighting, Ruby and any of the following tools are required:
"       - Highlight: http://www.andre-simon.de/doku/highlight/en/highlight.php
"       - CodeRay: http://coderay.rubychan.de/
"       - Rouge: https://github.com/jneen/rouge
"
"   :Ag  - Start fzf with hidden preview window that can be enabled with "?" key
"   :Ag! - Start fzf in fullscreen and display the preview window above
command! -bang -nargs=* Ag
  \ call fzf#vim#ag(<q-args>,
  \                 <bang>0 ? fzf#vim#with_preview('up:60%')
  \                         : fzf#vim#with_preview('right:50%:hidden', '?'),
  \                 <bang>0)

" Similarly, we can apply it to fzf#vim#grep. To use ripgrep instead of ag:
command! -bang -nargs=* Rg
  \ call fzf#vim#grep(
  \   'rg --column --line-number --no-heading --color=always --smart-case '.shellescape(<q-args>), 1,
  \   <bang>0 ? fzf#vim#with_preview('up:60%')
  \           : fzf#vim#with_preview('right:50%:hidden', '?'),
  \   <bang>0)

" Likewise, Files command with preview window
command! -bang -nargs=? -complete=dir Files
  \ call fzf#vim#files(<q-args>, fzf#vim#with_preview(), <bang>0)

映射

Mapping Description
<plug>(fzf-maps-n) Normal mode mappings
<plug>(fzf-maps-i) Insert mode mappings
<plug>(fzf-maps-x) Visual mode mappings
<plug>(fzf-maps-o) Operator-pending mappings
<plug>(fzf-complete-word) cat /usr/share/dict/words
<plug>(fzf-complete-path) Path completion using find (file + dir)
<plug>(fzf-complete-file) File completion using find
<plug>(fzf-complete-file-ag) File completion using ag
<plug>(fzf-complete-line) Line completion (all open buffers)
<plug>(fzf-complete-buffer-line) Line completion (current buffer only)

映射用法

" Mapping selecting mappings
nmap <leader><tab> <plug>(fzf-maps-n)
xmap <leader><tab> <plug>(fzf-maps-x)
omap <leader><tab> <plug>(fzf-maps-o)

" Insert mode completion
imap <c-x><c-k> <plug>(fzf-complete-word)
imap <c-x><c-f> <plug>(fzf-complete-path)
imap <c-x><c-j> <plug>(fzf-complete-file-ag)
imap <c-x><c-l> <plug>(fzf-complete-line)

" Advanced customization using autoload functions
inoremap <expr> <c-x><c-k> fzf#vim#complete#word({'left': '15%'})

创建自己的插件

fzf#run()是vim集成的核心函数,它接受一个字典变量作为输入, 你至少要通过sink选项来告诉fzf如何处理选中的条目。
比如:

call fzf#run({'sink': 'tabedit', 'options': '--multi --reverse'})

call fzf#run({'source': 'git ls-files', 'sink': 'e', 'right': '40%'})

call fzf#run({'source': map(split(globpath(&rtp, 'colors/*.vim')),
            \               'fnamemodify(v:val, ":t:r")'),
            \ 'sink': 'colo', 'left': '25%'})

下表是它可用的所有选项

Option name Type Description
source string External command to generate input to fzf (e.g. find .)
source list Vim list as input to fzf
sink string Vim command to handle the selected item (e.g. e, tabe)
sink funcref Reference to function to process each selected item
sink* funcref Similar to sink, but takes the list of output lines at once
options string Options to fzf
dir string Working directory
up/down/left/right number/string Use tmux pane with the given size (e.g. 20, 50%)
window (Neovim only) string Command to open fzf window (e.g. vertical aboveleft 30new)
launcher string External terminal emulator to start fzf with (GVim only)
launcher funcref Function for generating launcher string (GVim only)

completion helper

fzf#vim#complete是一个helper函数,用来创建自己的自动补全功能。 如果第一个参数是一个命令字符或一个vim list, 那么它会被用作source.

" Replace the default dictionary completion with fzf-based fuzzy completion
inoremap <expr> <c-x><c-k> fzf#vim#complete('cat /usr/share/dict/words')

对于高级用户,可以传入一个字典选项。它的选项和fzf#run是一致的,除了下面几个选项。

  • reducer (funcref)

    • 把fzf的输出转成单一字符串
  • prefix (funcref or string; default: k*$)

    • 用于匹配想自动补全字符串的正则表达式
    • 或者是一个函数
  • sourceoptions可以是一个函数引用, 它用prefix作为输入参数,返回最终的值
  • sinksink*被忽略
" 全局补全 (不仅仅是buffers. 需要安装ripgrep)
inoremap <expr> <c-x><c-l> fzf#vim#complete(fzf#wrap({
  \ 'prefix': '^.*$',
  \ 'source': 'rg -n ^ --color always',
  \ 'options': '--ansi --delimiter : --nth 3..',
  \ 'reducer': { lines -> join(split(lines[0], ':\zs')[2:], '') }}))

Reducer例子:

function! s:make_sentence(lines)
return substitute(join(a:lines), '^.', '\=toupper(submatch(0))', '').'.'
endfunction

inoremap <expr> <c-x><c-s> fzf#vim#complete({
\ 'source':  'cat /usr/share/dict/words',
\ 'reducer': function('<sid>make_sentence'),
\ 'options': '--multi --reverse --margin 15%,0',
\ 'left':    20})

总结

结合FZF,vim可实现快速文件跳转,特别是在结合Rg或ctags或git以后,可以快速地跳转到满足某种条件的文件中。
希望大家可以结合FZF创造出更多的使用方法。有任何好点子,欢迎联系本人

阅读 13.6k

验证的进阶之路
虽然从事的是芯片验证行业, 但对计算机体系结构,芯片架构,设计,验证,后端,软件前端,后端,C/C++,...

技术是愉悦自己的工具,是发现自我价值的手段

288 声望
68 粉丝
0 条评论
你知道吗?

技术是愉悦自己的工具,是发现自我价值的手段

288 声望
68 粉丝
宣传栏