1
0
mirror of https://github.com/ipfs/kubo.git synced 2025-08-06 19:44:01 +08:00
Files
kubo/core/commands/completion.go

224 lines
5.9 KiB
Go

package commands
import (
"io"
"sort"
"text/template"
cmds "github.com/ipfs/go-ipfs-cmds"
)
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
}
type singleOption struct {
LongNames []string
ShortNames []string
Description string
}
func commandToCompletions(name string, fullName string, cmd *cmds.Command) *completionCommand {
parsed := &completionCommand{
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, 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 {
return parsed.LongFlags[i] < parsed.LongFlags[j]
})
sort.Slice(parsed.ShortFlags, func(i, j int) bool {
return parsed.ShortFlags[i] < parsed.ShortFlags[j]
})
sort.Slice(parsed.LongOptions, func(i, j int) bool {
return parsed.LongOptions[i] < parsed.LongOptions[j]
})
sort.Slice(parsed.ShortOptions, func(i, j int) bool {
return parsed.ShortOptions[i] < parsed.ShortOptions[j]
})
return parsed
}
var bashCompletionTemplate, fishCompletionTemplate *template.Template
func init() {
commandTemplate := template.Must(template.New("command").Parse(`
while [[ ${index} -lt ${COMP_CWORD} ]]; do
case "${COMP_WORDS[index]}" in
-*)
let index++
continue
;;
{{ range .Subcommands }}
"{{ .Name }}")
let index++
{{ template "command" . }}
return 0
;;
{{ end }}
esac
break
done
if [[ "${word}" == -* ]]; then
{{ if .ShortFlags -}}
_ipfs_compgen -W $'{{ range .ShortFlags }}-{{.}} \n{{end}}' -- "${word}"
{{ end -}}
{{- if .ShortOptions -}}
_ipfs_compgen -S = -W $'{{ range .ShortOptions }}-{{.}}\n{{end}}' -- "${word}"
{{ end -}}
{{- if .LongFlags -}}
_ipfs_compgen -W $'{{ range .LongFlags }}--{{.}} \n{{end}}' -- "${word}"
{{ end -}}
{{- if .LongOptions -}}
_ipfs_compgen -S = -W $'{{ range .LongOptions }}--{{.}}\n{{end}}' -- "${word}"
{{ end -}}
return 0
fi
while [[ ${index} -lt ${COMP_CWORD} ]]; do
if [[ "${COMP_WORDS[index]}" != -* ]]; then
let argidx++
fi
let index++
done
{{- if .Subcommands }}
if [[ "${argidx}" -eq 0 ]]; then
_ipfs_compgen -W $'{{ range .Subcommands }}{{.Name}} \n{{end}}' -- "${word}"
fi
{{ end -}}
`))
bashCompletionTemplate = template.Must(commandTemplate.New("root").Parse(`#!/bin/bash
_ipfs_compgen() {
local oldifs="$IFS"
IFS=$'\n'
while read -r line; do
COMPREPLY+=("$line")
done < <(compgen "$@")
IFS="$oldifs"
}
_ipfs() {
COMPREPLY=()
local index=1
local argidx=0
local word="${COMP_WORDS[COMP_CWORD]}"
{{ template "command" . }}
}
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)
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)
}