@cryptotaxi247 / kubo / commits / ced348366

feat: add zsh completions (#10040)

Co-authored-by: Henrique Dias <hacdias@gmail.com>

Amir Mohammad Fakhimi committed Aug 17, 2023 at 18:13 UTC ced348366c13c841c93ec5734732e5789cba4cbb
6 files changed +99 -2
.github/workflows/gotest.yml
+2
@@ -35,6 +35,8 @@ jobs:
35 go-version: 1.19.x
36 - name: Check out Kubo
37 uses: actions/checkout@v3
38 + - name: Install missing tools
39 + run: sudo apt update && sudo apt install -y zsh
40 - name: Restore Go cache
41 uses: protocol/cache-go-action@v1
42 with:
core/commands/commands.go
+28 -1
@@ -13,7 +13,7 @@ import (
13 "sort"
14 "strings"
15
16 - "github.com/ipfs/go-ipfs-cmds"
16 + cmds "github.com/ipfs/go-ipfs-cmds"
17 )
18
19 type commandEncoder struct {
@@ -169,6 +169,33 @@ To install the completions permanently, they can be moved to
169 return res.Emit(&buf)
170 },
171 },
172 + "zsh": {
173 + Helptext: cmds.HelpText{
174 + Tagline: "Generate zsh shell completions.",
175 + ShortDescription: "Generates command completions for the zsh shell.",
176 + LongDescription: `
177 +Generates command completions for the zsh shell.
178 +
179 +The simplest way to see it working is write the completions
180 +to a file and then source it:
181 +
182 + > ipfs commands completion zsh > ipfs-completion.zsh
183 + > source ./ipfs-completion.zsh
184 +
185 +To install the completions permanently, they can be moved to
186 +/etc/zsh/completions or sourced from your ~/.zshrc file.
187 +`,
188 + },
189 + NoRemote: true,
190 + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
191 + var buf bytes.Buffer
192 + if err := writeZshCompletions(root, &buf); err != nil {
193 + return err
194 + }
195 + res.SetLength(uint64(buf.Len()))
196 + return res.Emit(&buf)
197 + },
198 + },
199 "fish": {
200 Helptext: cmds.HelpText{
201 Tagline: "Generate fish shell completions.",
core/commands/commands_test.go
+2
@@ -25,6 +25,7 @@ func TestROCommands(t *testing.T) {
25 "/commands/completion",
26 "/commands/completion/bash",
27 "/commands/completion/fish",
28 + "/commands/completion/zsh",
29 "/dag",
30 "/dag/get",
31 "/dag/resolve",
@@ -102,6 +103,7 @@ func TestCommands(t *testing.T) {
103 "/commands/completion",
104 "/commands/completion/bash",
105 "/commands/completion/fish",
106 + "/commands/completion/zsh",
107 "/config",
108 "/config/edit",
109 "/config/profile",
core/commands/completion.go
+28 -1
@@ -83,7 +83,7 @@ func commandToCompletions(name string, fullName string, cmd *cmds.Command) *comp
83 return parsed
84 }
85
86 -var bashCompletionTemplate, fishCompletionTemplate *template.Template
86 +var bashCompletionTemplate, fishCompletionTemplate, zshCompletionTemplate *template.Template
87
88 func init() {
89 commandTemplate := template.Must(template.New("command").Parse(`
@@ -153,6 +153,28 @@ _ipfs() {
153 {{ template "command" . }}
154 }
155 complete -o nosort -o nospace -o default -F _ipfs ipfs
156 +`))
157 +
158 + zshCompletionTemplate = template.Must(commandTemplate.New("root").Parse(`#!bin/zsh
159 +autoload bashcompinit
160 +bashcompinit
161 +_ipfs_compgen() {
162 +local oldifs="$IFS"
163 +IFS=$'\n'
164 +while read -r line; do
165 + COMPREPLY+=("$line")
166 +done < <(compgen "$@")
167 +IFS="$oldifs"
168 +}
169 +
170 +_ipfs() {
171 +COMPREPLY=()
172 +local index=1
173 +local argidx=0
174 +local word="${COMP_WORDS[COMP_CWORD]}"
175 +{{ template "command" . }}
176 +}
177 +complete -o nosort -o nospace -o default -F _ipfs ipfs
178 `))
179
180 fishCommandTemplate := template.Must(template.New("command").Parse(`
@@ -221,3 +243,8 @@ func writeFishCompletions(cmd *cmds.Command, out io.Writer) error {
243 cmds := commandToCompletions("ipfs", "", cmd)
244 return fishCompletionTemplate.Execute(out, cmds)
245 }
246 +
247 +func writeZshCompletions(cmd *cmds.Command, out io.Writer) error {
248 + cmds := commandToCompletions("ipfs", "", cmd)
249 + return zshCompletionTemplate.Execute(out, cmds)
250 +}
docs/command-completion.md
+13
@@ -24,3 +24,16 @@ The simplest way to use the completions logic:
24
25 To install the completions permanently, they can be moved to
26 `/etc/fish/completions` or `~/.config/fish/completions` or sourced from your `~/.config/fish/config.fish` file.
27 +
28 +## ZSH
29 +
30 +The zsh shell is also supported:
31 +
32 +The simplest way to "eval" the completions logic:
33 +
34 +```bash
35 +> eval "$(ipfs commands completion zsh)"
36 +```
37 +
38 +To install the completions permanently, they can be moved to
39 +`/etc/bash_completion.d` or sourced from your `~/.zshrc` file.
test/cli/completion_test.go
+26
@@ -29,3 +29,29 @@ func TestBashCompletion(t *testing.T) {
29 assert.NoError(t, res.Err)
30 })
31 }
32 +
33 +func TestZshCompletion(t *testing.T) {
34 + t.Parallel()
35 + h := harness.NewT(t)
36 + node := h.NewNode()
37 +
38 + res := node.IPFS("commands", "completion", "zsh")
39 +
40 + length := len(res.Stdout.String())
41 + if length < 100 {
42 + t.Fatalf("expected a long Bash completion file, but got one of length %d", length)
43 + }
44 +
45 + t.Run("completion file can be loaded in bash", func(t *testing.T) {
46 + RequiresLinux(t)
47 +
48 + completionFile := h.WriteToTemp(res.Stdout.String())
49 + res = h.Runner.Run(harness.RunRequest{
50 + Path: "zsh",
51 + Args: []string{"-c", fmt.Sprintf("autoload -Uz compinit && compinit && source %s && echo -E $_comps[ipfs]", completionFile)},
52 + })
53 +
54 + assert.NoError(t, res.Err)
55 + assert.NotEmpty(t, res.Stdout.String())
56 + })
57 +}