mirror of
https://github.com/ipfs/kubo.git
synced 2026-02-21 10:27:46 +08:00
feat: Add command line completion for fish
This commit is contained in:
parent
a9cbdef023
commit
2fd4e197a0
@ -160,7 +160,7 @@ jobs:
|
||||
tar xfz go1.19.1.linux-amd64.tar.gz
|
||||
echo "export PATH=$(pwd)/go/bin:\$PATH" >> ~/.bashrc
|
||||
- run: go version
|
||||
- run: sudo apt install socat net-tools
|
||||
- run: sudo apt install socat net-tools fish
|
||||
- checkout
|
||||
|
||||
- run:
|
||||
|
||||
@ -169,6 +169,33 @@ To install the completions permanently, they can be moved to
|
||||
return res.Emit(&buf)
|
||||
},
|
||||
},
|
||||
"fish": {
|
||||
Helptext: cmds.HelpText{
|
||||
Tagline: "Generate fish shell completions.",
|
||||
ShortDescription: "Generates command completions for the fish shell.",
|
||||
LongDescription: `
|
||||
Generates command completions for the fish shell.
|
||||
|
||||
The simplest way to see it working is write the completions
|
||||
to a file and then source it:
|
||||
|
||||
> ipfs commands completion fish > ipfs-completion.fish
|
||||
> source ./ipfs-completion.fish
|
||||
|
||||
To install the completions permanently, they can be moved to
|
||||
/etc/fish/completions or ~/.config/fish/completions or sourced from your ~/.config/fish/config.fish file.
|
||||
`,
|
||||
},
|
||||
NoRemote: true,
|
||||
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
|
||||
var buf bytes.Buffer
|
||||
if err := writeFishCompletions(root, &buf); err != nil {
|
||||
return err
|
||||
}
|
||||
res.SetLength(uint64(buf.Len()))
|
||||
return res.Emit(&buf)
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,6 +24,7 @@ func TestROCommands(t *testing.T) {
|
||||
"/commands",
|
||||
"/commands/completion",
|
||||
"/commands/completion/bash",
|
||||
"/commands/completion/fish",
|
||||
"/dag",
|
||||
"/dag/get",
|
||||
"/dag/resolve",
|
||||
@ -99,6 +100,7 @@ func TestCommands(t *testing.T) {
|
||||
"/commands",
|
||||
"/commands/completion",
|
||||
"/commands/completion/bash",
|
||||
"/commands/completion/fish",
|
||||
"/config",
|
||||
"/config/edit",
|
||||
"/config/profile",
|
||||
|
||||
@ -10,41 +10,62 @@ import (
|
||||
|
||||
type completionCommand struct {
|
||||
Name string
|
||||
FullName string
|
||||
Description string
|
||||
Subcommands []*completionCommand
|
||||
Flags []*singleOption
|
||||
Options []*singleOption
|
||||
ShortFlags []string
|
||||
ShortOptions []string
|
||||
LongFlags []string
|
||||
LongOptions []string
|
||||
IsFinal bool
|
||||
}
|
||||
|
||||
func commandToCompletions(name string, cmd *cmds.Command) *completionCommand {
|
||||
type singleOption struct {
|
||||
LongNames []string
|
||||
ShortNames []string
|
||||
Description string
|
||||
}
|
||||
|
||||
func commandToCompletions(name string, fullName string, cmd *cmds.Command) *completionCommand {
|
||||
parsed := &completionCommand{
|
||||
Name: name,
|
||||
Name: name,
|
||||
FullName: fullName,
|
||||
Description: cmd.Helptext.Tagline,
|
||||
IsFinal: len(cmd.Subcommands) == 0,
|
||||
}
|
||||
for name, subCmd := range cmd.Subcommands {
|
||||
parsed.Subcommands = append(parsed.Subcommands, commandToCompletions(name, subCmd))
|
||||
parsed.Subcommands = append(parsed.Subcommands,
|
||||
commandToCompletions(name, fullName+" "+name, subCmd))
|
||||
}
|
||||
sort.Slice(parsed.Subcommands, func(i, j int) bool {
|
||||
return parsed.Subcommands[i].Name < parsed.Subcommands[j].Name
|
||||
})
|
||||
|
||||
for _, opt := range cmd.Options {
|
||||
flag := &singleOption{Description: opt.Description()}
|
||||
flag.LongNames = append(flag.LongNames, opt.Name())
|
||||
if opt.Type() == cmds.Bool {
|
||||
parsed.LongFlags = append(parsed.LongFlags, opt.Name())
|
||||
for _, name := range opt.Names() {
|
||||
if len(name) == 1 {
|
||||
parsed.ShortFlags = append(parsed.ShortFlags, name)
|
||||
flag.ShortNames = append(flag.ShortNames, name)
|
||||
break
|
||||
}
|
||||
}
|
||||
parsed.Flags = append(parsed.Flags, flag)
|
||||
} else {
|
||||
parsed.LongOptions = append(parsed.LongOptions, opt.Name())
|
||||
for _, name := range opt.Names() {
|
||||
if len(name) == 1 {
|
||||
parsed.ShortOptions = append(parsed.ShortOptions, name)
|
||||
flag.ShortNames = append(flag.ShortNames, name)
|
||||
break
|
||||
}
|
||||
}
|
||||
parsed.Options = append(parsed.Options, flag)
|
||||
}
|
||||
}
|
||||
sort.Slice(parsed.LongFlags, func(i, j int) bool {
|
||||
@ -62,7 +83,7 @@ func commandToCompletions(name string, cmd *cmds.Command) *completionCommand {
|
||||
return parsed
|
||||
}
|
||||
|
||||
var bashCompletionTemplate *template.Template
|
||||
var bashCompletionTemplate, fishCompletionTemplate *template.Template
|
||||
|
||||
func init() {
|
||||
commandTemplate := template.Must(template.New("command").Parse(`
|
||||
@ -133,10 +154,71 @@ _ipfs() {
|
||||
}
|
||||
complete -o nosort -o nospace -o default -F _ipfs ipfs
|
||||
`))
|
||||
|
||||
fishCommandTemplate := template.Must(template.New("command").Parse(`
|
||||
{{- if .IsFinal -}}
|
||||
complete -c ipfs -n '__fish_ipfs_seen_all_subcommands_from{{ .FullName }}' -F
|
||||
{{ end -}}
|
||||
{{- range .Flags -}}
|
||||
complete -c ipfs -n '__fish_ipfs_seen_all_subcommands_from{{ $.FullName }}' {{ range .ShortNames }}-s {{.}} {{end}}{{ range .LongNames }}-l {{.}} {{end}}-d "{{ .Description }}"
|
||||
{{ end -}}
|
||||
{{- range .Options -}}
|
||||
complete -c ipfs -n '__fish_ipfs_seen_all_subcommands_from{{ $.FullName }}' -r {{ range .ShortNames }}-s {{.}} {{end}}{{ range .LongNames }}-l {{.}} {{end}}-d "{{ .Description }}"
|
||||
{{ end -}}
|
||||
|
||||
{{- range .Subcommands }}
|
||||
#{{ .FullName }}
|
||||
complete -c ipfs -n '__fish_ipfs_use_subcommand{{ .FullName }}' -a {{ .Name }} -d "{{ .Description }}"
|
||||
{{ template "command" . }}
|
||||
{{ end -}}
|
||||
`))
|
||||
fishCompletionTemplate = template.Must(fishCommandTemplate.New("root").Parse(`#!/usr/bin/env fish
|
||||
function __fish_ipfs_seen_all_subcommands_from
|
||||
set -l cmd (commandline -poc)
|
||||
set -e cmd[1]
|
||||
for c in $argv
|
||||
if not contains -- $c $cmd
|
||||
return 1
|
||||
end
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
function __fish_ipfs_use_subcommand
|
||||
set -e argv[-1]
|
||||
set -l cmd (commandline -poc)
|
||||
set -e cmd[1]
|
||||
for i in $cmd
|
||||
switch $i
|
||||
case '-*'
|
||||
continue
|
||||
case $argv[1]
|
||||
set argv $argv[2..]
|
||||
continue
|
||||
case '*'
|
||||
return 1
|
||||
end
|
||||
end
|
||||
test -z "$argv"
|
||||
end
|
||||
|
||||
complete -c ipfs -l help -d "Show the full command help text."
|
||||
|
||||
complete -c ipfs --keep-order --no-files
|
||||
|
||||
{{ template "command" . }}
|
||||
`))
|
||||
|
||||
}
|
||||
|
||||
// writeBashCompletions generates a bash completion script for the given command tree.
|
||||
func writeBashCompletions(cmd *cmds.Command, out io.Writer) error {
|
||||
cmds := commandToCompletions("ipfs", cmd)
|
||||
cmds := commandToCompletions("ipfs", "", cmd)
|
||||
return bashCompletionTemplate.Execute(out, cmds)
|
||||
}
|
||||
|
||||
// writeFishCompletions generates a fish completion script for the given command tree.
|
||||
func writeFishCompletions(cmd *cmds.Command, out io.Writer) error {
|
||||
cmds := commandToCompletions("ipfs", "", cmd)
|
||||
return fishCompletionTemplate.Execute(out, cmds)
|
||||
}
|
||||
|
||||
@ -11,3 +11,16 @@ The simplest way to "eval" the completions logic:
|
||||
|
||||
To install the completions permanently, they can be moved to
|
||||
`/etc/bash_completion.d` or sourced from your `~/.bashrc` file.
|
||||
|
||||
## Fish
|
||||
|
||||
The fish shell is also supported:
|
||||
|
||||
The simplest way to use the completions logic:
|
||||
|
||||
```bash
|
||||
> ipfs commands completion fish | source
|
||||
```
|
||||
|
||||
To install the completions permanently, they can be moved to
|
||||
`/etc/fish/completions` or `~/.config/fish/completions` or sourced from your `~/.config/fish/config.fish` file.
|
||||
|
||||
16
test/sharness/t0012-completion-fish.sh
Executable file
16
test/sharness/t0012-completion-fish.sh
Executable file
@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
test_description="Test generated fish completions"
|
||||
|
||||
. lib/test-lib.sh
|
||||
|
||||
test_expect_success "'ipfs commands completion fish' succeeds" '
|
||||
ipfs commands completion fish > completions.fish
|
||||
'
|
||||
|
||||
test_expect_success "generated completions completes 'ipfs version'" '
|
||||
fish -c "source completions.fish && complete -C \"ipfs ver\" | grep -q \"version.Show IPFS version information.\" "
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
Loading…
Reference in New Issue
Block a user