vim 的 grep 外掛`Leaderf rg`:grep 和模糊匹配的完美結合

Yggdroot發表於2019-01-15

前言

vim有很多著名的grep外掛,我使用過的有ack.vim,ag.vim和ctrlsf.vim,它們應該也是目前使用者最多的幾個了。

  1. ack.vim
    起步比較早,早期後端grep工具是ack,後來也支援ag(the_silver_searcher),pt(the_platinum_searcher),rg(ripgrep)等工具了。它是一個比較傳統的grep外掛,不支援非同步,要等到grep結束後才能顯示結果,在大的專案中grep會卡好一陣子。它貌似也不再維護,我N久前提交的pull request還掛在那,它最近的程式碼更新在11個月前。
  2. ag.vim
    它其實是抄襲ack.vim,沒錯,是抄襲。在早期ack.vim還不支援ag時,它的作者在ack.vim程式碼的基礎上稍微改了改,支援了ag。後來被ack.vim的作者給懟了,就放棄了對ag.vim的維護。目前功能上小於ack.vim。
  3. ctrlsf.vim
    這是國人開發的一個外掛, 後端grep工具支援ack/ag/pt/rg,同時也支援非同步,不過需要Vim 8.0.1039+或者NeoVim才支援非同步。這個外掛很好用,在我開發Leaderf rg之前一直使用的是它。

Leaderf rg顧名思義,後端基於rg,由於是LeaderF的子功能,基因上就決定它完美支援非同步。同時LeaderF又是一個非著名的模糊查詢外掛,這使它可以在grep結果的基礎上再通過模糊匹配的方式進行二次過濾,來幫助使用者更快地鎖定目標,這是目前上面提到的外掛所不具備的。

為什麼選擇rg(ripgrep)

快速grep工具目前有ag, rg, pt, sift, ucg等。
我選擇rg有以下幾點原因:

  1. 速度比較快,rg的README上有作者的對比,我實測也是rg快點。
  2. Windows上bug少(bug到目前還沒發現),ag和pt都遇到過bug。
  3. 作者很活躍,提的issue能很快得到回覆。
  4. rg功能相對多些,可以從rg --help 看出來。

Leaderf rg 使用介紹

Leaderf rg的使用也比較簡單,只要Leaderf[!] + rg命令和選項(同命令列上一樣)就可以了。 具體使用方法可以用:Leaderf rg -h來檢視。

usage: Leaderf[!] rg [-h] [-e <PATTERN>...] [-F] [-i] [-L] [-P] [-S] [-s] [-v]
                     [-w] [-x] [--hidden] [--no-config] [--no-ignore]
                     [--no-ignore-global] [--no-ignore-parent]
                     [--no-ignore-vcs] [--no-pcre2-unicode] [-E <ENCODING>]
                     [-M <NUM>] [-m <NUM>] [--max-depth <NUM>]
                     [--max-filesize <NUM+SUFFIX?>]
                     [--path-separator <SEPARATOR>] [--sort <SORTBY>]
                     [--sortr <SORTBY>] [-f <PATTERNFILE>...] [-g <GLOB>...]
                     [--iglob <GLOB>...] [--ignore-file <PATH>...]
                     [--type-add <TYPE_SPEC>...] [-t <TYPE>...] [-T <TYPE>...]
                     [--current-buffer | --all-buffers] [--recall] [--append]
                     [--reverse] [--stayOpen] [--input <INPUT> | --cword]
                     [--top | --bottom | --left | --right | --belowright | --aboveleft | --fullScreen]
                     [--nameOnly | --fullPath | --fuzzy | --regexMode] [--nowrap]
                     [<PATH> [<PATH> ...]]

optional arguments:
  -h, --help            show this help message and exit

specific arguments:
  -e <PATTERN>..., --regexp <PATTERN>...
                        A pattern to search for. This option can be provided multiple times, where all
                        patterns given are searched.
  -F, --fixed-strings   Treat the pattern as a literal string instead of a regular expression.
  -i, --ignore-case     Searches case insensitively.
  -L, --follow          Follow symbolic links while traversing directories.
  -P, --pcre2           When this flag is present, rg will use the PCRE2 regex engine instead of its
                        default regex engine.
  -S, --smart-case      Searches case insensitively if the pattern is all lowercase, case sensitively
                        otherwise.
  -s, --case-sensitive  Searches case sensitively.
  -v, --invert-match    Invert matching. Show lines that do not match the given patterns.
  -w, --word-regexp     Only show matches surrounded by word boundaries. This is roughly equivalent to
                        putting \b before and after all of the search patterns.
  -x, --line-regexp     Only show matches surrounded by line boundaries.
  --hidden              Search hidden files and directories. By default, hidden files and directories
                        are skipped.
  --no-config           Never read configuration files. When this flag is present, rg will not respect
                        the RIPGREP_CONFIG_PATH environment variable.
  --no-ignore           Don't respect ignore files (.gitignore, .ignore, etc.). This implies
                        --no-ignore-parent and --no-ignore-vcs.
  --no-ignore-global    Don't respect ignore files that come from 'global' sources such as git's
                        `core.excludesFile` configuration option (which defaults to
                        `$HOME/.config/git/ignore`).
  --no-ignore-parent    Don't respect ignore files (.gitignore, .ignore, etc.) in parent directories.
  --no-ignore-vcs       Don't respect version control ignore files (.gitignore, etc.).
  --no-pcre2-unicode    When PCRE2 matching is enabled, this flag will disable
                        Unicode mode, which is otherwise enabled by default.
  -E <ENCODING>, --encoding <ENCODING>
                        Specify the text encoding that rg will use on all files searched.
  -M <NUM>, --max-columns <NUM>
                        Don't print lines longer than this limit in bytes.
  -m <NUM>, --max-count <NUM>
                        Limit the number of matching lines per file searched to NUM.
  --max-depth <NUM>     Limit the depth of directory traversal to NUM levels beyond the paths given.
  --max-filesize <NUM+SUFFIX?>
                        Ignore files larger than NUM in size. This does not apply to directories.
  --path-separator <SEPARATOR>
                        Set the path separator to use when printing file paths.
  --sort <SORTBY>       This flag enables sorting of results in ascending order.
  --sortr <SORTBY>      This flag enables sorting of results in descending order.
  -f <PATTERNFILE>..., --file <PATTERNFILE>...
                        Search for patterns from the given file, with one pattern per line.
                        (This option can be provided multiple times.)
  -g <GLOB>..., --glob <GLOB>...
                        Include or exclude files and directories for searching that match the given
                        glob.(This option can be provided multiple times.)
  --iglob <GLOB>...     Include or exclude files and directories for searching that match the given glob.
                        Globs are matched case insensitively.(This option can be provided multiple times.)
  --ignore-file <PATH>...
                        Specifies a path to one or more .gitignore format rules files.
  --type-add <TYPE_SPEC>...
                        Add a new glob for a particular file type.
  -t <TYPE>..., --type <TYPE>...
                        Only search files matching TYPE. Multiple type flags may be provided.
  -T <TYPE>..., --type-not <TYPE>...
                        Do not search files matching TYPE. Multiple type-not flags may be provided.
  <PATH>                A file or directory to search. Directories are searched recursively. Paths
                        specified on the command line override glob and ignore rules.
  --current-buffer      Searches in current buffer.
  --all-buffers         Searches in all listed buffers.
  --recall              Recall last search. If the result window is closed, reopen it.
  --append              Append to the previous search results.

common arguments:
  --reverse             show results in bottom-up order
  --stayOpen            don't quit LeaderF after accepting an entry
  --input <INPUT>       specifies INPUT as the pattern inputted in advance
  --cword               current word under cursor is inputted in advance
  --top                 the LeaderF window is at the top of the screen
  --bottom              the LeaderF window is at the bottom of the screen
  --left                the LeaderF window is at the left of the screen
  --right               the LeaderF window is at the right of the screen
  --belowright          the LeaderF window is at the belowright of the screen
  --aboveleft           the LeaderF window is at the aboveleft of the screen
  --fullScreen          the LeaderF window takes up the full screen
  --nameOnly            LeaderF is in NameOnly mode by default
  --fullPath            LeaderF is in FullPath mode by default
  --fuzzy               LeaderF is in Fuzzy mode by default
  --regexMode           LeaderF is in Regex mode by default
  --nowrap              long lines in the LeaderF window won't wrap

If [!] is given, enter normal mode directly.
複製程式碼

注意:如果:Leaderf後面有感嘆號,會直接進入normal模式;如果沒有感嘆號,則是輸入模式,此時可以輸入字元來進行模糊匹配過濾。可以用tab鍵在兩個模式間來回切換。

Leaderf rg基本支援rg所有的必要選項,使用者如果對rg命令比較熟悉,可以在vim命令列內輸入:Leaderf, 然後手敲rg命令,命令選項還可以通過tab來補全。 當然,更聰明的做法是定義一些快捷鍵。例如:

" search word under cursor, the pattern is treated as regex, and enter normal mode directly
noremap <C-F> :<C-U><C-R>=printf("Leaderf! rg -e %s ", expand("<cword>"))<CR>

" search word under cursor, the pattern is treated as regex,
" append the result to previous search results.
noremap <C-G> :<C-U><C-R>=printf("Leaderf! rg --append -e %s ", expand("<cword>"))<CR>

" search word under cursor literally only in current buffer
noremap <C-B> :<C-U><C-R>=printf("Leaderf! rg -F --current-buffer -e %s ", expand("<cword>"))<CR>

" search word under cursor literally in all listed buffers
noremap <C-D> :<C-U><C-R>=printf("Leaderf! rg -F --all-buffers -e %s ", expand("<cword>"))<CR>

" search visually selected text literally, don't quit LeaderF after accepting an entry
xnoremap gf :<C-U><C-R>=printf("Leaderf! rg -F --stayOpen -e %s ", leaderf#Rg#visual())<CR>

" recall last search. If the result window is closed, reopen it.
noremap go :<C-U>Leaderf! rg --recall<CR>


" search word under cursor in *.h and *.cpp files.
noremap <Leader>a :<C-U><C-R>=printf("Leaderf! rg -e %s -g *.h -g *.cpp", expand("<cword>"))<CR>
" the same as above
noremap <Leader>a :<C-U><C-R>=printf("Leaderf! rg -e %s -g *.{h,cpp}", expand("<cword>"))<CR>

" search word under cursor in cpp and java files.
noremap <Leader>b :<C-U><C-R>=printf("Leaderf! rg -e %s -t cpp -t java", expand("<cword>"))<CR>

" search word under cursor in cpp files, exclude the *.hpp files
noremap <Leader>c :<C-U><C-R>=printf("Leaderf! rg -e %s -t cpp -g !*.hpp", expand("<cword>"))<CR>
複製程式碼

參考:rg的glob語法

順便說一下,直接在vim命令列敲:Leaderf rg,就會有傳說中的"grep on the fly"的功能哦,同時支援fuzzy和regex兩種模式。

會不會支援ag等其他grep工具

不會。
首先,ripgrep已經足夠強大,基本不存在別的工具有而ripgrep沒有的功能。其次,ripgrep有編譯好的Windows、Linux和MacOS上的binary,可以在這些平臺上很容易安裝。再者,由於作者比較懶,不想再實現重複的功能。

相關文章