From e527c57fdd3931127e31a434e55c1dac16eb20b4 Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Fri, 20 Jul 2018 15:48:18 +0200 Subject: add bash completion (only for "git-bug" for now ...) --- doc/bash_completion/git-bug | 547 ++++++++++++++++++++++++++++++++++++++++++++ doc/gen_bash_completion.go | 21 ++ 2 files changed, 568 insertions(+) create mode 100644 doc/bash_completion/git-bug create mode 100644 doc/gen_bash_completion.go (limited to 'doc') diff --git a/doc/bash_completion/git-bug b/doc/bash_completion/git-bug new file mode 100644 index 00000000..b357f4e0 --- /dev/null +++ b/doc/bash_completion/git-bug @@ -0,0 +1,547 @@ +# bash completion for git-bug -*- shell-script -*- + +__git-bug_debug() +{ + if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then + echo "$*" >> "${BASH_COMP_DEBUG_FILE}" + fi +} + +# Homebrew on Macs have version 1.3 of bash-completion which doesn't include +# _init_completion. This is a very minimal version of that function. +__git-bug_init_completion() +{ + COMPREPLY=() + _get_comp_words_by_ref "$@" cur prev words cword +} + +__git-bug_index_of_word() +{ + local w word=$1 + shift + index=0 + for w in "$@"; do + [[ $w = "$word" ]] && return + index=$((index+1)) + done + index=-1 +} + +__git-bug_contains_word() +{ + local w word=$1; shift + for w in "$@"; do + [[ $w = "$word" ]] && return + done + return 1 +} + +__git-bug_handle_reply() +{ + __git-bug_debug "${FUNCNAME[0]}" + case $cur in + -*) + if [[ $(type -t compopt) = "builtin" ]]; then + compopt -o nospace + fi + local allflags + if [ ${#must_have_one_flag[@]} -ne 0 ]; then + allflags=("${must_have_one_flag[@]}") + else + allflags=("${flags[*]} ${two_word_flags[*]}") + fi + COMPREPLY=( $(compgen -W "${allflags[*]}" -- "$cur") ) + if [[ $(type -t compopt) = "builtin" ]]; then + [[ "${COMPREPLY[0]}" == *= ]] || compopt +o nospace + fi + + # complete after --flag=abc + if [[ $cur == *=* ]]; then + if [[ $(type -t compopt) = "builtin" ]]; then + compopt +o nospace + fi + + local index flag + flag="${cur%=*}" + __git-bug_index_of_word "${flag}" "${flags_with_completion[@]}" + COMPREPLY=() + if [[ ${index} -ge 0 ]]; then + PREFIX="" + cur="${cur#*=}" + ${flags_completion[${index}]} + if [ -n "${ZSH_VERSION}" ]; then + # zsh completion needs --flag= prefix + eval "COMPREPLY=( \"\${COMPREPLY[@]/#/${flag}=}\" )" + fi + fi + fi + return 0; + ;; + esac + + # check if we are handling a flag with special work handling + local index + __git-bug_index_of_word "${prev}" "${flags_with_completion[@]}" + if [[ ${index} -ge 0 ]]; then + ${flags_completion[${index}]} + return + fi + + # we are parsing a flag and don't have a special handler, no completion + if [[ ${cur} != "${words[cword]}" ]]; then + return + fi + + local completions + completions=("${commands[@]}") + if [[ ${#must_have_one_noun[@]} -ne 0 ]]; then + completions=("${must_have_one_noun[@]}") + fi + if [[ ${#must_have_one_flag[@]} -ne 0 ]]; then + completions+=("${must_have_one_flag[@]}") + fi + COMPREPLY=( $(compgen -W "${completions[*]}" -- "$cur") ) + + if [[ ${#COMPREPLY[@]} -eq 0 && ${#noun_aliases[@]} -gt 0 && ${#must_have_one_noun[@]} -ne 0 ]]; then + COMPREPLY=( $(compgen -W "${noun_aliases[*]}" -- "$cur") ) + fi + + if [[ ${#COMPREPLY[@]} -eq 0 ]]; then + declare -F __custom_func >/dev/null && __custom_func + fi + + # available in bash-completion >= 2, not always present on macOS + if declare -F __ltrim_colon_completions >/dev/null; then + __ltrim_colon_completions "$cur" + fi + + # If there is only 1 completion and it is a flag with an = it will be completed + # but we don't want a space after the = + if [[ "${#COMPREPLY[@]}" -eq "1" ]] && [[ $(type -t compopt) = "builtin" ]] && [[ "${COMPREPLY[0]}" == --*= ]]; then + compopt -o nospace + fi +} + +# The arguments should be in the form "ext1|ext2|extn" +__git-bug_handle_filename_extension_flag() +{ + local ext="$1" + _filedir "@(${ext})" +} + +__git-bug_handle_subdirs_in_dir_flag() +{ + local dir="$1" + pushd "${dir}" >/dev/null 2>&1 && _filedir -d && popd >/dev/null 2>&1 +} + +__git-bug_handle_flag() +{ + __git-bug_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" + + # if a command required a flag, and we found it, unset must_have_one_flag() + local flagname=${words[c]} + local flagvalue + # if the word contained an = + if [[ ${words[c]} == *"="* ]]; then + flagvalue=${flagname#*=} # take in as flagvalue after the = + flagname=${flagname%=*} # strip everything after the = + flagname="${flagname}=" # but put the = back + fi + __git-bug_debug "${FUNCNAME[0]}: looking for ${flagname}" + if __git-bug_contains_word "${flagname}" "${must_have_one_flag[@]}"; then + must_have_one_flag=() + fi + + # if you set a flag which only applies to this command, don't show subcommands + if __git-bug_contains_word "${flagname}" "${local_nonpersistent_flags[@]}"; then + commands=() + fi + + # keep flag value with flagname as flaghash + # flaghash variable is an associative array which is only supported in bash > 3. + if [[ -z "${BASH_VERSION}" || "${BASH_VERSINFO[0]}" -gt 3 ]]; then + if [ -n "${flagvalue}" ] ; then + flaghash[${flagname}]=${flagvalue} + elif [ -n "${words[ $((c+1)) ]}" ] ; then + flaghash[${flagname}]=${words[ $((c+1)) ]} + else + flaghash[${flagname}]="true" # pad "true" for bool flag + fi + fi + + # skip the argument to a two word flag + if __git-bug_contains_word "${words[c]}" "${two_word_flags[@]}"; then + c=$((c+1)) + # if we are looking for a flags value, don't show commands + if [[ $c -eq $cword ]]; then + commands=() + fi + fi + + c=$((c+1)) + +} + +__git-bug_handle_noun() +{ + __git-bug_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" + + if __git-bug_contains_word "${words[c]}" "${must_have_one_noun[@]}"; then + must_have_one_noun=() + elif __git-bug_contains_word "${words[c]}" "${noun_aliases[@]}"; then + must_have_one_noun=() + fi + + nouns+=("${words[c]}") + c=$((c+1)) +} + +__git-bug_handle_command() +{ + __git-bug_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" + + local next_command + if [[ -n ${last_command} ]]; then + next_command="_${last_command}_${words[c]//:/__}" + else + if [[ $c -eq 0 ]]; then + next_command="_git-bug_root_command" + else + next_command="_${words[c]//:/__}" + fi + fi + c=$((c+1)) + __git-bug_debug "${FUNCNAME[0]}: looking for ${next_command}" + declare -F "$next_command" >/dev/null && $next_command +} + +__git-bug_handle_word() +{ + if [[ $c -ge $cword ]]; then + __git-bug_handle_reply + return + fi + __git-bug_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" + if [[ "${words[c]}" == -* ]]; then + __git-bug_handle_flag + elif __git-bug_contains_word "${words[c]}" "${commands[@]}"; then + __git-bug_handle_command + elif [[ $c -eq 0 ]]; then + __git-bug_handle_command + elif __git-bug_contains_word "${words[c]}" "${command_aliases[@]}"; then + # aliashash variable is an associative array which is only supported in bash > 3. + if [[ -z "${BASH_VERSION}" || "${BASH_VERSINFO[0]}" -gt 3 ]]; then + words[c]=${aliashash[${words[c]}]} + __git-bug_handle_command + else + __git-bug_handle_noun + fi + else + __git-bug_handle_noun + fi + __git-bug_handle_word +} + +_git-bug_close() +{ + last_command="git-bug_close" + + command_aliases=() + + commands=() + + flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + + + must_have_one_flag=() + must_have_one_noun=() + noun_aliases=() +} + +_git-bug_commands() +{ + last_command="git-bug_commands" + + command_aliases=() + + commands=() + + flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + + flags+=("--pretty") + flags+=("-p") + local_nonpersistent_flags+=("--pretty") + + must_have_one_flag=() + must_have_one_noun=() + noun_aliases=() +} + +_git-bug_comment() +{ + last_command="git-bug_comment" + + command_aliases=() + + commands=() + + flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + + flags+=("--file=") + two_word_flags+=("-F") + local_nonpersistent_flags+=("--file=") + flags+=("--message=") + two_word_flags+=("-m") + local_nonpersistent_flags+=("--message=") + + must_have_one_flag=() + must_have_one_noun=() + noun_aliases=() +} + +_git-bug_label() +{ + last_command="git-bug_label" + + command_aliases=() + + commands=() + + flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + + flags+=("--remove") + flags+=("-r") + local_nonpersistent_flags+=("--remove") + + must_have_one_flag=() + must_have_one_noun=() + noun_aliases=() +} + +_git-bug_ls() +{ + last_command="git-bug_ls" + + command_aliases=() + + commands=() + + flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + + + must_have_one_flag=() + must_have_one_noun=() + noun_aliases=() +} + +_git-bug_new() +{ + last_command="git-bug_new" + + command_aliases=() + + commands=() + + flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + + flags+=("--file=") + two_word_flags+=("-F") + local_nonpersistent_flags+=("--file=") + flags+=("--message=") + two_word_flags+=("-m") + local_nonpersistent_flags+=("--message=") + + must_have_one_flag=() + must_have_one_noun=() + noun_aliases=() +} + +_git-bug_open() +{ + last_command="git-bug_open" + + command_aliases=() + + commands=() + + flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + + + must_have_one_flag=() + must_have_one_noun=() + noun_aliases=() +} + +_git-bug_pull() +{ + last_command="git-bug_pull" + + command_aliases=() + + commands=() + + flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + + + must_have_one_flag=() + must_have_one_noun=() + noun_aliases=() +} + +_git-bug_push() +{ + last_command="git-bug_push" + + command_aliases=() + + commands=() + + flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + + + must_have_one_flag=() + must_have_one_noun=() + noun_aliases=() +} + +_git-bug_show() +{ + last_command="git-bug_show" + + command_aliases=() + + commands=() + + flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + + + must_have_one_flag=() + must_have_one_noun=() + noun_aliases=() +} + +_git-bug_webui() +{ + last_command="git-bug_webui" + + command_aliases=() + + commands=() + + flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + + + must_have_one_flag=() + must_have_one_noun=() + noun_aliases=() +} + +_git-bug_root_command() +{ + last_command="git-bug" + + command_aliases=() + + commands=() + commands+=("close") + commands+=("commands") + commands+=("comment") + commands+=("label") + commands+=("ls") + commands+=("new") + commands+=("open") + commands+=("pull") + commands+=("push") + commands+=("show") + commands+=("webui") + + flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + + + must_have_one_flag=() + must_have_one_noun=() + noun_aliases=() +} + +__start_git-bug() +{ + local cur prev words cword + declare -A flaghash 2>/dev/null || : + declare -A aliashash 2>/dev/null || : + if declare -F _init_completion >/dev/null 2>&1; then + _init_completion -s || return + else + __git-bug_init_completion -n "=" || return + fi + + local c=0 + local flags=() + local two_word_flags=() + local local_nonpersistent_flags=() + local flags_with_completion=() + local flags_completion=() + local commands=("git-bug") + local must_have_one_flag=() + local must_have_one_noun=() + local last_command + local nouns=() + + __git-bug_handle_word +} + +if [[ $(type -t compopt) = "builtin" ]]; then + complete -o default -F __start_git-bug git-bug +else + complete -o default -o nospace -F __start_git-bug git-bug +fi + +# ex: ts=4 sw=4 et filetype=sh diff --git a/doc/gen_bash_completion.go b/doc/gen_bash_completion.go new file mode 100644 index 00000000..df662a6c --- /dev/null +++ b/doc/gen_bash_completion.go @@ -0,0 +1,21 @@ +package main + +import ( + "fmt" + "github.com/MichaelMure/git-bug/commands" + "log" + "os" + "path" +) + +func main() { + cwd, _ := os.Getwd() + filepath := path.Join(cwd, "doc", "bash_completion", "git-bug") + + fmt.Println("Generating bash completion file ...") + + err := commands.RootCmd.GenBashCompletionFile(filepath) + if err != nil { + log.Fatal(err) + } +} -- cgit