Difference between revisions of "How to integrate fzf for fuzzy finding"

From Vifm Wiki
Jump to: navigation, search
(Commands)
(Examples: Replace system() with term(), this should make :redraw redundant)
(4 intermediate revisions by 3 users not shown)
Line 14: Line 14:
  
 
<source lang="vim">
 
<source lang="vim">
command! FZFlocate :set noquickview | :execute 'goto "'.system('locate $HOME | fzf --height 10 2>/dev/tty ').'"%IU' | redraw
+
command! FZFlocate :set noquickview | :execute 'goto' fnameescape(term('locate $HOME | fzf --height 10 2>/dev/tty'))
command! FZFfind :set noquickview | :execute 'goto "'.system('find | fzf --height 10 2>/dev/tty ').'"%IU' | redraw
+
command! FZFfind :set noquickview | :execute 'goto' fnameescape(term('find | fzf --height 10 2>/dev/tty'))
 
</source>
 
</source>
  
Line 22: Line 22:
 
=== Options ===
 
=== 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>system()</code> function to run our searching/listing <code>fzf</code> pipeline. To prevent visual artifacts in the terminal, we use the command macro <code>%IU</code> and a call to <code>:redraw</code> for extra safety.
+
<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 34: Line 34:
  
 
In this example I have chosen <kbd>CTRL-g</kbd> and <kbd>CTRL-f</kbd>.
 
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. This is a bit like the <code>vifm</code> <code>%s</code> split macro.
 +
 +
In the example below we use <code>fzf-tmux</code> with no other alterations. 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 | :execute 'goto ' fnameescape(term('locate $HOME | fzf-tmux --height 10 2>/dev/tty'))
 +
</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).
 +
 +
=== 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 ==
 
== Discussions ==

Revision as of 21:08, 21 March 2020

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

You need to have fzf installed. This method creates pipelines using the find and locate commands which are present in most Linux distributions.

Considerations

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 though the user's $HOME directory and the output from find for recursive traversal.

Examples

Commands

We can add the following two commands to vifmrc:

command! FZFlocate :set noquickview | :execute 'goto' fnameescape(term('locate $HOME | fzf --height 10 2>/dev/tty'))
command! FZFfind :set noquickview | :execute 'goto' fnameescape(term('find | fzf --height 10 2>/dev/tty'))

In both examples fzf will pop up on the bottom 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

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

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

fzf ships with the fzf-tmux wrapper script. This is a bit like the vifm %s split macro.

In the example below we use fzf-tmux with no other alterations. By taking advantage of tmux where it's available, we can enjoy a more graceful integration with fzf.

command! FZFlocate :set noquickview | :execute 'goto ' fnameescape(term('locate $HOME | fzf-tmux --height 10 2>/dev/tty'))

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 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).

Mappings

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

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

Here are some further links to discussion of fzf and the system() function: