Difference between revisions of "How to integrate fzf for fuzzy finding"
m (Corrected lang spelling) |
(→Commands: Update wording: https://github.com/vifm/vifm/issues/963#issuecomment-1830457216) |
||
(19 intermediate revisions by 8 users not shown) | |||
Line 1: | Line 1: | ||
− | <code>vifm</code> | + | <code>vifm</code> already has powerful search features such as <code>:grep</code> and <code>:locate</code>. However, it is possible to further expand its capabilities with fun and powerful programs such as <code>fzf</code> "a general-purpose command-line fuzzy finder." |
== Prerequisites == | == Prerequisites == | ||
Line 5: | Line 5: | ||
== Considerations == | == Considerations == | ||
− | <code>fzf</code> is a powerful and quick interactive filter in addition to working as a file searching utility by default. This method focuses on piping the output from <code>locate $HOME</code> for quick traversal | + | <code>fzf</code> is a powerful and quick interactive filter in addition to working as a file searching utility by default. This method focuses on piping the output from <code>locate $HOME</code> for quick traversal over the user's <code>$HOME</code> directory and the output from <code>find</code> for recursive traversal. |
== Examples == | == Examples == | ||
Line 11: | Line 11: | ||
=== Commands === | === Commands === | ||
− | We can add the following two commands to <code> | + | We can add the following two commands to <code>vifmrc</code>: |
<source lang="vim"> | <source lang="vim"> | ||
− | command! FZFlocate : | + | command! FZFlocate : set noquickview |
− | command! FZFfind :execute 'goto " | + | \| let $FZF_PICK = term('locate $HOME | fzf --height 10 2>/dev/tty') |
+ | \| if $FZF_PICK != '' | ||
+ | \| execute 'goto' fnameescape($FZF_PICK) | ||
+ | \| endif | ||
+ | command! FZFfind : set noquickview | ||
+ | \| let $FZF_PICK = term('find | fzf --height 10 2>/dev/tty') | ||
+ | \| if $FZF_PICK != '' | ||
+ | \| execute 'goto' fnameescape($FZF_PICK) | ||
+ | \| endif | ||
+ | |||
+ | " variation that automatically enters directories | ||
+ | command! FZFlocate : set noquickview | ||
+ | \| let $FZF_PICK = term('locate $HOME | fzf --height 10 2>/dev/tty') | ||
+ | \| if $FZF_PICK != '' | ||
+ | \| execute system('[ -f "$FZF_PICK" ] && echo goto || echo cd') fnameescape($FZF_PICK) | ||
+ | \| endif | ||
</source> | </source> | ||
− | In both examples <code>fzf</code> will | + | In both examples <code>fzf</code> will show up and allow us to filter the output of the feeder command. To prevent artifacts, <code>vifm</code>'s previews are switched off. Once our selection is made, <code>fzf</code> will close and <code>vifm</code> will advance the active pane to the directory of the selected file with the cursor highlighting it. |
+ | |||
+ | === Options === | ||
+ | |||
+ | <code>vifm</code> will run its built-in <code>:goto</code>, however other commands like <code>:cd</code> should work if an appropriate list is piped to <code>fzf</code>. As this is an example of process substitution (using the output of a command or commands as an argument for another command), we need to use the <code>term()</code> function to run our searching/listing <code>fzf</code> pipeline. | ||
=== Mappings === | === Mappings === | ||
Line 29: | Line 48: | ||
</source> | </source> | ||
− | In this example I have chosen < | + | In this example I have chosen <kbd>CTRL-g</kbd> and <kbd>CTRL-f</kbd>. |
+ | |||
+ | === Tmux === | ||
+ | |||
+ | <code>fzf</code> ships with the <code>fzf-tmux</code> wrapper script. There are two display option: | ||
+ | * <code>split pane</code> - bit like the <code>vifm</code> <code>%s</code> split macro | ||
+ | * <code>popup window</code> - requires <code>tmux</code> 3.2 or above | ||
+ | |||
+ | In the example below we use <code>fzf-tmux</code> with popup window of 80% size. By taking advantage of <code>tmux</code> where it's available, we can enjoy a more graceful integration with <code>fzf</code>. | ||
+ | <source lang="vim"> | ||
+ | command! FZFlocate : set noquickview | ||
+ | \| let $FZF_PICK = system('locate $HOME | fzf-tmux -p 80%% 2>/dev/tty') | ||
+ | \| if $FZF_PICK != '' | ||
+ | \| execute 'goto' fnameescape($FZF_PICK) | ||
+ | \| endif | ||
+ | </source> | ||
+ | |||
+ | == Windows Examples == | ||
+ | |||
+ | Similar results may be achieved on Windows through PowerShell and (optionally) a database-based command line search tool such as Everything. | ||
+ | |||
+ | === Commands === | ||
+ | |||
+ | We can add the following command to <code>vifmrc</code>: | ||
+ | |||
+ | <source lang="vim"> | ||
+ | command! Find :execute 'goto' fnameescape(term(expand('powershell find.ps1 %a | fzf'))) | ||
+ | </source> | ||
+ | |||
+ | Where the script, find.ps1, located within %Path% (such as in the vifm directory), contains: | ||
+ | |||
+ | <source lang="powershell"> | ||
+ | param([String]$searchStr) | ||
+ | |||
+ | if($searchStr){ | ||
+ | es $searchStr | ForEach { $_.Replace('\','/') } | ||
+ | }else{ | ||
+ | Get-ChildItem -Recurse | ForEach { $_.FullName.Replace('\','/') } | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | This will search the local directory and child directories using PowerShell if no search criteria is supplied to :Find, else it will search the entire system for the supplied search criteria using the Everything command line interface tool, es (note that convert_forward_slash_to_backslash must be preset within Everything.ini and either forward or double backslashes should be used where appropriate in any search criteria). | ||
+ | |||
+ | ==== A faster alternative that uses sed ==== | ||
+ | <source lang="vim"> | ||
+ | command! Find :execute 'goto' fnameescape(term('fzf | sed.exe -b -e "s/\\/\//g"')) | ||
+ | </source> | ||
+ | |||
+ | This approach has two advantages it uses the <code>fzf</code> built-in directory scanning which is much faster than the <code>powershell</code> <code>Get-ChildItem</code> cmdlet (especially if the <code>powershell</code> startup time is taken into account) secondly it only performs the <code>\</code> to <code>/</code> replacement for the result returned by <code>fzf</code> which is a much smaller set of paths. A windows version of [https://www.gnu.org/software/sed/ GNU sed] can be easily installed using [https://chocolatey.org/packages/sed this] [https://chocolatey.org/ chocolatey] [https://chocolatey.org/packages/sed package]. | ||
+ | |||
+ | === Mappings === | ||
+ | |||
+ | We can then add the following mappings to run these commands from normal mode: | ||
+ | |||
+ | <source lang="vim"> | ||
+ | nnoremap gtg :Find<space> | ||
+ | nnoremap gtl :Find<cr> | ||
+ | </source> | ||
+ | |||
+ | In this example <kbd>gtg</kbd> (goto global) and <kbd>gtl</kbd> (goto local) are chosen as helpful mnemonics and to avoid conflicts with preexisting commands. | ||
+ | |||
+ | === Notes === | ||
+ | |||
+ | It's possible for these examples to cause display bugs in vifm when used in conjunction with some other background shell commands, for which the solution given in the Linux example doesn't work. If this occurs, :!cls should be used, which may optionally be appended to any problematic mappings, e.g. | ||
+ | |||
+ | <source lang="vim"> | ||
+ | nnoremap go :execute '!dummy.cmd &' | :execute '!cls'<cr> | ||
+ | </source> | ||
+ | |||
+ | == Discussions == | ||
+ | |||
+ | Here are some further links to discussion of <code>fzf</code> and the <code>system()</code> function: | ||
+ | * [https://github.com/vifm/vifm/issues/279 Using fzf with vifm — Github] | ||
+ | * [https://q2a.vifm.info/264/using-cd-with-stdout-from-external-command?show=265#a265 Using :cd with stdout from external command — vifm Q&A] | ||
+ | |||
+ | [[Category:HOWTO]] |
Latest revision as of 18:35, 28 November 2023
vifm
already has powerful search features such as :grep
and :locate
. However, it is possible to further expand its capabilities with fun and powerful programs such as fzf
"a general-purpose command-line fuzzy finder."
Prerequisites[edit]
You need to have fzf installed. This method creates pipelines using the find
and locate
commands which are present in most Linux distributions.
Considerations[edit]
fzf
is a powerful and quick interactive filter in addition to working as a file searching utility by default. This method focuses on piping the output from locate $HOME
for quick traversal over the user's $HOME
directory and the output from find
for recursive traversal.
Examples[edit]
Commands[edit]
We can add the following two commands to vifmrc
:
command! FZFlocate : set noquickview
\| let $FZF_PICK = term('locate $HOME | fzf --height 10 2>/dev/tty')
\| if $FZF_PICK != ''
\| execute 'goto' fnameescape($FZF_PICK)
\| endif
command! FZFfind : set noquickview
\| let $FZF_PICK = term('find | fzf --height 10 2>/dev/tty')
\| if $FZF_PICK != ''
\| execute 'goto' fnameescape($FZF_PICK)
\| endif
" variation that automatically enters directories
command! FZFlocate : set noquickview
\| let $FZF_PICK = term('locate $HOME | fzf --height 10 2>/dev/tty')
\| if $FZF_PICK != ''
\| execute system('[ -f "$FZF_PICK" ] && echo goto || echo cd') fnameescape($FZF_PICK)
\| endif
In both examples fzf
will show up and allow us to filter the output of the feeder command. To prevent artifacts, vifm
's previews are switched off. Once our selection is made, fzf
will close and vifm
will advance the active pane to the directory of the selected file with the cursor highlighting it.
Options[edit]
vifm
will run its built-in :goto
, however other commands like :cd
should work if an appropriate list is piped to fzf
. As this is an example of process substitution (using the output of a command or commands as an argument for another command), we need to use the term()
function to run our searching/listing fzf
pipeline.
Mappings[edit]
We can then add the following mappings to run these commands from normal mode:
nnoremap <c-g> :FZFlocate<cr>
nnoremap <c-f> :FZFfind<cr>
In this example I have chosen CTRL-g and CTRL-f.
Tmux[edit]
fzf
ships with the fzf-tmux
wrapper script. There are two display option:
split pane
- bit like thevifm
%s
split macropopup window
- requirestmux
3.2 or above
In the example below we use fzf-tmux
with popup window of 80% size. By taking advantage of tmux
where it's available, we can enjoy a more graceful integration with fzf
.
command! FZFlocate : set noquickview
\| let $FZF_PICK = system('locate $HOME | fzf-tmux -p 80%% 2>/dev/tty')
\| if $FZF_PICK != ''
\| execute 'goto' fnameescape($FZF_PICK)
\| endif
Windows Examples[edit]
Similar results may be achieved on Windows through PowerShell and (optionally) a database-based command line search tool such as Everything.
Commands[edit]
We can add the following command to vifmrc
:
command! Find :execute 'goto' fnameescape(term(expand('powershell find.ps1 %a | fzf')))
Where the script, find.ps1, located within %Path% (such as in the vifm directory), contains:
param([String]$searchStr)
if($searchStr){
es $searchStr | ForEach { $_.Replace('\','/') }
}else{
Get-ChildItem -Recurse | ForEach { $_.FullName.Replace('\','/') }
}
This will search the local directory and child directories using PowerShell if no search criteria is supplied to :Find, else it will search the entire system for the supplied search criteria using the Everything command line interface tool, es (note that convert_forward_slash_to_backslash must be preset within Everything.ini and either forward or double backslashes should be used where appropriate in any search criteria).
A faster alternative that uses sed[edit]
command! Find :execute 'goto' fnameescape(term('fzf | sed.exe -b -e "s/\\/\//g"'))
This approach has two advantages it uses the fzf
built-in directory scanning which is much faster than the powershell
Get-ChildItem
cmdlet (especially if the powershell
startup time is taken into account) secondly it only performs the \
to /
replacement for the result returned by fzf
which is a much smaller set of paths. A windows version of GNU sed can be easily installed using this chocolatey package.
Mappings[edit]
We can then add the following mappings to run these commands from normal mode:
nnoremap gtg :Find<space>
nnoremap gtl :Find<cr>
In this example gtg (goto global) and gtl (goto local) are chosen as helpful mnemonics and to avoid conflicts with preexisting commands.
Notes[edit]
It's possible for these examples to cause display bugs in vifm when used in conjunction with some other background shell commands, for which the solution given in the Linux example doesn't work. If this occurs, :!cls should be used, which may optionally be appended to any problematic mappings, e.g.
nnoremap go :execute '!dummy.cmd &' | :execute '!cls'<cr>
Discussions[edit]
Here are some further links to discussion of fzf
and the system()
function: