Skip to content

Commit 1f61673

Browse files
committed
Zsh completion V2 using Go completion
Signed-off-by: Marc Khouzam <[email protected]>
1 parent 99fa66f commit 1f61673

File tree

1 file changed

+146
-0
lines changed

1 file changed

+146
-0
lines changed

zsh_completions_v2.go

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package cobra
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"io"
7+
"os"
8+
)
9+
10+
func genZshCompV2(buf *bytes.Buffer, name string, includeDesc bool) {
11+
compCmd := CompRequestCmd
12+
if !includeDesc {
13+
compCmd = CompNoDescRequestCmd
14+
}
15+
buf.WriteString(fmt.Sprintf(`#compdef __%[1]s %[1]s
16+
17+
# zsh completion for %-36[1]s -*- shell-script -*-
18+
19+
__%[1]s_debug()
20+
{
21+
local file="$BASH_COMP_DEBUG_FILE"
22+
if [[ -n ${file} ]]; then
23+
echo "$*" >> "${file}"
24+
fi
25+
}
26+
27+
_%[1]s()
28+
{
29+
local lastParam lastChar flagPrefix requestComp out directive compCount comp lastComp
30+
local -a completions
31+
32+
__%[1]s_debug "\n========= starting completion logic =========="
33+
__%[1]s_debug "CURRENT: ${CURRENT}, words[*]: ${words[*]}"
34+
35+
lastParam=${words[-1]}
36+
lastChar=${lastParam[-1]}
37+
__%[1]s_debug "lastParam: ${lastParam}, lastChar: ${lastChar}"
38+
39+
# For zsh, when completing a flag with an = (e.g., %[1]s -n=<TAB>)
40+
# completions must be prefixed with the flag
41+
setopt local_options BASH_REMATCH
42+
if [[ "${lastParam}" =~ '-.*=' ]]; then
43+
# We are dealing with a flag with an =
44+
flagPrefix=${BASH_REMATCH}
45+
fi
46+
47+
# Prepare the command to obtain completions
48+
requestComp="${words[1]} %[2]s ${words[2,-1]}"
49+
if [ "${lastChar}" = "" ]; then
50+
# If the last parameter is complete (there is a space following it)
51+
# We add an extra empty parameter so we can indicate this to the go completion code.
52+
__%[1]s_debug "Adding extra empty parameter"
53+
requestComp="${requestComp} \"\""
54+
fi
55+
56+
__%[1]s_debug "About to call: eval ${requestComp}"
57+
58+
# Use eval to handle any environment variables and such
59+
out=$(eval ${requestComp} 2>/dev/null)
60+
__%[1]s_debug "completion output: ${out}"
61+
62+
# Extract the directive integer following a : as the last line
63+
if [ "${out[-2]}" = : ]; then
64+
directive=${out[-1]}
65+
# Remove the directive (that means the last 3 chars as we include the : and the newline)
66+
out=${out[1,-4]}
67+
else
68+
# There is not directive specified. Leave $out as is.
69+
__%[1]s_debug "No directive found. Setting do default"
70+
directive=0
71+
fi
72+
73+
__%[1]s_debug "directive: ${directive}"
74+
__%[1]s_debug "completions: ${out}"
75+
__%[1]s_debug "flagPrefix: ${flagPrefix}"
76+
77+
if [ $((directive & %[3]d)) -ne 0 ]; then
78+
__%[1]s_debug "Completion received error. Ignoring completions."
79+
else
80+
compCount=0
81+
while IFS='\n' read -r comp; do
82+
if [ -n "$comp" ]; then
83+
((compCount++))
84+
if [ -n "$flagPrefix" ]; then
85+
# We use compadd here so that we can hide the flagPrefix from the list
86+
# of choices. We can use compadd because there is no description in this case.
87+
__%[1]s_debug "Calling: compadd -p ${flagPrefix} ${comp}"
88+
compadd -p ${flagPrefix} ${comp}
89+
else
90+
# If requested, completions are returned with a description.
91+
# The description is preceded by a TAB character.
92+
# For zsh's _describe, we need to use a : instead of a TAB.
93+
# We first need to escape any : as part of the completion itself.
94+
comp=${comp//:/\\:}
95+
96+
local tab=$(printf '\t')
97+
comp=${comp//$tab/:}
98+
99+
__%[1]s_debug "Adding completion: ${comp}"
100+
completions+=${comp}
101+
fi
102+
lastComp=$comp
103+
fi
104+
done < <(printf "%%s\n" "${out[@]}")
105+
106+
if [ ${compCount} -eq 0 ]; then
107+
if [ $((directive & %[5]d)) -ne 0 ]; then
108+
__%[1]s_debug "deactivating file completion"
109+
else
110+
# Perform file completion
111+
__%[1]s_debug "activating file completion"
112+
_arguments '*:filename:_files'
113+
fi
114+
elif [ $((directive & %[4]d)) -ne 0 ] && [ ${compCount} -eq 1 ]; then
115+
__%[1]s_debug "Activating nospace."
116+
# We can use compadd here as there is no description when
117+
# there is only one completion.
118+
compadd -S '' "${lastComp}"
119+
else
120+
_describe "completions" completions
121+
fi
122+
fi
123+
}
124+
125+
compdef _%[1]s %[1]s
126+
`, name, compCmd, BashCompDirectiveError, BashCompDirectiveNoSpace, BashCompDirectiveNoFileComp))
127+
}
128+
129+
// GenZshCompletionV2 generates the zsh completion V2 file and writes to the passed writer.
130+
func (c *Command) GenZshCompletionV2(w io.Writer, includeDesc bool) error {
131+
buf := new(bytes.Buffer)
132+
genZshCompV2(buf, c.Name(), includeDesc)
133+
_, err := buf.WriteTo(w)
134+
return err
135+
}
136+
137+
// GenZshCompletionFileV2 generates the zsh completion V2 file.
138+
func (c *Command) GenZshCompletionFileV2(filename string, includeDesc bool) error {
139+
outFile, err := os.Create(filename)
140+
if err != nil {
141+
return err
142+
}
143+
defer outFile.Close()
144+
145+
return c.GenZshCompletionV2(outFile, includeDesc)
146+
}

0 commit comments

Comments
 (0)