This content originally appeared on DEV Community and was authored by Peter Benjamin
Table of Contents
Overview
FZF is a great command-line fuzzy-finder and there is fzf.vim plugin that integrates with Vim to provide features, like :Files to fuzzy search over files and :Rg to fuzzy search over text using ripgrep.
Recently, however, I have been experimenting with a plugin-free Vim setup and while :find is sufficient for some use-cases, I found myself quitting vim and running fzf to find deeply nested files in new or large projects.
There has to be a simple way to integrate a command-line program with a command-line editor, right?
The Setup
This setup is simple and it leverages a feature in vim called quickfix.
The flow is:
- Call
fzf - Format the output to make it compatible with
errorformat - Write the results to a temporary file
- Load the results into Vim's quickfix list with
:cfileor:cgetfile, so that we can navigate through the results with:cnext/:cpreviousor:copen - Clean up temporary file
Here is what the final vimscript looks like:
function! FZF() abort
let l:tempname = tempname()
" fzf | awk '{ print $1":1:0" }' > file
execute 'silent !fzf --multi ' . '| awk ''{ print $1":1:0" }'' > ' . fnameescape(l:tempname)
try
execute 'cfile ' . l:tempname
redraw!
finally
call delete(l:tempname)
endtry
endfunction
" :Files
command! -nargs=* Files call FZF()
" \ff
nnoremap <leader>ff :Files<cr>
A quick breakdown:
-
let l:tempname = tempname()- Generate a path to a temporary file and store it in a variable.
- See
:h tempname()
-
execute 'silent !fzf --multi ' . '| awk ''{ print $1":1:0" }'' > ' . fnameescape(l:tempname)- Call
fzfwith--multito allow for selecting multiple files - Pipe to
awkto append:1:0to fzf results to make themerrorformat-compatible. - Note: you can drop this
awkcommand if youset errorformat+=%fin your vimrc, but I found%fto capture a lot of false-positives from other programs' outputs and therefore:cnext/:cpreviousdon't function on these false-positive results. - Finally, direct the results into the temp file
- Call
-
execute 'cfile ' . l:tempname- Load results from temp file into quickfix list and jump to the 1st result.
- Note #1: you may use
:cgetfileto only load results into quickfix list without jumping to the 1st result. - Note #2: you may replace
:cfile/:cgetfilewith:lfile/:lgetfileto use location list instead of quickfix list. Location lists are window-specific, whereas quickfix lists are global. So if you prefer to have different set of results per vim window, then use:lfile/:lgetfile.
-
call delete(l:tempname)- Clean up by deleting the temp file
-
command! -nargs=* Files call FZF()- Invoke
FZF()function when we call:Filesin vim
- Invoke
-
nnoremap <leader>ff :Files<cr>- Normal-mode mapping so that we can trigger this flow with
<leader>ff
- Normal-mode mapping so that we can trigger this flow with
Bonus
While :set grepprg=rg\ --vimgrep is again sufficient for most of my use-cases, those who have used :Rg in fzf.vim will appreciate the interactive fuzzy grepping experience and the ability to preview results before opening files in vim.
Well, here is a similar experience with pure vim (obviously, fzf and rg binaries are still required):
function! RG(args) abort
let l:tempname = tempname()
let l:pattern = '.'
if len(a:args) > 0
let l:pattern = a:args
endif
" rg --vimgrep <pattern> | fzf -m > file
execute 'silent !rg --vimgrep ''' . l:pattern . ''' | fzf -m > ' . fnameescape(l:tempname)
try
execute 'cfile ' . l:tempname
redraw!
finally
call delete(l:tempname)
endtry
endfunction
" :Rg [pattern]
command! -nargs=* Rg call RG(<q-args>)
" \fs
nnoremap <leader>fs :Rg<cr>
This offers the same experience where:
-
:Rgwithout arguments will load all text into vim and allow users to interactively type and preview results before selecting files -
:Rg [pattern]will pre-filter results to just ones that match[pattern]before passing them tofzffor further fuzzy searching.
Caveat
In my testing, I found one major caveat that did not impact me too much, but it is still worth calling out here:
Executing shell commands in vim with bangs, like :!fzf, is not meant to be interactive (at least, not out of the box). This could be a problem in GVim/MacVim. The vim docs mention the following workaround:
On Unix the command normally runs in a non-interactive shell. If you want an interactive shell to be used (to use aliases) set 'shellcmdflag' to "-ic".
Setting shellcmdflag=-ic could incur a time penalty, depending on your shell startup/initialization times.
Summary
Vim is extremely versatile and customizable.
With some knowledge of vim concepts (e.g. quickfix, :cfile/:lfile) and a little bit of (vim & bash) scripting, you can achieve a richer experience and pleasant integrations to enhance your productivity in a way that suits you and your workflow.
I hope you enjoyed this post and I hope it inspires you to develop and share your productivity tips and tricks in vim.
Happy hacking!
This content originally appeared on DEV Community and was authored by Peter Benjamin
Peter Benjamin | Sciencx (2021-07-25T03:11:02+00:00) Interactive Fuzzy Finding in Vim without Plugins. Retrieved from https://www.scien.cx/2021/07/25/interactive-fuzzy-finding-in-vim-without-plugins/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.