Hero Image

How to enable working bash completions with fish shell

I often run into programs that lack the vision to use the proper fish shell, and often have to do without fancy completions. I found a VERY nice gist that allows one to do this successfully, and have it seamlessly appear in the fish shell.

Here are some prerequisites that are necessary;

  1. Bash completion has to be installed
  2. your completions should be in an approrpiate directory as referenced by the script. For arch this is /usr/share/bash-completion/completions
  3. You need to use the first part of the gist as the 'caller' to the script which does the magic of working nicely with the automatic replacement of internal fish completions with the bash ones.
  4. You can edit the alias in your ~/.config/fish/config.fish file to use the completions you want. I generally prefer to use the fish ones but for bash only, you just add the ones you are interested in.

Here is the gist link. I also include a local copy in case this disappears. Alas to get this to work with GRAV I put a backslash in front of the hash, since it seemed to think it was a shortcode. This is the hardest post I have ever done!


#!/bin/bash
# Author: Brian Beffa <brbsix@gmail.com>
# Original source: https://brbsix.github.io/2015/11/29/accessing-tab-completion-programmatically-in-bash/
# License: LGPLv3 (http://www.gnu.org/licenses/lgpl-3.0.txt)

get_completions(){
    local completion COMP_CWORD COMP_LINE COMP_POINT COMP_WORDS COMPREPLY=()

    # load bash-completion if necessary
    declare -F _completion_loader &>/dev/null || {
        if [ -f /etc/bash_completion ]; then
            source /etc/bash_completion
        elif [ -f /usr/share/bash-completion/bash_completion ]; then
            source /usr/share/bash-completion/bash_completion
        fi
    }

    COMP_LINE=$*
    # Be sure to remove the backslash here, as grav got mad at the unquoted hash)
    COMP_POINT=${\#COMP_LINE}

    eval set -- "$@"

    COMP_WORDS=("$@")

    # add '' to COMP_WORDS if the last character of the command line is a space
    [[ ${COMP_LINE[@]: -1} = ' ' ]] && COMP_WORDS+=('')

    # index of the last word be sure to remove the backslash in front of comp_words
    COMP_CWORD=$(( ${\#COMP_WORDS[@]} - 1 ))

    # load completion
    _completion_loader "$1"

    # detect completion
    completion=$(complete -p "$1" 2>/dev/null | awk '{print $(NF-1)}')

    # ensure completion was detected
    [[ -n $completion ]] || return 1

    # execute completion function
    "$completion"

    # print completions to stdout be sure to remove the backslash in front of compreply
    for ((i = 0; i < ${\#COMPREPLY[@]}; i++)); do
        echo "${COMPREPLY[$i]%%*( )}"
    done
}

This gets you the basic get-bash-completions.sh script. I put this in ~/bin and make it executable. The second part is to add the following to your ~/.config/fish/config.fish line:

# You can add this to your ~/.config/fish/config.fish

function __fish_complete_bash
    set cmd (commandline -cp)
    bash -c "source get-bash-completions.sh; get_completions '$cmd'"
end

# Set the tool to use bash completions
complete -xc git -a '(__fish_complete_bash)'
complete -xc gdbus -a '(__fish_complete_bash)'
complete -xc java -a '(__fish_complete_bash)'
complete -xc distrobox -a '(__fish_complete_bash)'
# load bash-completion if necessary
    declare -F _completion_loader &>/dev/null || {
        if [ -f /etc/bash_completion ]; then
            source /etc/bash_completion

This (in my case) that the commands for git, gdbus, java and distrobox are going to use the default bash completions instead of the fish ones. Remember that fish has its own completions which are often superior to the bash ones, but in this case I get the nice completions for distrobox!