diff options
-rw-r--r-- | api/graphql/graphql_test.go | 2 | ||||
-rw-r--r-- | api/graphql/handler.go | 6 | ||||
-rw-r--r-- | api/graphql/tracer.go | 67 | ||||
-rw-r--r-- | commands/webui.go | 26 | ||||
-rw-r--r-- | doc/man/git-bug-webui.1 | 4 | ||||
-rw-r--r-- | doc/md/git-bug_webui.md | 1 | ||||
-rw-r--r-- | go.mod | 5 | ||||
-rw-r--r-- | go.sum | 7 | ||||
-rw-r--r-- | misc/completion/bash/git-bug | 177 | ||||
-rw-r--r-- | misc/completion/fish/git-bug | 3 | ||||
-rw-r--r-- | misc/completion/powershell/git-bug | 5 | ||||
-rw-r--r-- | misc/completion/zsh/git-bug | 32 |
12 files changed, 247 insertions, 88 deletions
diff --git a/api/graphql/graphql_test.go b/api/graphql/graphql_test.go index 350e489a..2ddfb314 100644 --- a/api/graphql/graphql_test.go +++ b/api/graphql/graphql_test.go @@ -22,7 +22,7 @@ func TestQueries(t *testing.T) { _, err := mrc.RegisterDefaultRepository(repo) require.NoError(t, err) - handler := NewHandler(mrc) + handler := NewHandler(mrc, nil) c := client.New(handler) diff --git a/api/graphql/handler.go b/api/graphql/handler.go index 00141f01..1d30bf72 100644 --- a/api/graphql/handler.go +++ b/api/graphql/handler.go @@ -20,11 +20,15 @@ type Handler struct { io.Closer } -func NewHandler(mrc *cache.MultiRepoCache) Handler { +func NewHandler(mrc *cache.MultiRepoCache, errorOut io.Writer) Handler { rootResolver := resolvers.NewRootResolver(mrc) config := graph.Config{Resolvers: rootResolver} h := handler.NewDefaultServer(graph.NewExecutableSchema(config)) + if errorOut != nil { + h.Use(&Tracer{Out: errorOut}) + } + return Handler{ Handler: h, Closer: rootResolver, diff --git a/api/graphql/tracer.go b/api/graphql/tracer.go new file mode 100644 index 00000000..11448a3a --- /dev/null +++ b/api/graphql/tracer.go @@ -0,0 +1,67 @@ +package graphql + +import ( + "context" + "encoding/json" + "fmt" + "io" + "strings" + + "github.com/99designs/gqlgen/graphql" + + "github.com/MichaelMure/git-bug/util/colors" +) + +// adapted from https://github.com/99designs/gqlgen/blob/master/graphql/handler/debug/tracer.go + +type Tracer struct { + Out io.Writer +} + +var _ interface { + graphql.HandlerExtension + graphql.ResponseInterceptor +} = &Tracer{} + +func (a Tracer) ExtensionName() string { + return "error tracer" +} + +func (a *Tracer) Validate(schema graphql.ExecutableSchema) error { + return nil +} + +func stringify(value interface{}) string { + valueJson, err := json.MarshalIndent(value, " ", " ") + if err == nil { + return string(valueJson) + } + + return fmt.Sprint(value) +} + +func (a Tracer) InterceptResponse(ctx context.Context, next graphql.ResponseHandler) *graphql.Response { + resp := next(ctx) + + if len(resp.Errors) == 0 { + return resp + } + + rctx := graphql.GetOperationContext(ctx) + + _, _ = fmt.Fprintln(a.Out, "GraphQL Request {") + for _, line := range strings.Split(rctx.RawQuery, "\n") { + _, _ = fmt.Fprintln(a.Out, " ", colors.Cyan(line)) + } + for name, value := range rctx.Variables { + _, _ = fmt.Fprintf(a.Out, " var %s = %s\n", name, colors.Yellow(stringify(value))) + } + + _, _ = fmt.Fprintln(a.Out, " resp:", colors.Green(stringify(resp))) + for _, err := range resp.Errors { + _, _ = fmt.Fprintln(a.Out, " error:", colors.Bold(err.Path.String()+":"), colors.Red(err.Message)) + } + _, _ = fmt.Fprintln(a.Out, "}") + _, _ = fmt.Fprintln(a.Out) + return resp +} diff --git a/commands/webui.go b/commands/webui.go index 406485c3..758a153b 100644 --- a/commands/webui.go +++ b/commands/webui.go @@ -3,6 +3,7 @@ package commands import ( "context" "fmt" + "io" "log" "net" "net/http" @@ -30,12 +31,13 @@ import ( const webUIOpenConfigKey = "git-bug.webui.open" type webUIOptions struct { - host string - port int - open bool - noOpen bool - readOnly bool - query string + host string + port int + open bool + noOpen bool + readOnly bool + logErrors bool + query string } func newWebUICommand() *cobra.Command { @@ -52,7 +54,7 @@ Available git config: `, PreRunE: loadRepo(env), RunE: func(cmd *cobra.Command, args []string) error { - return runWebUI(env, options, args) + return runWebUI(env, options) }, } @@ -64,12 +66,13 @@ Available git config: flags.BoolVar(&options.noOpen, "no-open", false, "Prevent the automatic opening of the web UI in the default browser") flags.IntVarP(&options.port, "port", "p", 0, "Port to listen to (default to random available port)") flags.BoolVar(&options.readOnly, "read-only", false, "Whether to run the web UI in read-only mode") + flags.BoolVar(&options.logErrors, "log-errors", false, "Whether to log errors") flags.StringVarP(&options.query, "query", "q", "", "The query to open in the web UI bug list") return cmd } -func runWebUI(env *Env, opts webUIOptions, args []string) error { +func runWebUI(env *Env, opts webUIOptions) error { if opts.port == 0 { var err error opts.port, err = freeport.GetFreePort() @@ -106,7 +109,12 @@ func runWebUI(env *Env, opts webUIOptions, args []string) error { return err } - graphqlHandler := graphql.NewHandler(mrc) + var errOut io.Writer + if opts.logErrors { + errOut = env.err + } + + graphqlHandler := graphql.NewHandler(mrc, errOut) // Routes router.Path("/playground").Handler(playground.Handler("git-bug", "/graphql")) diff --git a/doc/man/git-bug-webui.1 b/doc/man/git-bug-webui.1 index ae6c7837..0fcfeac2 100644 --- a/doc/man/git-bug-webui.1 +++ b/doc/man/git-bug-webui.1 @@ -42,6 +42,10 @@ Available git config: Whether to run the web UI in read-only mode .PP +\fB--log-errors\fP[=false] + Whether to log errors + +.PP \fB-q\fP, \fB--query\fP="" The query to open in the web UI bug list diff --git a/doc/md/git-bug_webui.md b/doc/md/git-bug_webui.md index 2d6be920..b1c54d3b 100644 --- a/doc/md/git-bug_webui.md +++ b/doc/md/git-bug_webui.md @@ -22,6 +22,7 @@ git-bug webui [flags] --no-open Prevent the automatic opening of the web UI in the default browser -p, --port int Port to listen to (default to random available port) --read-only Whether to run the web UI in read-only mode + --log-errors Whether to log errors -q, --query string The query to open in the web UI bug list -h, --help help for webui ``` @@ -26,7 +26,7 @@ require ( github.com/spf13/cobra v1.5.0 github.com/stretchr/testify v1.8.0 github.com/vektah/gqlparser/v2 v2.4.6 - github.com/xanzy/go-gitlab v0.72.0 + github.com/xanzy/go-gitlab v0.73.1 golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838 golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c golang.org/x/sync v0.0.0-20210220032951-036812b2e83c @@ -94,9 +94,12 @@ require ( github.com/xanzy/ssh-agent v0.3.0 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect go.etcd.io/bbolt v1.3.5 // indirect + golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect golang.org/x/net v0.0.0-20220805013720-a33c5aa5df48 // indirect golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect + golang.org/x/tools v0.1.10 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect @@ -276,8 +276,8 @@ github.com/vektah/gqlparser/v2 v2.4.6 h1:Yjzp66g6oVq93Jihbi0qhGnf/6zIWjcm8H6gA27 github.com/vektah/gqlparser/v2 v2.4.6/go.mod h1:flJWIR04IMQPGz+BXLrORkrARBxv/rtyIAFvd/MceW0= github.com/willf/bitset v1.1.10 h1:NotGKqX0KwQ72NUzqrjZq5ipPNDQex9lo3WpaS8L2sc= github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= -github.com/xanzy/go-gitlab v0.72.0 h1:/9BQTftUE7GRK/RO1eeWxG1cOE+tjwBrvRdpkeSOq6w= -github.com/xanzy/go-gitlab v0.72.0/go.mod h1:d/a0vswScO7Agg1CZNz15Ic6SSvBG9vfw8egL99t4kA= +github.com/xanzy/go-gitlab v0.73.1 h1:UMagqUZLJdjss1SovIC+kJCH4k2AZWXl58gJd38Y/hI= +github.com/xanzy/go-gitlab v0.73.1/go.mod h1:d/a0vswScO7Agg1CZNz15Ic6SSvBG9vfw8egL99t4kA= github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI= github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= @@ -296,6 +296,7 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838 h1:71vQrMauZZhcTVK6KdYM+rklehEEwb3E+ZhaE5jrPrE= golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -352,10 +353,12 @@ golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= diff --git a/misc/completion/bash/git-bug b/misc/completion/bash/git-bug index 92b90d3c..2bca660b 100644 --- a/misc/completion/bash/git-bug +++ b/misc/completion/bash/git-bug @@ -56,7 +56,7 @@ __git-bug_get_completion_results() { directive=0 fi __git-bug_debug "The completion directive is: ${directive}" - __git-bug_debug "The completions are: ${out[*]}" + __git-bug_debug "The completions are: ${out}" } __git-bug_process_completion_results() { @@ -89,13 +89,18 @@ __git-bug_process_completion_results() { fi fi + # Separate activeHelp from normal completions + local completions=() + local activeHelp=() + __git-bug_extract_activeHelp + if [ $((directive & shellCompDirectiveFilterFileExt)) -ne 0 ]; then # File extension filtering local fullFilter filter filteringCmd - # Do not use quotes around the $out variable or else newline + # Do not use quotes around the $completions variable or else newline # characters will be kept. - for filter in ${out[*]}; do + for filter in ${completions[*]}; do fullFilter+="$filter|" done @@ -107,7 +112,7 @@ __git-bug_process_completion_results() { # Use printf to strip any trailing newline local subdir - subdir=$(printf "%s" "${out[0]}") + subdir=$(printf "%s" "${completions[0]}") if [ -n "$subdir" ]; then __git-bug_debug "Listing directories in $subdir" pushd "$subdir" >/dev/null 2>&1 && _filedir -d && popd >/dev/null 2>&1 || return @@ -121,6 +126,43 @@ __git-bug_process_completion_results() { __git-bug_handle_special_char "$cur" : __git-bug_handle_special_char "$cur" = + + # Print the activeHelp statements before we finish + if [ ${#activeHelp} -ne 0 ]; then + printf "\n"; + printf "%s\n" "${activeHelp[@]}" + printf "\n" + + # The prompt format is only available from bash 4.4. + # We test if it is available before using it. + if (x=${PS1@P}) 2> /dev/null; then + printf "%s" "${PS1@P}${COMP_LINE[@]}" + else + # Can't print the prompt. Just print the + # text the user had typed, it is workable enough. + printf "%s" "${COMP_LINE[@]}" + fi + fi +} + +# Separate activeHelp lines from real completions. +# Fills the $activeHelp and $completions arrays. +__git-bug_extract_activeHelp() { + local activeHelpMarker="_activeHelp_ " + local endIndex=${#activeHelpMarker} + + while IFS='' read -r comp; do + if [ "${comp:0:endIndex}" = "$activeHelpMarker" ]; then + comp=${comp:endIndex} + __git-bug_debug "ActiveHelp found: $comp" + if [ -n "$comp" ]; then + activeHelp+=("$comp") + fi + else + # Not an activeHelp line but a normal completion + completions+=("$comp") + fi + done < <(printf "%s\n" "${out}") } __git-bug_handle_completion_types() { @@ -132,17 +174,16 @@ __git-bug_handle_completion_types() { # If the user requested inserting one completion at a time, or all # completions at once on the command-line we must remove the descriptions. # https://github.com/spf13/cobra/issues/1508 - local tab comp - tab=$(printf '\t') + local tab=$'\t' comp while IFS='' read -r comp; do + [[ -z $comp ]] && continue # Strip any description comp=${comp%%$tab*} # Only consider the completions that match - comp=$(compgen -W "$comp" -- "$cur") - if [ -n "$comp" ]; then + if [[ $comp == "$cur"* ]]; then COMPREPLY+=("$comp") fi - done < <(printf "%s\n" "${out[@]}") + done < <(printf "%s\n" "${completions[@]}") ;; *) @@ -153,44 +194,37 @@ __git-bug_handle_completion_types() { } __git-bug_handle_standard_completion_case() { - local tab comp - tab=$(printf '\t') + local tab=$'\t' comp + + # Short circuit to optimize if we don't have descriptions + if [[ "${completions[*]}" != *$tab* ]]; then + IFS=$'\n' read -ra COMPREPLY -d '' < <(compgen -W "${completions[*]}" -- "$cur") + return 0 + fi local longest=0 + local compline # Look for the longest completion so that we can format things nicely - while IFS='' read -r comp; do + while IFS='' read -r compline; do + [[ -z $compline ]] && continue # Strip any description before checking the length - comp=${comp%%$tab*} + comp=${compline%%$tab*} # Only consider the completions that match - comp=$(compgen -W "$comp" -- "$cur") + [[ $comp == "$cur"* ]] || continue + COMPREPLY+=("$compline") if ((${#comp}>longest)); then longest=${#comp} fi - done < <(printf "%s\n" "${out[@]}") - - local completions=() - while IFS='' read -r comp; do - if [ -z "$comp" ]; then - continue - fi - - __git-bug_debug "Original comp: $comp" - comp="$(__git-bug_format_comp_descriptions "$comp" "$longest")" - __git-bug_debug "Final comp: $comp" - completions+=("$comp") - done < <(printf "%s\n" "${out[@]}") - - while IFS='' read -r comp; do - COMPREPLY+=("$comp") - done < <(compgen -W "${completions[*]}" -- "$cur") + done < <(printf "%s\n" "${completions[@]}") # If there is a single completion left, remove the description text if [ ${#COMPREPLY[*]} -eq 1 ]; then __git-bug_debug "COMPREPLY[0]: ${COMPREPLY[0]}" - comp="${COMPREPLY[0]%% *}" + comp="${COMPREPLY[0]%%$tab*}" __git-bug_debug "Removed description from single completion, which is now: ${comp}" - COMPREPLY=() - COMPREPLY+=("$comp") + COMPREPLY[0]=$comp + else # Format the descriptions + __git-bug_format_comp_descriptions $longest fi } @@ -209,45 +243,48 @@ __git-bug_handle_special_char() __git-bug_format_comp_descriptions() { - local tab - tab=$(printf '\t') - local comp="$1" - local longest=$2 - - # Properly format the description string which follows a tab character if there is one - if [[ "$comp" == *$tab* ]]; then - desc=${comp#*$tab} - comp=${comp%%$tab*} - - # $COLUMNS stores the current shell width. - # Remove an extra 4 because we add 2 spaces and 2 parentheses. - maxdesclength=$(( COLUMNS - longest - 4 )) - - # Make sure we can fit a description of at least 8 characters - # if we are to align the descriptions. - if [[ $maxdesclength -gt 8 ]]; then - # Add the proper number of spaces to align the descriptions - for ((i = ${#comp} ; i < longest ; i++)); do - comp+=" " - done - else - # Don't pad the descriptions so we can fit more text after the completion - maxdesclength=$(( COLUMNS - ${#comp} - 4 )) - fi + local tab=$'\t' + local comp desc maxdesclength + local longest=$1 + + local i ci + for ci in ${!COMPREPLY[*]}; do + comp=${COMPREPLY[ci]} + # Properly format the description string which follows a tab character if there is one + if [[ "$comp" == *$tab* ]]; then + __git-bug_debug "Original comp: $comp" + desc=${comp#*$tab} + comp=${comp%%$tab*} + + # $COLUMNS stores the current shell width. + # Remove an extra 4 because we add 2 spaces and 2 parentheses. + maxdesclength=$(( COLUMNS - longest - 4 )) + + # Make sure we can fit a description of at least 8 characters + # if we are to align the descriptions. + if [[ $maxdesclength -gt 8 ]]; then + # Add the proper number of spaces to align the descriptions + for ((i = ${#comp} ; i < longest ; i++)); do + comp+=" " + done + else + # Don't pad the descriptions so we can fit more text after the completion + maxdesclength=$(( COLUMNS - ${#comp} - 4 )) + fi - # If there is enough space for any description text, - # truncate the descriptions that are too long for the shell width - if [ $maxdesclength -gt 0 ]; then - if [ ${#desc} -gt $maxdesclength ]; then - desc=${desc:0:$(( maxdesclength - 1 ))} - desc+="…" + # If there is enough space for any description text, + # truncate the descriptions that are too long for the shell width + if [ $maxdesclength -gt 0 ]; then + if [ ${#desc} -gt $maxdesclength ]; then + desc=${desc:0:$(( maxdesclength - 1 ))} + desc+="…" + fi + comp+=" ($desc)" fi - comp+=" ($desc)" + COMPREPLY[ci]=$comp + __git-bug_debug "Final comp: $comp" fi - fi - - # Must use printf to escape all special characters - printf "%q" "${comp}" + done } __start_git-bug() diff --git a/misc/completion/fish/git-bug b/misc/completion/fish/git-bug index a3c2d008..c5ab1b42 100644 --- a/misc/completion/fish/git-bug +++ b/misc/completion/fish/git-bug @@ -18,7 +18,8 @@ function __git_bug_perform_completion __git_bug_debug "args: $args" __git_bug_debug "last arg: $lastArg" - set -l requestComp "$args[1] __complete $args[2..-1] $lastArg" + # Disable ActiveHelp which is not supported for fish shell + set -l requestComp "GIT_BUG_ACTIVE_HELP=0 $args[1] __complete $args[2..-1] $lastArg" __git_bug_debug "Calling $requestComp" set -l results (eval $requestComp 2> /dev/null) diff --git a/misc/completion/powershell/git-bug b/misc/completion/powershell/git-bug index b8280eea..1b3e99d0 100644 --- a/misc/completion/powershell/git-bug +++ b/misc/completion/powershell/git-bug @@ -44,6 +44,7 @@ Register-ArgumentCompleter -CommandName 'git-bug' -ScriptBlock { # Prepare the command to request completions for the program. # Split the command at the first space to separate the program and arguments. $Program,$Arguments = $Command.Split(" ",2) + $RequestComp="$Program __completeNoDesc $Arguments" __git-bug_debug "RequestComp: $RequestComp" @@ -73,11 +74,13 @@ Register-ArgumentCompleter -CommandName 'git-bug' -ScriptBlock { } __git-bug_debug "Calling $RequestComp" + # First disable ActiveHelp which is not supported for Powershell + $env:GIT_BUG_ACTIVE_HELP=0 + #call the command store the output in $out and redirect stderr and stdout to null # $Out is an array contains each line per element Invoke-Expression -OutVariable out "$RequestComp" 2>&1 | Out-Null - # get directive from last line [int]$Directive = $Out[-1].TrimStart(':') if ($Directive -eq "") { diff --git a/misc/completion/zsh/git-bug b/misc/completion/zsh/git-bug index e7cbe9a9..3ddfddb8 100644 --- a/misc/completion/zsh/git-bug +++ b/misc/completion/zsh/git-bug @@ -1,4 +1,4 @@ -#compdef _git-bug git-bug +#compdef git-bug # zsh completion for git-bug -*- shell-script -*- @@ -86,7 +86,24 @@ _git-bug() return fi + local activeHelpMarker="_activeHelp_ " + local endIndex=${#activeHelpMarker} + local startIndex=$((${#activeHelpMarker}+1)) + local hasActiveHelp=0 while IFS='\n' read -r comp; do + # Check if this is an activeHelp statement (i.e., prefixed with $activeHelpMarker) + if [ "${comp[1,$endIndex]}" = "$activeHelpMarker" ];then + __git-bug_debug "ActiveHelp found: $comp" + comp="${comp[$startIndex,-1]}" + if [ -n "$comp" ]; then + compadd -x "${comp}" + __git-bug_debug "ActiveHelp will need delimiter" + hasActiveHelp=1 + fi + + continue + fi + if [ -n "$comp" ]; then # If requested, completions are returned with a description. # The description is preceded by a TAB character. @@ -94,7 +111,7 @@ _git-bug() # We first need to escape any : as part of the completion itself. comp=${comp//:/\\:} - local tab=$(printf '\t') + local tab="$(printf '\t')" comp=${comp//$tab/:} __git-bug_debug "Adding completion: ${comp}" @@ -103,6 +120,17 @@ _git-bug() fi done < <(printf "%s\n" "${out[@]}") + # Add a delimiter after the activeHelp statements, but only if: + # - there are completions following the activeHelp statements, or + # - file completion will be performed (so there will be choices after the activeHelp) + if [ $hasActiveHelp -eq 1 ]; then + if [ ${#completions} -ne 0 ] || [ $((directive & shellCompDirectiveNoFileComp)) -eq 0 ]; then + __git-bug_debug "Adding activeHelp delimiter" + compadd -x "--" + hasActiveHelp=0 + fi + fi + if [ $((directive & shellCompDirectiveNoSpace)) -ne 0 ]; then __git-bug_debug "Activating nospace." noSpace="-S ''" |