Skip to content

Commit a93cfe1

Browse files
author
Deepak kudi
committed
Fix bash completion for colon word breaks
1 parent ad460ea commit a93cfe1

2 files changed

Lines changed: 94 additions & 0 deletions

File tree

bash_completionsV2.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,41 @@ __%[1]s_handle_activeHelp() {
233233
fi
234234
}
235235
236+
__%[1]s_rejoin_colon_wordbreak()
237+
{
238+
if [[ "$COMP_WORDBREAKS" != *:* || "$cur" == *:* ]]; then
239+
return
240+
fi
241+
242+
local rawWord
243+
rawWord=${COMP_LINE:0:COMP_POINT}
244+
rawWord=${rawWord##*[[:space:]]}
245+
if [[ "$rawWord" != *:* ]]; then
246+
return
247+
fi
248+
249+
local i
250+
for ((i=cword; i>0; i--)); do
251+
if [[ "${words[i]}" == ":" ]]; then
252+
words[i-1]="${words[i-1]}:${cur}"
253+
words=("${words[@]:0:i}")
254+
cur="${words[i-1]}"
255+
cword=$((i - 1))
256+
__%[1]s_debug "Rejoined colon wordbreak: ${cur}"
257+
return
258+
fi
259+
260+
if [[ "${words[i]}" == *: ]]; then
261+
words[i]="${words[i]}${cur}"
262+
words=("${words[@]:0:$((i + 1))}")
263+
cur="${words[i]}"
264+
cword=$i
265+
__%[1]s_debug "Rejoined colon-suffixed word: ${cur}"
266+
return
267+
fi
268+
done
269+
}
270+
236271
__%[1]s_reprint_commandLine() {
237272
# The prompt format is only available from bash 4.4.
238273
# We test if it is available before using it.
@@ -448,6 +483,8 @@ __start_%[1]s()
448483
words=("${words[@]:0:$cword+1}")
449484
__%[1]s_debug "Truncated words[*]: ${words[*]},"
450485
486+
__%[1]s_rejoin_colon_wordbreak
487+
451488
local out directive
452489
__%[1]s_get_completion_results
453490
__%[1]s_process_completion_results

bash_completionsV2_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ package cobra
1717
import (
1818
"bytes"
1919
"fmt"
20+
"os"
21+
"os/exec"
22+
"strings"
2023
"testing"
2124
)
2225

@@ -31,3 +34,57 @@ func TestBashCompletionV2WithActiveHelp(t *testing.T) {
3134
activeHelpVar := activeHelpEnvVar(c.Name())
3235
checkOmit(t, output, fmt.Sprintf("%s=0", activeHelpVar))
3336
}
37+
38+
func TestBashCompletionV2RejoinsColonWordbreak(t *testing.T) {
39+
bash, err := exec.LookPath("bash")
40+
if err != nil {
41+
t.Skip("bash is not available")
42+
}
43+
44+
c := &Command{Use: "c", Run: emptyRun}
45+
46+
buf := new(bytes.Buffer)
47+
assertNoErr(t, c.GenBashCompletionV2(buf, false))
48+
49+
tempDir := t.TempDir()
50+
completionFile := tempDir + "/completion.bash"
51+
requestFile := tempDir + "/request.log"
52+
assertNoErr(t, os.WriteFile(completionFile, buf.Bytes(), 0o600))
53+
54+
script := fmt.Sprintf(`
55+
set -euo pipefail
56+
source "$COMPLETION_FILE"
57+
_init_completion() {
58+
cur="/"
59+
prev=":"
60+
words=(c ls local : /)
61+
cword=4
62+
}
63+
c() {
64+
printf '%%s\n' "$@" > "$REQUEST_FILE"
65+
printf ':%d\n'
66+
}
67+
COMP_TYPE=9
68+
COMP_LINE='c ls local:/'
69+
COMP_POINT=${#COMP_LINE}
70+
__start_c
71+
`, ShellCompDirectiveNoFileComp)
72+
73+
cmd := exec.Command(bash, "-c", script)
74+
cmd.Env = append(os.Environ(),
75+
"COMPLETION_FILE="+completionFile,
76+
"REQUEST_FILE="+requestFile,
77+
)
78+
output, err := cmd.CombinedOutput()
79+
if err != nil {
80+
t.Fatalf("bash completion failed: %v\n%s", err, output)
81+
}
82+
83+
request, err := os.ReadFile(requestFile)
84+
assertNoErr(t, err)
85+
86+
expected := "__completeNoDesc\nls\nlocal:/"
87+
if got := strings.TrimSpace(string(request)); got != expected {
88+
t.Fatalf("expected request:\n%s\ngot:\n%s", expected, got)
89+
}
90+
}

0 commit comments

Comments
 (0)