Supercharging your terminal with fzf
I love using the terminal, it allows me to quickly access a large amount of tools to tackle any problem I might face. But I tell you what I do not like, unnecessary typing. Way too often I encounter the scenario where I need to go from one directory to another directory somewhere far away on my system.
# I would rather chew moist chalk, than waste time having to manually cd into this directory 9 times a day
cd some/directory/i/need/to/be/that/is/suffering/to/manually/type
Luckily there is a nifty little tool called fzf. Its a command line fuzzy finder. Here is a little demo of me using fzf to print a file using batcat:
We could use this to get the path to any file on the system. We could then use the rifle program from ranger to then open this file using the correct program for its mimetype:
Rifle decides that the .cpp
file is best opened using neovim.
We can also use fzf to cd to a directory:
cd "$(dirname "$(fzf)")"
And this works splendidly.
But we would not want to have to retype this command everytime we want to fzf into a directory or use rifle to open a file.
So lets define them as functions, in our .zshrc
or .bashrc
:
# Use rifle to open programs function z_fzf { rifle "$(fzf)" } # cd into any directory using fzf function z_fzfdir { cd "$(dirname "$(fzf)")" }
Now if we source our .zshrc we will have instant access to these functions in our terminal.
Except we can do better!
It would be nice if we could bind our z_fzfdir
and z_fzf
functions under Alt+S and Alt+Shift+S.
As you might know shells have keybinds.
These keybinds can actually be rebound using bindkey
in Zsh.
You can get a list of all bound keybinds by running bindkey
.
[hackerman@droid (0) ~] $> bindkey | head "^@" set-mark-command "^A" beginning-of-line "^B" backward-char "^D" delete-char-or-list "^E" end-of-line "^F" forward-char "^G" send-break "^H" backward-delete-char "^I" expand-or-complete "^J" accept-line
The left column displays the keychord a command is bound to. The right column displays the command bound to the keychord.
These commands are not always programs.
Most of them are Zsh line editor widgets.
You can get a list of these with zle -al
.
The keychords are defined by control characters.
We can use showkey -a
and then type our keychord to get the keychord we want.
[hackerman@droid (0) ~] $> showkey -a Press any keys - Ctrl-D will terminate this program ^[s 27 0033 0x1b 115 0163 0x73 ^[S 27 0033 0x1b 83 0123 0x53
We can then use bindkey to bind our functions to these keychords.
# The -s flag tells bindkey we want to bind to a string and not a zle command bindkey -s "^[s" z_fzfdir bindkey -s "^[S" z_fzf
The -s
option in this case tells bind key we are binding to a string, not a Zsh line editor command.
If we where to now press Alt+S right this would just insert the z_fzfdir
string under where our cursor is currently located.
This is not desired, ideally we would want to clear the line insert the z_fzfdir
string and instantly execute it.
We can do this by inserting keychords of other bound commands into our command.
# keychord and commands we will use: "^A" beginning-of-line "^K" kill-line "^M" accept-line
Now lets change our bindkey definitions.
bindkey -s "^A^K^[s^M" z_fzfdir bindkey -s "^A^K^[S^M" z_fzf
Now if we press Alt+S the line will be cleared and z_fzfdir
will be called.
This is functional and it works but it is not the correct way of doing it.
The correct way of doing it would be to define our z_fzfdir
and z_fzf
functions as a zle widget.
And then use bindkey
without the -s
option.
If you are interested in reading more about the Zsh line editor I recommend reading the blogpost sgeb.io. You could sidestep creating your own script and use a tool like zoxide. Which also has fzf intergration. I prefer keeping my dependencies for my shell to a minimum so I dont use zoxide.