Git completion for alias as if for Git itself
Asked Answered
S

1

11

Background

I have successfully configured Bash completion for various Git aliases. For example:

$ git config alias.subject
!git --no-pager show --quiet --pretty='%s'
$ function _git_subject() { _git_show; }
$ git subject my<TAB>
$ git subject my-branch

Challenge

However, I have a Git alias that I don't know how to set up Bash completion for. The problem is that I want the alias to complete as if for the top-level Git command itself. The alias is this:

$ git config alias.alias
alias = !"f() { if [[ \"$#\" != 1 ]]; then >&2 echo \"Usage: git alias COMMAND\"; return 1; fi; git config alias.\"$1\"; }; f"
# Example
$ git alias s
status

I have tried using _git, __git_main, and __git_wrap__git_main, but none of them work (I think it leads to an infinite loop since it never returns after I press tab).

Is there a way to add completion for a Git alias that completes as if it was the top-level Git command? Or specifically how to have completion for this alias?

Tried but doesn't work

function _git_alias() { _git; }
function _git_alias() { __git_main; }
function _git_alias() { __git_wrap__git_main; }

Desired behavior

$ git alias su<TAB>
subject     submodule
$ git alias sub

Alternatively, if there's an easy way to complete for only aliases that would be cool, too. I would like to know how to complete as if for the top-level Git command just for curiosity as well, though.

Sherrard answered 31/3, 2021 at 15:24 Comment(0)
S
1

I was finally able to create a working solution with a bit of hackery around the "magic" Bash completion variables. I changed these variables to "pretend" we were completing the given command as given to git itself.

If anybody has any suggestions to simplify this I would totally be open to suggestions.

# This is complex because we want to delegate to the completion for Git
# itself without ending up with an infinite loop (which happens if you try
# to just delegate to _git).
_git_alias() {
    if [[ "$COMP_CWORD" -lt 2 ]]; then
        return
    fi
    local old_comp_line_length new_comp_line_length
    COMP_WORDS=(git "${COMP_WORDS[@]:2}")
    ((COMP_CWORD -= 1))
    old_comp_line_length=${#COMP_LINE}
    if [[ "$COMP_LINE" =~ ^[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+(.*)$ ]]; then
        COMP_LINE="git ${BASH_REMATCH[1]}"
    fi
    new_comp_line_length=${#COMP_LINE}
    (( COMP_POINT += new_comp_line_length - old_comp_line_length ))

    _git "$@"

    # git alias blah
    #            ^
    # 01234567890123
    # 0         1
    # point:  11
    # length: 13
    #
    # git blah
    #      ^
    # 01234567
    # point:  5
    # length: 7
    #
    # point = point - (old length) + (new length)
    # point = 11 - 13 + 7
    # point = -2 + 7
    # point = 5
}
Sherrard answered 26/5, 2021 at 20:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.