-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8043 from ipfs/feat/completions
programmatic shell completions
- Loading branch information
Showing
7 changed files
with
211 additions
and
982 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
package commands | ||
|
||
import ( | ||
"io" | ||
"sort" | ||
"text/template" | ||
|
||
cmds "github.com/ipfs/go-ipfs-cmds" | ||
) | ||
|
||
type completionCommand struct { | ||
Name string | ||
Subcommands []*completionCommand | ||
ShortFlags []string | ||
ShortOptions []string | ||
LongFlags []string | ||
LongOptions []string | ||
} | ||
|
||
func commandToCompletions(name string, cmd *cmds.Command) *completionCommand { | ||
parsed := &completionCommand{ | ||
Name: name, | ||
} | ||
for name, subCmd := range cmd.Subcommands { | ||
parsed.Subcommands = append(parsed.Subcommands, commandToCompletions(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 { | ||
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) | ||
break | ||
} | ||
} | ||
} else { | ||
parsed.LongOptions = append(parsed.LongOptions, opt.Name()) | ||
for _, name := range opt.Names() { | ||
if len(name) == 1 { | ||
parsed.ShortOptions = append(parsed.ShortOptions, name) | ||
break | ||
} | ||
} | ||
} | ||
} | ||
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 *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 | ||
`)) | ||
} | ||
|
||
// 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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,13 @@ | ||
Command Completion | ||
================== | ||
# Command Completion | ||
|
||
Shell command completion is provided by the script at | ||
[/misc/completion/ipfs-completion.bash](../misc/completion/ipfs-completion.bash). | ||
Shell command completions can be generated by running one of the `ipfs commands completions` | ||
sub-commands. | ||
|
||
The simplest way to "eval" the completions logic: | ||
|
||
Installation | ||
------------ | ||
The simplest way to see it working is to run | ||
`source misc/completion/ipfs-completion.bash` straight from your shell. This | ||
is only temporary and to fully enable it, you'll have to follow one of the steps | ||
below. | ||
|
||
### Bash on Linux | ||
For bash, completion can be enabled in a couple of ways. One is to copy the | ||
completion script to the directory `~/.ipfs/` and then in the file | ||
`~/.bash_completion` add | ||
```bash | ||
source ~/.ipfs/ipfs-completion.bash | ||
> eval "$(ipfs commands completion bash)" | ||
``` | ||
It will automatically be loaded the next time bash is loaded. | ||
To enable ipfs command completion globally on your system you may also | ||
copy the completion script to `/etc/bash_completion.d/`. | ||
|
||
|
||
Additional References | ||
--------------------- | ||
* https://www.debian-administration.org/article/316/An_introduction_to_bash_completion_part_1 | ||
To install the completions permanently, they can be moved to | ||
`/etc/bash_completion.d` or sourced from your `~/.bashrc` file. |
Oops, something went wrong.