views:

835

answers:

5

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?

+7  A: 

In git-compeltion.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
Chris Lloyd
Great Tip, thank you !!
Casey
+3  A: 

You could also try using Git aliases. For example, in my ~/.gitconfig file, I have a section that looks like this:

[alias]
        co = checkout

So you could type git co m<TAB>, and that should expand to git co master, which is the git checkout command.

mipadi
yea i do that already, but i want to type even less.
kch
+1  A: 

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
balshetzer
A: 

The wrap_alias() function rise another problem:

$ touch xp1 xp2 && mkdir aa

$ cp xp* <tab>

When <tab> press, the complete goes wrong.

qq274980
A: 

This forum page shows a solution.

Put these lines into yur .bashrc or .bash_profile:

# Author.: Ole J
# Date...: 23.03.2008
# License: Whatever

# Wraps a completion function
# make-completion-wrapper <actual completion function> <name of new func.>
#                         <command name> <list supplied arguments>
# eg.
#   alias agi='apt-get install'
#   make-completion-wrapper _apt_get _apt_get_install apt-get install
# defines a function called _apt_get_install (that's $2) that will complete
# the 'agi' alias. (complete -F _apt_get_install agi)
#
function make-completion-wrapper () {
    local function_name="$2"
    local arg_count=$(($#-3))
    local comp_function_name="$1"
    shift 2
    local function="
function $function_name {
    ((COMP_CWORD+=$arg_count))
    COMP_WORDS=( "$@" \${COMP_WORDS[@]:1} )
    "$comp_function_name"
    return 0
}"
    eval "$function"
}

# and now the commands that are specific to this SO question

alias gco='git checkout'

# we create a _git_checkout_mine function that will do the completion for "gco"
# using the completion function "_git"
make-completion-wrapper _git _git_checkout_mine git checkout

# we tell bash to actually use _git_checkout_mine to complete "gco"
complete -o bashdefault -o default -o nospace -F _git_checkout_mine gco

This solution is similar to balshetzer's script, but only this one actually works for me. (balshetzer's script had problems with some of my aliases.)

hcs42