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

FZF and VIM

前言

fzf自己并非一个vim 插件,原本做者只提供了基本的wrapper函数(好比fzf#run). 但后来做者发现不少人并不熟悉VIMScript, 因此就建立一个默认的vim plugin.php

为何在VIM里用fzf?

fzf能够异步地运行,不影响vim操做,比同类的其余插件都快得多。git

如何安装

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

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文件shell

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-filesexpress

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

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

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

由于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创造出更多的使用方法。有任何好点子,欢迎联系本人

相关文章
相关标签/搜索