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創造出更多的使用方法。有任何好點子,歡迎聯繫本人

相關文章
相關標籤/搜索