how to re-use existing completion with recent bash-completion?

i use alias which adds some parameters to some program.

for example:

alias git.home='git --git-dir=$HOME/.git.home/'

complete -o bashdefault -o default -o nospace -F _git git.home 
  2>/dev/null || complete -o default -o nospace -F _git git.home

it works with bash-completion v.1:2.0-1 (“debian 7 wheezy”):
tab completes parameters and options for alias git.home as well as for “original” git.

but with bash-completion v.1:2.1-4 (“debian 8 jessie”) after typing git.home and pressing tab i get this error:

bash: completion: function `_git’ not found

how to re-use existing completion (for example for git) with recent versions of package bash-completion?


update

found partial solution: do source /usr/share/bash-completion/completions/git before complete ...

but i get another error – git.home log + space + tab results in:

fatal: Not a git repository (or any of the parent directories): .git
HEAD

updated

solution for alias git.home, which operates with ~/.git.home as repo.

partially based on this answer.

add to ~/.bashrc:

alias git.home='git --git-dir=$HOME/.git.home/'

_completion_loader git
eval "$(complete -p git | sed -r 's/(s)git$/1git.home/')"

eval "$(type __gitdir | 
  sed '1d;1,/if/s|if|if [[ "$COMP_LINE" == "git.home "* ]]; thennecho "$HOME/.git.home"nelif|')"

explanation of last eval (for other lines see answer):

we are patching the function __gitdir(), which returns a path to the git repo dir. from:

$ type __gitdir | head -n 4
__gitdir is a function
__gitdir () 
{ 
    if [ -z "${1-}" ]; then

to:

$ type __gitdir | head -n 7
__gitdir is a function
__gitdir () 
{ 
    if [[ "$COMP_LINE" == "git.home "* ]]; then
        echo "$HOME/.git.home";
    else
        if [ -z "${1-}" ]; then

i.e., if command starts with a "git.home ", function returns $HOME/.git.home.

Answered By: aleksandr barakin

how to re-use existing completion (for example for git) with recent versions of package bash-completion

Add the following lines to your ~/.bashrc:

_completion_loader git
eval $(complete -p git | perl -pe 's/(s)git$/$1git.home/')

Explanation:

bash-completion uses a dynamic loading of completions since 1.90. So, the git compspec unavailable before your typing git,Space,Tab.

Try:

bash
complete -p git # bash: complete: git: no completion specification
git<space><tab>
complete -p git # complete -o bashdefault -o default -o nospace -F __git_wrap__git_main git

_completion_loader git imitates git,Space,Tab (i.e. loads a compspec for git)

complete -p git | perl -pe 's/(s)git$/$1git.home/' builds a compspec for git.home (replaces git to git.home in a git‘s compspec)

eval execute the resulting compspec.

Type git.home,Space,pTab. You should see:

popt   pull   push

But git.home log,Space,Tab produces:

fatal: Not a git repository (or any of the parent directories): .git HEAD

This message is a result of the git completion bug. There is a fix in the upstream: completion: silence “fatal: Not a git repository” error

Looks like another question:)

_githome(){ 
   GIT_DIR=$HOME/.git.home
   complete -o default -o nospace -F _git git.home
 }

doesn’t work as expected. You set GIT_DIR for current session. Try with your solution:

bash
git clone git://git.debian.org/git/bash-completion/bash-completion.git ~/bash-completion
cd ~/bash-completion
git log<space><tab> # completion for current repo
git.home log<space><tab> # completion for GIT_DIR
git log<space><tab> # completion for GIT_DIR
Answered By: Evgeny Vereshchagin

This is a general answer that should work for any completion function. I’m concerned about the insecurity of the other answers assuming a single line output to be parsed by sed or perl and I can implement that without an external call.

# Usage: complete_as TARGET COMMAND [COMMAND...]
# Tab-complete one or more COMMANDs like TARGET
complete_as() {
  _completion_loader "$1"
  local completion="$(complete -p "$1")"
  completion="${completion%[[:space:]]$1}"
  shift
  eval "$completion $@"
}
complete_as git git.home

Just like the sed and perl code in the older answers, this replaces the last instance of the target command ($1) with the new command ($2), but I’m using parameter substitution, which views the whole content rather than running on each line of the output. This is important because the end of the output of the completion function sometimes has line breaks (as is the case with complete -W "$(_parse_help foo)" foo). Removing external calls also makes this much faster, which you may notice if you’re doing a lot of these.

You could similarly build such safeties (and use bash substitution) to fix the __gitdir function like aleksandr barakin’s answer does.

See also this zsh solution to the same question.

Answered By: Adam Katz
Categories: Answers Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.