better fish shell keybinds

Posted on:October 20, 2023

Table of contents

Open Table of contents

Intro

Wanting to extend the keybinding functions of the fish shell first came to me when I was to add use cases to my <C-k> and <C-j> keys. In my keybind functions I initially had <C-k> and <C-j> bound to the functions prevd-or-backward-word and nextd-or-forward-word. The behviour I provided to these functions was being able to navigate the completion-list/pager up and down as well as the default functions mentioned already. In this post, we will delve into how I did this first by reviewing these functions, and then working our way into more advanced interactive functions to bind keys to.

fish_user_key_bindings

Here, is where I’ll be placing each respective function I describe below. This function will need to be defined in the $fish_autoloaded_functions path, and will also need to be called in the ~/.config/fish/config.fish. In the future I might create an actual fisher plugin to make these features more accessible.

~/.config/fish/functions/fish_user_key_bindings.fish

function fish_user_key_bindings
    bind \cj 'down-or-nextd-or-forward-word'
    bind \ck 'up-or-prevd-or-backward-word'
    #...
end

~/.config/fish/config.fish

# ...

# call the function so that keybinds are bound when the shell process starts
fish_user_key_bindings
# ...

down-or-nextd-or-forward-word

This will move down the pager if it visible in the current command line. If the pager is not visible, then it handle the nextd-or-forward-word case.

function down-or-nextd-or-forward-word
    if not commandline --paging-mode; and not commandline --search-mode
        commandline -f nextd-or-forward-word
        return
    else
        commandline -f down-line
        return
    end
end

up-or-prevd-or-backward-word

This function will interactively move down through the pager if it has been toggle visible for the current command. If the pager is not visible, then it calls prevd-or-backward-word.

function up-or-prevd-or-backward-word -d "if in completion mode(pager), then move up, otherwise, prevd-or-forward-word"
    if not commandline --paging-mode; and not commandline --search-mode
        commandline -f prevd-or-backward-word
        return
    else
        commandline -f up-line
        return
    end
end

toggle-complete

This function will interactively handle opening and closing the pager to show completions for the current commandline. In my config I have the following function bound in fish_user_key_bindings as: bind -k nul toggle-auto-complete. On every keyboard I’ve interacted with, the nul keysequence is sent for Control+Space

function toggle-complete
    if commandline --paging-mode; or commandline --search-mode
        commandline -f suppress-autosuggestion
        commandline -f accept-autosuggestion

    else
        commandline -f complete
    end
end

More advanced usage

With the previously introduced functions, we see some nice functionality added to the fish-shell experience. The below functions are more advanced, and require some of the following tools:

Running external tools

For the commands: ranger, alacritty, and lazy-git I simply bind them to keys to launch new processes using them in the shell. Since these commands break the interactive prompt, we have to repaint the commandline after the sub-process exits. This is done by following the keybind with commandline -f repaint. Here’s a simple snippet showing possible bindings for these functions in the fish_user_key_bindings file.

bind \cR 'ranger; commandline -f repaint'
bind \cG 'lazygit; commandline -f repaint'
bind \cT 'alacritty --working-directory "$PWD" &;disown $last_pid; commandline -f repaint'

Using fzf with keybinds

Here I will outline two useful keybinds, I use regularly. First I will show fzf-history-or-copy-to-clipboard and next I will show fzf-search-or-toggle-complete.

fzf-history-or-copy-to-clipboard

If there is text in the current commandline, then this function yanks (places it in the system clipboard) & clears the command line. If the command line is empty it pipes the history list into fzf and allows you to select a previous command to copy to the clipboard.

function fzf-history-or-copy-to-clipboard -d "copy to clipboard from history or current cmd"
    set -l current (commandline -c); # to test this line run 'bind \cy "commandline -r (commandline -c)"'
    if test -z "$current"
        set current (__history_fzf);
    end
    commandline $current;and fish_clipboard_copy;
    commandline -r ""; and commandline -f repaint;
end

function __history_fzf
    set -l res (history | fzf --no-sort)
    echo $res;
end