How do I get bash completion to work with aliases?

34,514

Solution 1

As stated in the comments above,

complete -o default -o nospace -F _git_checkout gco

will no longer work. However, there's a __git_complete function in git-completion.bash which can be used to set up completion for aliases like so:

__git_complete gco _git_checkout

Solution 2

I ran into this problem as well and came up with this code snippet. This will automatically give you completion for all aliases. Run it after declaring all (or any) alias.

# wrap_alias takes three arguments:
# $1: The name of the alias
# $2: The command used in the alias
# $3: The arguments in the alias all in one string
# Generate a wrapper completion function (completer) for an alias
# based on the command and the given arguments, if there is a
# completer for the command, and set the wrapper as the completer for
# the alias.
function wrap_alias() {
  [[ "$#" == 3 ]] || return 1

  local alias_name="$1"
  local aliased_command="$2"
  local alias_arguments="$3"
  local num_alias_arguments=$(echo "$alias_arguments" | wc -w)

  # The completion currently being used for the aliased command.
  local completion=$(complete -p $aliased_command 2> /dev/null)

  # Only a completer based on a function can be wrapped so look for -F
  # in the current completion. This check will also catch commands
  # with no completer for which $completion will be empty.
  echo $completion | grep -q -- -F || return 0

  local namespace=alias_completion::

  # Extract the name of the completion function from a string that
  # looks like: something -F function_name something
  # First strip the beginning of the string up to the function name by
  # removing "* -F " from the front.
  local completion_function=${completion##* -F }
  # Then strip " *" from the end, leaving only the function name.
  completion_function=${completion_function%% *}

  # Try to prevent an infinite loop by not wrapping a function
  # generated by this function. This can happen when the user runs
  # this twice for an alias like ls='ls --color=auto' or alias l='ls'
  # and alias ls='l foo'
  [[ "${completion_function#$namespace}" != $completion_function ]] && return 0

  local wrapper_name="${namespace}${alias_name}"

  eval "
function ${wrapper_name}() {
  let COMP_CWORD+=$num_alias_arguments
  args=( \"${alias_arguments}\" )
  COMP_WORDS=( $aliased_command \${args[@]} \${COMP_WORDS[@]:1} )
  $completion_function
  }
"

  # To create the new completion we use the old one with two
  # replacements:
  # 1) Replace the function with the wrapper.
  local new_completion=${completion/-F * /-F $wrapper_name }
  # 2) Replace the command being completed with the alias.
  new_completion="${new_completion% *} $alias_name"

  eval "$new_completion"
}

# For each defined alias, extract the necessary elements and use them
# to call wrap_alias.
eval "$(alias -p | sed -e 's/alias \([^=][^=]*\)='\''\([^ ][^ ]*\) *\(.*\)'\''/wrap_alias \1 \2 '\''\3'\'' /')"

unset wrap_alias

Solution 3

Ideally I'd like autocompletion to just magically work for all my aliases. Is it possible?

Yes, it is possible with the complete-alias project (on Linux). Support for Mac is experimental but users have reported success.

Solution 4

In git-completion.bash there is a line:

complete -o default -o nospace -F _git git

Looking at that line (and the _git function) you can add this line to your .bash_profile:

complete -o default -o nospace -F _git_checkout gco

Solution 5

I have aliased g='git', and combined with my git aliases I type things like

$ g co <branchname>

The simpler fix for my specific use case was to add a single line to git-completion.

Right below this line:

__git_complete git _git

I added this line to handle my single 'g' alias:

__git_complete g _git
Share:
34,514

Related videos on Youtube

kch
Author by

kch

Updated on April 18, 2022

Comments

  • kch
    kch about 2 years

    Case in point:

    I'm a on mac with bash v3.2.17, I'm using git installed via macports with the bash_completion variant.

    When I type git checkout m<tab>. for example, I get it completed to master.

    However, I've got an alias to git checkout, gco. When I type gco m<tab>, I don't get the branch name autocompleted.

    Ideally I'd like autocompletion to just magically work for all my aliases. Is it possible? Failing that, I'd like to manually customize it for each alias. So, how do I go about either?

    • eighteyes
      eighteyes about 11 years
      complete -o default -o nospace -F doesn't work nowadays
    • Ciro Santilli OurBigBook.com
      Ciro Santilli OurBigBook.com over 10 years
      Questions with more upvotes than the top answer often imply great feature requests
    • dstarh
      dstarh over 10 years
      Another answer from superuser as someone pointed out to me that my question there was a dupe of this one. superuser.com/questions/436314/…
  • Mario F
    Mario F over 13 years
    the line let COMP_CWORD+=$num_alias_arguments did not work on Mac OS X for some reason. Replacing it with ((COMP_CWORD+=$num_alias_arguments)) fixed it though
  • Jo Liss
    Jo Liss over 13 years
    Wow, that's awesome -- thanks! wrap_alias chokes on double quotes in the alias definition, and I guess it doesn't make much sense for multi-command aliases (alias 'foo=bar; baz'), so I'm putting an extra | grep -v '[";|&]' after the alias -p. Also, it gets a bit slow for hundreds of alias definitions, but I'm happy to confirm that using echo instead of eval and piping the output into a cache file (which can then be eval'ed in one go) works fine and is super-fast.
  • Jo Liss
    Jo Liss over 13 years
    Another hint: wrap_alias requires the completions to be set up, so I had to move source /etc/bash_completion in front of the wrap_alias code.
  • Jo Liss
    Jo Liss about 13 years
    I think there needs to be \" around \${COMP_WORDS[@]:1} to make this work with blank spaces in arguments. (But I don't really know what I'm doing, so I'm not comfortable editing the answer at the moment.)
  • cmcginty
    cmcginty over 12 years
    some of the git* bash functions no longer work using this method
  • pforhan
    pforhan over 12 years
    ;This almost works -- I get a couple of errors, but the completion goes through. Anything else I can do? -bash: eval: line 28: unexpected EOF while looking for matching ''' -bash: eval: line 29: syntax error: unexpected end of file
  • irh
    irh over 12 years
    This worked for me on OS X 10.7.2 after changing the line let COMP_CWORD+=$num_alias_arguments to let \"COMP_CWORD+=$num_alias_arguments\".
  • Graham
    Graham over 12 years
    Did the following to wrap only specified aliases: function wrap_aliases { for cmdname in "$@"; do cmdname="$(alias $cmdname | sed 's/sudo //')"; eval "$(echo $cmdname | sed -e 's/alias ([^=][^=]*)='\''([^ ][^ ]*) *(.*)'\''/wrap_alias \1 \2 '\''\3'\'' /')"; done }
  • Michael Smith
    Michael Smith over 12 years
    Yes this used to work great until something changed in git_completion.bash... Now it works with the full command but not with the alias.
  • agentofuser
    agentofuser over 12 years
    OSX 10.7.2 here, didn't work with either @irh's or MarioFernandez's tips. Any ideas what could be wrong or how I can debug this?
  • idbrii
    idbrii over 11 years
    (I'm using Cygwin.) I couldn't find the file git-completion or that line in /etc/bash_completion.d/git, but I added complete -o default -o nospace -F _git g after my alias in .bash_aliases and it worked!
  • Julien Carsique
    Julien Carsique over 11 years
    I had to exclude the following aliases: [[ "_longopt" = $completion_function ]] && return 0
  • eighteyes
    eighteyes about 11 years
    See the end of this page for answers that work in modern git.
  • Ondrej Machulda
    Ondrej Machulda about 11 years
    If you use global alias "g" for git, you could also add __git_complete g __git_main to get code completition working on all git commands.
  • Larry
    Larry almost 11 years
    this works well - added this to my .bash_profile, and works fine with and without aliases so far: github.com/larrybotha/dotfiles/blob/master/…
  • Elijah Lynn
    Elijah Lynn almost 11 years
    ^^ For those new to git/shell/bash. The above comment refers to a global shell alias, not a native git alias.
  • Jürgen Paul
    Jürgen Paul over 10 years
    What if git was aliased to g and checkout is alised to co so I do g co <tab> but doesn't autocomplete as well.
  • benregn
    benregn over 10 years
    Where should I put this?
  • agentofuser
    agentofuser about 10 years
    Any way I can make this work with sudo? I added alias agi='sudo apt-get install' but agi+emac<TAB> doesn't do anything.
  • Hesky Fisher
    Hesky Fisher about 10 years
    @obvio171 You would need to have a completion function for sudo.
  • kpsfoo
    kpsfoo about 10 years
    Finally figured out how to do this properly! Step 1) Copy git-completion.bash from <your git install folder>/etc/bash-completion.d/ to ~/.git-completion.bash Step 2) add source ~/.git-completion.bash to your .bash_profile Step 3) Add __git_complete gco _git_checkout anywhere after the above line in your .bash_profile. Step 4) Reboot shell and enjoy your alias auto completion! :)
  • agentofuser
    agentofuser about 10 years
    @HeskyFisher could you give some more detail, please? Sorry, I'm not too experienced with bash.
  • Hesky Fisher
    Hesky Fisher about 10 years
    @obvio171 I suggest asking this as a stack overflow question.
  • dsrees
    dsrees almost 10 years
    @benregn I placed it directly below the source ~/.git_completion.sh in my ~/.bash_profile
  • jamadagni
    jamadagni over 9 years
    @obvio171: From Graham's reply: Add | sed 's/sudo //' before the sed -e 's/alias' and it should automatically bring in the completion function for the command after sudo.
  • jamadagni
    jamadagni over 9 years
    If anyone is having trouble with some aliases not getting the correct completion, please see my comment here
  • John Mellor
    John Mellor over 9 years
    See the updated version of this script at superuser.com/a/437508/102281 (for example, I added support for COMP_LINE and COMP_POINT which are required for some git completions).
  • Theodore Murdock
    Theodore Murdock about 9 years
    -1 This answer seems to assume that you'll have read and understood some older, incorrect answer (but which one?), and therefore know in what file and where in that file this line of text belongs. Try to only assume knowledge of things established in the question.
  • stempler
    stempler about 9 years
    There is a nice Gist that provides all information needed to set it up
  • Jan Van Bruggen
    Jan Van Bruggen over 8 years
    To check the number of leading underscores for commands other than __git_main and _git_checkout (such as _git_merge and _git_pull), search in the .git-completion.bash script. All of the common commands (the ones I alias) have only one leading underscore.
  • kub1x
    kub1x about 8 years
    Beware, that if you edit a file in /etc/bash-completion.d/ or newly in /usr/share/bash-completion/, you will lose your changes whenever that file gets updated using your package manager.
  • Tom Hale
    Tom Hale over 7 years
    Why is _xfunc git required?
  • kub1x
    kub1x over 7 years
    @TomHale I tried to improve the answer. Rather than doing source ~/.git-completion.sh I let _xfunc do it for me. It just feels nicer and cleaner to do it solely in ~/.bash_completion. Without the _xfunc (or the sourcing) the __git_complete function doesn't exist.
  • Tom Hale
    Tom Hale over 7 years
    No need for the ~/.bash_completion file - the _xfunc line works for me in .bashrc.
  • Alexej Magura
    Alexej Magura over 7 years
    What if the alias you want completion for has nothing to do with git; what if you don't have git installed on your system/server?
  • Bruno Bronosky
    Bruno Bronosky about 7 years
    I'm sorry, but non-threaded comments are the NOT proper place collaborate on code. At a minimum this should be a gist. But, since gists allow neither issues to be created or pull requests to be offered, this really should be an actual git repo. (even the link John Mellor shared is full comment coders)
  • Tom Hale
    Tom Hale about 7 years
    @pforhan I can see quoting issues above... the " quotes inside the function string should be quoted as \". This probably eats one of your ' quotes somewhere along the line.
  • artm
    artm almost 7 years
    thanks a lot, this is so much better than figuring out how every utility in the world implements bash completion.
  • wisbucky
    wisbucky almost 6 years
    This is the best solution for simple (one to one) aliases, like docker aliased to d. Although for the example in the question, git checkout aliased to gco is more complex.
  • 0xC0000022L
    0xC0000022L over 5 years
    Yikes, cool script, but it fails when it's a non-trivial alias which contains tokens such as (($UID)) ;) ... still, very cool. I'm using grep -v to get rid of the few offending aliases and added that to the pipe sequence right after alias -p ...
  • still_dreaming_1
    still_dreaming_1 over 5 years
    This is not an actual answer because it doesn't actually tell you what to do so much as just provide some information. Very incomplete.
  • ShellFish
    ShellFish about 4 years
    For people who are looking for the completion script: find / -name "git-completion.bash".
  • DerRockWolf
    DerRockWolf about 3 years
    The _completion_loader line is always required if the completion script is located in /usr/share/bash-completion/completions/. For backwards compatibility scripts that are located in /etc/bash_completion.d will still be loaded when bash_completion is being loaded. See: github bash-completion commit