diff options
author | Michael Muré <batolettre@gmail.com> | 2019-06-23 14:31:27 +0200 |
---|---|---|
committer | Michael Muré <batolettre@gmail.com> | 2019-06-23 14:33:02 +0200 |
commit | b64587f87abdca0bb15120bf3da48eedd812ca6b (patch) | |
tree | 0846697446f1a75a170c8cef749b35992a40bde5 | |
parent | 7461c1fe3a9ea404e38dd05182400d5739793d11 (diff) | |
download | git-bug-b64587f87abdca0bb15120bf3da48eedd812ca6b.tar.gz |
misc: generate PowerShell command completion
-rw-r--r-- | doc/man/git-bug-bridge-rm.1 | 2 | ||||
-rw-r--r-- | doc/md/git-bug_bridge_rm.md | 2 | ||||
-rw-r--r-- | git-bug.go | 1 | ||||
-rw-r--r-- | misc/gen_bash_completion.go | 2 | ||||
-rw-r--r-- | misc/gen_powershell_completion.go | 24 | ||||
-rw-r--r-- | misc/gen_zsh_completion.go | 2 | ||||
-rw-r--r-- | misc/powershell_completion/git-bug | 207 | ||||
-rw-r--r-- | misc/zsh_completion/git-bug | 442 | ||||
-rw-r--r-- | vendor/github.com/spf13/cobra/.gitignore | 2 | ||||
-rw-r--r-- | vendor/github.com/spf13/cobra/README.md | 9 | ||||
-rw-r--r-- | vendor/github.com/spf13/cobra/bash_completions.go | 48 | ||||
-rw-r--r-- | vendor/github.com/spf13/cobra/command.go | 97 | ||||
-rw-r--r-- | vendor/github.com/spf13/cobra/powershell_completions.go | 100 | ||||
-rw-r--r-- | vendor/github.com/spf13/cobra/powershell_completions.md | 14 | ||||
-rw-r--r-- | vendor/github.com/spf13/cobra/shell_completions.go | 85 | ||||
-rw-r--r-- | vendor/github.com/spf13/cobra/zsh_completions.go | 363 | ||||
-rw-r--r-- | vendor/github.com/spf13/cobra/zsh_completions.md | 39 |
17 files changed, 1250 insertions, 189 deletions
diff --git a/doc/man/git-bug-bridge-rm.1 b/doc/man/git-bug-bridge-rm.1 index 630eba93..324d4237 100644 --- a/doc/man/git-bug-bridge-rm.1 +++ b/doc/man/git-bug-bridge-rm.1 @@ -10,7 +10,7 @@ git\-bug\-bridge\-rm \- Delete a configured bridge. .SH SYNOPSIS .PP -\fBgit\-bug bridge rm name <name> [flags]\fP +\fBgit\-bug bridge rm <name> [flags]\fP .SH DESCRIPTION diff --git a/doc/md/git-bug_bridge_rm.md b/doc/md/git-bug_bridge_rm.md index ab58e969..54941b2f 100644 --- a/doc/md/git-bug_bridge_rm.md +++ b/doc/md/git-bug_bridge_rm.md @@ -7,7 +7,7 @@ Delete a configured bridge. Delete a configured bridge. ``` -git-bug bridge rm name <name> [flags] +git-bug bridge rm <name> [flags] ``` ### Options @@ -1,6 +1,7 @@ //go:generate go run doc/gen_markdown.go //go:generate go run doc/gen_manpage.go //go:generate go run misc/gen_bash_completion.go +//go:generate go run misc/gen_powershell_completion.go //go:generate go run misc/gen_zsh_completion.go package main diff --git a/misc/gen_bash_completion.go b/misc/gen_bash_completion.go index f2506606..2d5e400b 100644 --- a/misc/gen_bash_completion.go +++ b/misc/gen_bash_completion.go @@ -15,7 +15,7 @@ func main() { cwd, _ := os.Getwd() dir := path.Join(cwd, "misc", "bash_completion", "git-bug") - fmt.Println("Generating bash completion file ...") + fmt.Println("Generating Bash completion file ...") err := commands.RootCmd.GenBashCompletionFile(dir) if err != nil { diff --git a/misc/gen_powershell_completion.go b/misc/gen_powershell_completion.go new file mode 100644 index 00000000..c2766399 --- /dev/null +++ b/misc/gen_powershell_completion.go @@ -0,0 +1,24 @@ +// +build ignore + +package main + +import ( + "fmt" + "log" + "os" + "path" + + "github.com/MichaelMure/git-bug/commands" +) + +func main() { + cwd, _ := os.Getwd() + filepath := path.Join(cwd, "misc", "powershell_completion", "git-bug") + + fmt.Println("Generating PowerShell completion file ...") + + err := commands.RootCmd.GenPowerShellCompletionFile(filepath) + if err != nil { + log.Fatal(err) + } +} diff --git a/misc/gen_zsh_completion.go b/misc/gen_zsh_completion.go index 184cab43..f80477d7 100644 --- a/misc/gen_zsh_completion.go +++ b/misc/gen_zsh_completion.go @@ -15,7 +15,7 @@ func main() { cwd, _ := os.Getwd() filepath := path.Join(cwd, "misc", "zsh_completion", "git-bug") - fmt.Println("Generating zsh completion file ...") + fmt.Println("Generating ZSH completion file ...") err := commands.RootCmd.GenZshCompletionFile(filepath) if err != nil { diff --git a/misc/powershell_completion/git-bug b/misc/powershell_completion/git-bug new file mode 100644 index 00000000..7eff1cda --- /dev/null +++ b/misc/powershell_completion/git-bug @@ -0,0 +1,207 @@ +using namespace System.Management.Automation +using namespace System.Management.Automation.Language +Register-ArgumentCompleter -Native -CommandName 'git-bug' -ScriptBlock { + param($wordToComplete, $commandAst, $cursorPosition) + $commandElements = $commandAst.CommandElements + $command = @( + 'git-bug' + for ($i = 1; $i -lt $commandElements.Count; $i++) { + $element = $commandElements[$i] + if ($element -isnot [StringConstantExpressionAst] -or + $element.StringConstantType -ne [StringConstantType]::BareWord -or + $element.Value.StartsWith('-')) { + break + } + $element.Value + } + ) -join ';' + $completions = @(switch ($command) { + 'git-bug' { + [CompletionResult]::new('add', 'add', [CompletionResultType]::ParameterValue, 'Create a new bug.') + [CompletionResult]::new('bridge', 'bridge', [CompletionResultType]::ParameterValue, 'Configure and use bridges to other bug trackers.') + [CompletionResult]::new('commands', 'commands', [CompletionResultType]::ParameterValue, 'Display available commands.') + [CompletionResult]::new('comment', 'comment', [CompletionResultType]::ParameterValue, 'Display or add comments to a bug.') + [CompletionResult]::new('deselect', 'deselect', [CompletionResultType]::ParameterValue, 'Clear the implicitly selected bug.') + [CompletionResult]::new('label', 'label', [CompletionResultType]::ParameterValue, 'Display, add or remove labels to/from a bug.') + [CompletionResult]::new('ls', 'ls', [CompletionResultType]::ParameterValue, 'List bugs.') + [CompletionResult]::new('ls-id', 'ls-id', [CompletionResultType]::ParameterValue, 'List bug identifiers.') + [CompletionResult]::new('ls-label', 'ls-label', [CompletionResultType]::ParameterValue, 'List valid labels.') + [CompletionResult]::new('pull', 'pull', [CompletionResultType]::ParameterValue, 'Pull bugs update from a git remote.') + [CompletionResult]::new('push', 'push', [CompletionResultType]::ParameterValue, 'Push bugs update to a git remote.') + [CompletionResult]::new('select', 'select', [CompletionResultType]::ParameterValue, 'Select a bug for implicit use in future commands.') + [CompletionResult]::new('show', 'show', [CompletionResultType]::ParameterValue, 'Display the details of a bug.') + [CompletionResult]::new('status', 'status', [CompletionResultType]::ParameterValue, 'Display or change a bug status.') + [CompletionResult]::new('termui', 'termui', [CompletionResultType]::ParameterValue, 'Launch the terminal UI.') + [CompletionResult]::new('title', 'title', [CompletionResultType]::ParameterValue, 'Display or change a title of a bug.') + [CompletionResult]::new('user', 'user', [CompletionResultType]::ParameterValue, 'Display or change the user identity.') + [CompletionResult]::new('version', 'version', [CompletionResultType]::ParameterValue, 'Show git-bug version information.') + [CompletionResult]::new('webui', 'webui', [CompletionResultType]::ParameterValue, 'Launch the web UI.') + break + } + 'git-bug;add' { + [CompletionResult]::new('-t', 't', [CompletionResultType]::ParameterName, 'Provide a title to describe the issue') + [CompletionResult]::new('--title', 'title', [CompletionResultType]::ParameterName, 'Provide a title to describe the issue') + [CompletionResult]::new('-m', 'm', [CompletionResultType]::ParameterName, 'Provide a message to describe the issue') + [CompletionResult]::new('--message', 'message', [CompletionResultType]::ParameterName, 'Provide a message to describe the issue') + [CompletionResult]::new('-F', 'F', [CompletionResultType]::ParameterName, 'Take the message from the given file. Use - to read the message from the standard input') + [CompletionResult]::new('--file', 'file', [CompletionResultType]::ParameterName, 'Take the message from the given file. Use - to read the message from the standard input') + break + } + 'git-bug;bridge' { + [CompletionResult]::new('configure', 'configure', [CompletionResultType]::ParameterValue, 'Configure a new bridge.') + [CompletionResult]::new('pull', 'pull', [CompletionResultType]::ParameterValue, 'Pull updates.') + [CompletionResult]::new('rm', 'rm', [CompletionResultType]::ParameterValue, 'Delete a configured bridge.') + break + } + 'git-bug;bridge;configure' { + [CompletionResult]::new('-n', 'n', [CompletionResultType]::ParameterName, 'A distinctive name to identify the bridge') + [CompletionResult]::new('--name', 'name', [CompletionResultType]::ParameterName, 'A distinctive name to identify the bridge') + [CompletionResult]::new('-t', 't', [CompletionResultType]::ParameterName, 'The target of the bridge. Valid values are [github,launchpad-preview]') + [CompletionResult]::new('--target', 'target', [CompletionResultType]::ParameterName, 'The target of the bridge. Valid values are [github,launchpad-preview]') + [CompletionResult]::new('-u', 'u', [CompletionResultType]::ParameterName, 'The URL of the target repository') + [CompletionResult]::new('--url', 'url', [CompletionResultType]::ParameterName, 'The URL of the target repository') + [CompletionResult]::new('-o', 'o', [CompletionResultType]::ParameterName, 'The owner of the target repository') + [CompletionResult]::new('--owner', 'owner', [CompletionResultType]::ParameterName, 'The owner of the target repository') + [CompletionResult]::new('-T', 'T', [CompletionResultType]::ParameterName, 'The authentication token for the API') + [CompletionResult]::new('--token', 'token', [CompletionResultType]::ParameterName, 'The authentication token for the API') + [CompletionResult]::new('-p', 'p', [CompletionResultType]::ParameterName, 'The name of the target repository') + [CompletionResult]::new('--project', 'project', [CompletionResultType]::ParameterName, 'The name of the target repository') + break + } + 'git-bug;bridge;pull' { + break + } + 'git-bug;bridge;rm' { + break + } + 'git-bug;commands' { + [CompletionResult]::new('-p', 'p', [CompletionResultType]::ParameterName, 'Output the command description as well as Markdown compatible comment') + [CompletionResult]::new('--pretty', 'pretty', [CompletionResultType]::ParameterName, 'Output the command description as well as Markdown compatible comment') + break + } + 'git-bug;comment' { + [CompletionResult]::new('add', 'add', [CompletionResultType]::ParameterValue, 'Add a new comment to a bug.') + break + } + 'git-bug;comment;add' { + [CompletionResult]::new('-F', 'F', [CompletionResultType]::ParameterName, 'Take the message from the given file. Use - to read the message from the standard input') + [CompletionResult]::new('--file', 'file', [CompletionResultType]::ParameterName, 'Take the message from the given file. Use - to read the message from the standard input') + [CompletionResult]::new('-m', 'm', [CompletionResultType]::ParameterName, 'Provide the new message from the command line') + [CompletionResult]::new('--message', 'message', [CompletionResultType]::ParameterName, 'Provide the new message from the command line') + break + } + 'git-bug;deselect' { + break + } + 'git-bug;label' { + [CompletionResult]::new('add', 'add', [CompletionResultType]::ParameterValue, 'Add a label to a bug.') + [CompletionResult]::new('rm', 'rm', [CompletionResultType]::ParameterValue, 'Remove a label from a bug.') + break + } + 'git-bug;label;add' { + break + } + 'git-bug;label;rm' { + break + } + 'git-bug;ls' { + [CompletionResult]::new('-s', 's', [CompletionResultType]::ParameterName, 'Filter by status. Valid values are [open,closed]') + [CompletionResult]::new('--status', 'status', [CompletionResultType]::ParameterName, 'Filter by status. Valid values are [open,closed]') + [CompletionResult]::new('-a', 'a', [CompletionResultType]::ParameterName, 'Filter by author') + [CompletionResult]::new('--author', 'author', [CompletionResultType]::ParameterName, 'Filter by author') + [CompletionResult]::new('-p', 'p', [CompletionResultType]::ParameterName, 'Filter by participant') + [CompletionResult]::new('--participant', 'participant', [CompletionResultType]::ParameterName, 'Filter by participant') + [CompletionResult]::new('-A', 'A', [CompletionResultType]::ParameterName, 'Filter by actor') + [CompletionResult]::new('--actor', 'actor', [CompletionResultType]::ParameterName, 'Filter by actor') + [CompletionResult]::new('-l', 'l', [CompletionResultType]::ParameterName, 'Filter by label') + [CompletionResult]::new('--label', 'label', [CompletionResultType]::ParameterName, 'Filter by label') + [CompletionResult]::new('-t', 't', [CompletionResultType]::ParameterName, 'Filter by title') + [CompletionResult]::new('--title', 'title', [CompletionResultType]::ParameterName, 'Filter by title') + [CompletionResult]::new('-n', 'n', [CompletionResultType]::ParameterName, 'Filter by absence of something. Valid values are [label]') + [CompletionResult]::new('--no', 'no', [CompletionResultType]::ParameterName, 'Filter by absence of something. Valid values are [label]') + [CompletionResult]::new('-b', 'b', [CompletionResultType]::ParameterName, 'Sort the results by a characteristic. Valid values are [id,creation,edit]') + [CompletionResult]::new('--by', 'by', [CompletionResultType]::ParameterName, 'Sort the results by a characteristic. Valid values are [id,creation,edit]') + [CompletionResult]::new('-d', 'd', [CompletionResultType]::ParameterName, 'Select the sorting direction. Valid values are [asc,desc]') + [CompletionResult]::new('--direction', 'direction', [CompletionResultType]::ParameterName, 'Select the sorting direction. Valid values are [asc,desc]') + break + } + 'git-bug;ls-id' { + break + } + 'git-bug;ls-label' { + break + } + 'git-bug;pull' { + break + } + 'git-bug;push' { + break + } + 'git-bug;select' { + break + } + 'git-bug;show' { + [CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Select field to display. Valid values are [author,authorEmail,createTime,humanId,id,labels,shortId,status,title,actors,participants]') + [CompletionResult]::new('--field', 'field', [CompletionResultType]::ParameterName, 'Select field to display. Valid values are [author,authorEmail,createTime,humanId,id,labels,shortId,status,title,actors,participants]') + break + } + 'git-bug;status' { + [CompletionResult]::new('close', 'close', [CompletionResultType]::ParameterValue, 'Mark a bug as closed.') + [CompletionResult]::new('open', 'open', [CompletionResultType]::ParameterValue, 'Mark a bug as open.') + break + } + 'git-bug;status;close' { + break + } + 'git-bug;status;open' { + break + } + 'git-bug;termui' { + break + } + 'git-bug;title' { + [CompletionResult]::new('edit', 'edit', [CompletionResultType]::ParameterValue, 'Edit a title of a bug.') + break + } + 'git-bug;title;edit' { + [CompletionResult]::new('-t', 't', [CompletionResultType]::ParameterName, 'Provide a title to describe the issue') + [CompletionResult]::new('--title', 'title', [CompletionResultType]::ParameterName, 'Provide a title to describe the issue') + break + } + 'git-bug;user' { + [CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Select field to display. Valid values are [email,humanId,id,lastModification,lastModificationLamport,login,metadata,name]') + [CompletionResult]::new('--field', 'field', [CompletionResultType]::ParameterName, 'Select field to display. Valid values are [email,humanId,id,lastModification,lastModificationLamport,login,metadata,name]') + [CompletionResult]::new('adopt', 'adopt', [CompletionResultType]::ParameterValue, 'Adopt an existing identity as your own.') + [CompletionResult]::new('create', 'create', [CompletionResultType]::ParameterValue, 'Create a new identity.') + [CompletionResult]::new('ls', 'ls', [CompletionResultType]::ParameterValue, 'List identities.') + break + } + 'git-bug;user;adopt' { + break + } + 'git-bug;user;create' { + break + } + 'git-bug;user;ls' { + break + } + 'git-bug;version' { + [CompletionResult]::new('-n', 'n', [CompletionResultType]::ParameterName, 'Only show the version number') + [CompletionResult]::new('--number', 'number', [CompletionResultType]::ParameterName, 'Only show the version number') + [CompletionResult]::new('-c', 'c', [CompletionResultType]::ParameterName, 'Only show the commit hash') + [CompletionResult]::new('--commit', 'commit', [CompletionResultType]::ParameterName, 'Only show the commit hash') + [CompletionResult]::new('-a', 'a', [CompletionResultType]::ParameterName, 'Show all version informations') + [CompletionResult]::new('--all', 'all', [CompletionResultType]::ParameterName, 'Show all version informations') + break + } + 'git-bug;webui' { + [CompletionResult]::new('--open', 'open', [CompletionResultType]::ParameterName, 'Automatically open the web UI in the default browser') + [CompletionResult]::new('--no-open', 'no-open', [CompletionResultType]::ParameterName, 'Prevent the automatic opening of the web UI in the default browser') + [CompletionResult]::new('-p', 'p', [CompletionResultType]::ParameterName, 'Port to listen to (default is random)') + [CompletionResult]::new('--port', 'port', [CompletionResultType]::ParameterName, 'Port to listen to (default is random)') + break + } + }) + $completions.Where{ $_.CompletionText -like "$wordToComplete*" } | + Sort-Object -Property ListItemText +}
\ No newline at end of file diff --git a/misc/zsh_completion/git-bug b/misc/zsh_completion/git-bug index c2ed9872..ed676724 100644 --- a/misc/zsh_completion/git-bug +++ b/misc/zsh_completion/git-bug @@ -1,46 +1,396 @@ -#compdef git-bug - -_arguments \ - '1: :->level1' \ - '2: :->level2' \ - '3: :_files' -case $state in - level1) - case $words[1] in - git-bug) - _arguments '1: :(add bridge commands comment deselect label ls ls-id ls-label pull push select show status termui title user version webui)' - ;; - *) - _arguments '*: :_files' - ;; - esac - ;; - level2) - case $words[2] in - bridge) - _arguments '2: :(configure pull rm)' - ;; - comment) - _arguments '2: :(add)' - ;; - label) - _arguments '2: :(add rm)' - ;; - status) - _arguments '2: :(close open)' - ;; - title) - _arguments '2: :(edit)' - ;; - user) - _arguments '2: :(adopt create ls)' - ;; - *) - _arguments '*: :_files' - ;; - esac - ;; - *) - _arguments '*: :_files' - ;; -esac +#compdef _git-bug git-bug + + +function _git-bug { + local -a commands + + _arguments -C \ + "1: :->cmnds" \ + "*::arg:->args" + + case $state in + cmnds) + commands=( + "add:Create a new bug." + "bridge:Configure and use bridges to other bug trackers." + "commands:Display available commands." + "comment:Display or add comments to a bug." + "deselect:Clear the implicitly selected bug." + "label:Display, add or remove labels to/from a bug." + "ls:List bugs." + "ls-id:List bug identifiers." + "ls-label:List valid labels." + "pull:Pull bugs update from a git remote." + "push:Push bugs update to a git remote." + "select:Select a bug for implicit use in future commands." + "show:Display the details of a bug." + "status:Display or change a bug status." + "termui:Launch the terminal UI." + "title:Display or change a title of a bug." + "user:Display or change the user identity." + "version:Show git-bug version information." + "webui:Launch the web UI." + ) + _describe "command" commands + ;; + esac + + case "$words[1]" in + add) + _git-bug_add + ;; + bridge) + _git-bug_bridge + ;; + commands) + _git-bug_commands + ;; + comment) + _git-bug_comment + ;; + deselect) + _git-bug_deselect + ;; + label) + _git-bug_label + ;; + ls) + _git-bug_ls + ;; + ls-id) + _git-bug_ls-id + ;; + ls-label) + _git-bug_ls-label + ;; + pull) + _git-bug_pull + ;; + push) + _git-bug_push + ;; + select) + _git-bug_select + ;; + show) + _git-bug_show + ;; + status) + _git-bug_status + ;; + termui) + _git-bug_termui + ;; + title) + _git-bug_title + ;; + user) + _git-bug_user + ;; + version) + _git-bug_version + ;; + webui) + _git-bug_webui + ;; + esac +} + +function _git-bug_add { + _arguments \ + '(-t --title)'{-t,--title}'[Provide a title to describe the issue]:' \ + '(-m --message)'{-m,--message}'[Provide a message to describe the issue]:' \ + '(-F --file)'{-F,--file}'[Take the message from the given file. Use - to read the message from the standard input]:' +} + + +function _git-bug_bridge { + local -a commands + + _arguments -C \ + "1: :->cmnds" \ + "*::arg:->args" + + case $state in + cmnds) + commands=( + "configure:Configure a new bridge." + "pull:Pull updates." + "rm:Delete a configured bridge." + ) + _describe "command" commands + ;; + esac + + case "$words[1]" in + configure) + _git-bug_bridge_configure + ;; + pull) + _git-bug_bridge_pull + ;; + rm) + _git-bug_bridge_rm + ;; + esac +} + +function _git-bug_bridge_configure { + _arguments \ + '(-n --name)'{-n,--name}'[A distinctive name to identify the bridge]:' \ + '(-t --target)'{-t,--target}'[The target of the bridge. Valid values are [github,launchpad-preview]]:' \ + '(-u --url)'{-u,--url}'[The URL of the target repository]:' \ + '(-o --owner)'{-o,--owner}'[The owner of the target repository]:' \ + '(-T --token)'{-T,--token}'[The authentication token for the API]:' \ + '(-p --project)'{-p,--project}'[The name of the target repository]:' +} + +function _git-bug_bridge_pull { + _arguments +} + +function _git-bug_bridge_rm { + _arguments +} + +function _git-bug_commands { + _arguments \ + '(-p --pretty)'{-p,--pretty}'[Output the command description as well as Markdown compatible comment]' +} + + +function _git-bug_comment { + local -a commands + + _arguments -C \ + "1: :->cmnds" \ + "*::arg:->args" + + case $state in + cmnds) + commands=( + "add:Add a new comment to a bug." + ) + _describe "command" commands + ;; + esac + + case "$words[1]" in + add) + _git-bug_comment_add + ;; + esac +} + +function _git-bug_comment_add { + _arguments \ + '(-F --file)'{-F,--file}'[Take the message from the given file. Use - to read the message from the standard input]:' \ + '(-m --message)'{-m,--message}'[Provide the new message from the command line]:' +} + +function _git-bug_deselect { + _arguments +} + + +function _git-bug_label { + local -a commands + + _arguments -C \ + "1: :->cmnds" \ + "*::arg:->args" + + case $state in + cmnds) + commands=( + "add:Add a label to a bug." + "rm:Remove a label from a bug." + ) + _describe "command" commands + ;; + esac + + case "$words[1]" in + add) + _git-bug_label_add + ;; + rm) + _git-bug_label_rm + ;; + esac +} + +function _git-bug_label_add { + _arguments +} + +function _git-bug_label_rm { + _arguments +} + +function _git-bug_ls { + _arguments \ + '(*-s *--status)'{\*-s,\*--status}'[Filter by status. Valid values are [open,closed]]:' \ + '(*-a *--author)'{\*-a,\*--author}'[Filter by author]:' \ + '(*-p *--participant)'{\*-p,\*--participant}'[Filter by participant]:' \ + '(*-A *--actor)'{\*-A,\*--actor}'[Filter by actor]:' \ + '(*-l *--label)'{\*-l,\*--label}'[Filter by label]:' \ + '(*-t *--title)'{\*-t,\*--title}'[Filter by title]:' \ + '(*-n *--no)'{\*-n,\*--no}'[Filter by absence of something. Valid values are [label]]:' \ + '(-b --by)'{-b,--by}'[Sort the results by a characteristic. Valid values are [id,creation,edit]]:' \ + '(-d --direction)'{-d,--direction}'[Select the sorting direction. Valid values are [asc,desc]]:' +} + +function _git-bug_ls-id { + _arguments +} + +function _git-bug_ls-label { + _arguments +} + +function _git-bug_pull { + _arguments +} + +function _git-bug_push { + _arguments +} + +function _git-bug_select { + _arguments +} + +function _git-bug_show { + _arguments \ + '(-f --field)'{-f,--field}'[Select field to display. Valid values are [author,authorEmail,createTime,humanId,id,labels,shortId,status,title,actors,participants]]:' +} + + +function _git-bug_status { + local -a commands + + _arguments -C \ + "1: :->cmnds" \ + "*::arg:->args" + + case $state in + cmnds) + commands=( + "close:Mark a bug as closed." + "open:Mark a bug as open." + ) + _describe "command" commands + ;; + esac + + case "$words[1]" in + close) + _git-bug_status_close + ;; + open) + _git-bug_status_open + ;; + esac +} + +function _git-bug_status_close { + _arguments +} + +function _git-bug_status_open { + _arguments +} + +function _git-bug_termui { + _arguments +} + + +function _git-bug_title { + local -a commands + + _arguments -C \ + "1: :->cmnds" \ + "*::arg:->args" + + case $state in + cmnds) + commands=( + "edit:Edit a title of a bug." + ) + _describe "command" commands + ;; + esac + + case "$words[1]" in + edit) + _git-bug_title_edit + ;; + esac +} + +function _git-bug_title_edit { + _arguments \ + '(-t --title)'{-t,--title}'[Provide a title to describe the issue]:' +} + + +function _git-bug_user { + local -a commands + + _arguments -C \ + '(-f --field)'{-f,--field}'[Select field to display. Valid values are [email,humanId,id,lastModification,lastModificationLamport,login,metadata,name]]:' \ + "1: :->cmnds" \ + "*::arg:->args" + + case $state in + cmnds) + commands=( + "adopt:Adopt an existing identity as your own." + "create:Create a new identity." + "ls:List identities." + ) + _describe "command" commands + ;; + esac + + case "$words[1]" in + adopt) + _git-bug_user_adopt + ;; + create) + _git-bug_user_create + ;; + ls) + _git-bug_user_ls + ;; + esac +} + +function _git-bug_user_adopt { + _arguments +} + +function _git-bug_user_create { + _arguments +} + +function _git-bug_user_ls { + _arguments +} + +function _git-bug_version { + _arguments \ + '(-n --number)'{-n,--number}'[Only show the version number]' \ + '(-c --commit)'{-c,--commit}'[Only show the commit hash]' \ + '(-a --all)'{-a,--all}'[Show all version informations]' +} + +function _git-bug_webui { + _arguments \ + '--open[Automatically open the web UI in the default browser]' \ + '--no-open[Prevent the automatic opening of the web UI in the default browser]' \ + '(-p --port)'{-p,--port}'[Port to listen to (default is random)]:' +} + diff --git a/vendor/github.com/spf13/cobra/.gitignore b/vendor/github.com/spf13/cobra/.gitignore index 1b8c7c26..3b053c59 100644 --- a/vendor/github.com/spf13/cobra/.gitignore +++ b/vendor/github.com/spf13/cobra/.gitignore @@ -34,3 +34,5 @@ tags *.exe cobra.test + +.idea/* diff --git a/vendor/github.com/spf13/cobra/README.md b/vendor/github.com/spf13/cobra/README.md index ff16e3f6..60c5a425 100644 --- a/vendor/github.com/spf13/cobra/README.md +++ b/vendor/github.com/spf13/cobra/README.md @@ -23,6 +23,7 @@ Many of the most widely used Go projects are built using Cobra, such as: [Istio](https://istio.io), [Prototool](https://github.com/uber/prototool), [mattermost-server](https://github.com/mattermost/mattermost-server), +[Gardener](https://github.com/gardener/gardenctl), etc. [![Build Status](https://travis-ci.org/spf13/cobra.svg "Travis CI status")](https://travis-ci.org/spf13/cobra) @@ -48,6 +49,7 @@ etc. * [Suggestions when "unknown command" happens](#suggestions-when-unknown-command-happens) * [Generating documentation for your command](#generating-documentation-for-your-command) * [Generating bash completions](#generating-bash-completions) + * [Generating zsh completions](#generating-zsh-completions) - [Contributing](#contributing) - [License](#license) @@ -336,7 +338,7 @@ rootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose out A flag can also be assigned locally which will only apply to that specific command. ```go -rootCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from") +localCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from") ``` ### Local Flag on Parent Commands @@ -719,6 +721,11 @@ Cobra can generate documentation based on subcommands, flags, etc. in the follow Cobra can generate a bash-completion file. If you add more information to your command, these completions can be amazingly powerful and flexible. Read more about it in [Bash Completions](bash_completions.md). +## Generating zsh completions + +Cobra can generate zsh-completion file. Read more about it in +[Zsh Completions](zsh_completions.md). + # Contributing 1. Fork it diff --git a/vendor/github.com/spf13/cobra/bash_completions.go b/vendor/github.com/spf13/cobra/bash_completions.go index c3c1e501..57bb8e1b 100644 --- a/vendor/github.com/spf13/cobra/bash_completions.go +++ b/vendor/github.com/spf13/cobra/bash_completions.go @@ -545,51 +545,3 @@ func (c *Command) GenBashCompletionFile(filename string) error { return c.GenBashCompletion(outFile) } - -// MarkFlagRequired adds the BashCompOneRequiredFlag annotation to the named flag if it exists, -// and causes your command to report an error if invoked without the flag. -func (c *Command) MarkFlagRequired(name string) error { - return MarkFlagRequired(c.Flags(), name) -} - -// MarkPersistentFlagRequired adds the BashCompOneRequiredFlag annotation to the named persistent flag if it exists, -// and causes your command to report an error if invoked without the flag. -func (c *Command) MarkPersistentFlagRequired(name string) error { - return MarkFlagRequired(c.PersistentFlags(), name) -} - -// MarkFlagRequired adds the BashCompOneRequiredFlag annotation to the named flag if it exists, -// and causes your command to report an error if invoked without the flag. -func MarkFlagRequired(flags *pflag.FlagSet, name string) error { - return flags.SetAnnotation(name, BashCompOneRequiredFlag, []string{"true"}) -} - -// MarkFlagFilename adds the BashCompFilenameExt annotation to the named flag, if it exists. -// Generated bash autocompletion will select filenames for the flag, limiting to named extensions if provided. -func (c *Command) MarkFlagFilename(name string, extensions ...string) error { - return MarkFlagFilename(c.Flags(), name, extensions...) -} - -// MarkFlagCustom adds the BashCompCustom annotation to the named flag, if it exists. -// Generated bash autocompletion will call the bash function f for the flag. -func (c *Command) MarkFlagCustom(name string, f string) error { - return MarkFlagCustom(c.Flags(), name, f) -} - -// MarkPersistentFlagFilename adds the BashCompFilenameExt annotation to the named persistent flag, if it exists. -// Generated bash autocompletion will select filenames for the flag, limiting to named extensions if provided. -func (c *Command) MarkPersistentFlagFilename(name string, extensions ...string) error { - return MarkFlagFilename(c.PersistentFlags(), name, extensions...) -} - -// MarkFlagFilename adds the BashCompFilenameExt annotation to the named flag in the flag set, if it exists. -// Generated bash autocompletion will select filenames for the flag, limiting to named extensions if provided. -func MarkFlagFilename(flags *pflag.FlagSet, name string, extensions ...string) error { - return flags.SetAnnotation(name, BashCompFilenameExt, extensions) -} - -// MarkFlagCustom adds the BashCompCustom annotation to the named flag in the flag set, if it exists. -// Generated bash autocompletion will call the bash function f for the flag. -func MarkFlagCustom(flags *pflag.FlagSet, name string, f string) error { - return flags.SetAnnotation(name, BashCompCustom, []string{f}) -} diff --git a/vendor/github.com/spf13/cobra/command.go b/vendor/github.com/spf13/cobra/command.go index b257f91b..c7e89830 100644 --- a/vendor/github.com/spf13/cobra/command.go +++ b/vendor/github.com/spf13/cobra/command.go @@ -177,8 +177,6 @@ type Command struct { // that we can use on every pflag set and children commands globNormFunc func(f *flag.FlagSet, name string) flag.NormalizedName - // output is an output writer defined by user. - output io.Writer // usageFunc is usage func defined by user. usageFunc func(*Command) error // usageTemplate is usage template defined by user. @@ -195,6 +193,13 @@ type Command struct { helpCommand *Command // versionTemplate is the version template defined by user. versionTemplate string + + // inReader is a reader defined by the user that replaces stdin + inReader io.Reader + // outWriter is a writer defined by the user that replaces stdout + outWriter io.Writer + // errWriter is a writer defined by the user that replaces stderr + errWriter io.Writer } // SetArgs sets arguments for the command. It is set to os.Args[1:] by default, if desired, can be overridden @@ -205,8 +210,28 @@ func (c *Command) SetArgs(a []string) { // SetOutput sets the destination for usage and error messages. // If output is nil, os.Stderr is used. +// Deprecated: Use SetOut and/or SetErr instead func (c *Command) SetOutput(output io.Writer) { - c.output = output + c.outWriter = output + c.errWriter = output +} + +// SetOut sets the destination for usage messages. +// If newOut is nil, os.Stdout is used. +func (c *Command) SetOut(newOut io.Writer) { + c.outWriter = newOut +} + +// SetErr sets the destination for error messages. +// If newErr is nil, os.Stderr is used. +func (c *Command) SetErr(newErr io.Writer) { + c.errWriter = newErr +} + +// SetOut sets the source for input data +// If newIn is nil, os.Stdin is used. +func (c *Command) SetIn(newIn io.Reader) { + c.inReader = newIn } // SetUsageFunc sets usage function. Usage can be defined by application. @@ -267,9 +292,19 @@ func (c *Command) OutOrStderr() io.Writer { return c.getOut(os.Stderr) } +// ErrOrStderr returns output to stderr +func (c *Command) ErrOrStderr() io.Writer { + return c.getErr(os.Stderr) +} + +// ErrOrStderr returns output to stderr +func (c *Command) InOrStdin() io.Reader { + return c.getIn(os.Stdin) +} + func (c *Command) getOut(def io.Writer) io.Writer { - if c.output != nil { - return c.output + if c.outWriter != nil { + return c.outWriter } if c.HasParent() { return c.parent.getOut(def) @@ -277,6 +312,26 @@ func (c *Command) getOut(def io.Writer) io.Writer { return def } +func (c *Command) getErr(def io.Writer) io.Writer { + if c.errWriter != nil { + return c.errWriter + } + if c.HasParent() { + return c.parent.getErr(def) + } + return def +} + +func (c *Command) getIn(def io.Reader) io.Reader { + if c.inReader != nil { + return c.inReader + } + if c.HasParent() { + return c.parent.getIn(def) + } + return def +} + // UsageFunc returns either the function set by SetUsageFunc for this command // or a parent, or it returns a default usage function. func (c *Command) UsageFunc() (f func(*Command) error) { @@ -329,13 +384,22 @@ func (c *Command) Help() error { return nil } -// UsageString return usage string. +// UsageString returns usage string. func (c *Command) UsageString() string { - tmpOutput := c.output + // Storing normal writers + tmpOutput := c.outWriter + tmpErr := c.errWriter + bb := new(bytes.Buffer) - c.SetOutput(bb) + c.outWriter = bb + c.errWriter = bb + c.Usage() - c.output = tmpOutput + + // Setting things back to normal + c.outWriter = tmpOutput + c.errWriter = tmpErr + return bb.String() } @@ -1068,6 +1132,21 @@ func (c *Command) Printf(format string, i ...interface{}) { c.Print(fmt.Sprintf(format, i...)) } +// PrintErr is a convenience method to Print to the defined Err output, fallback to Stderr if not set. +func (c *Command) PrintErr(i ...interface{}) { + fmt.Fprint(c.ErrOrStderr(), i...) +} + +// PrintErrln is a convenience method to Println to the defined Err output, fallback to Stderr if not set. +func (c *Command) PrintErrln(i ...interface{}) { + c.Print(fmt.Sprintln(i...)) +} + +// PrintErrf is a convenience method to Printf to the defined Err output, fallback to Stderr if not set. +func (c *Command) PrintErrf(format string, i ...interface{}) { + c.Print(fmt.Sprintf(format, i...)) +} + // CommandPath returns the full path to this command. func (c *Command) CommandPath() string { if c.HasParent() { diff --git a/vendor/github.com/spf13/cobra/powershell_completions.go b/vendor/github.com/spf13/cobra/powershell_completions.go new file mode 100644 index 00000000..756c61b9 --- /dev/null +++ b/vendor/github.com/spf13/cobra/powershell_completions.go @@ -0,0 +1,100 @@ +// PowerShell completions are based on the amazing work from clap: +// https://github.com/clap-rs/clap/blob/3294d18efe5f264d12c9035f404c7d189d4824e1/src/completions/powershell.rs +// +// The generated scripts require PowerShell v5.0+ (which comes Windows 10, but +// can be downloaded separately for windows 7 or 8.1). + +package cobra + +import ( + "bytes" + "fmt" + "io" + "os" + "strings" + + "github.com/spf13/pflag" +) + +var powerShellCompletionTemplate = `using namespace System.Management.Automation +using namespace System.Management.Automation.Language +Register-ArgumentCompleter -Native -CommandName '%s' -ScriptBlock { + param($wordToComplete, $commandAst, $cursorPosition) + $commandElements = $commandAst.CommandElements + $command = @( + '%s' + for ($i = 1; $i -lt $commandElements.Count; $i++) { + $element = $commandElements[$i] + if ($element -isnot [StringConstantExpressionAst] -or + $element.StringConstantType -ne [StringConstantType]::BareWord -or + $element.Value.StartsWith('-')) { + break + } + $element.Value + } + ) -join ';' + $completions = @(switch ($command) {%s + }) + $completions.Where{ $_.CompletionText -like "$wordToComplete*" } | + Sort-Object -Property ListItemText +}` + +func generatePowerShellSubcommandCases(out io.Writer, cmd *Command, previousCommandName string) { + var cmdName string + if previousCommandName == "" { + cmdName = cmd.Name() + } else { + cmdName = fmt.Sprintf("%s;%s", previousCommandName, cmd.Name()) + } + + fmt.Fprintf(out, "\n '%s' {", cmdName) + + cmd.Flags().VisitAll(func(flag *pflag.Flag) { + if nonCompletableFlag(flag) { + return + } + usage := escapeStringForPowerShell(flag.Usage) + if len(flag.Shorthand) > 0 { + fmt.Fprintf(out, "\n [CompletionResult]::new('-%s', '%s', [CompletionResultType]::ParameterName, '%s')", flag.Shorthand, flag.Shorthand, usage) + } + fmt.Fprintf(out, "\n [CompletionResult]::new('--%s', '%s', [CompletionResultType]::ParameterName, '%s')", flag.Name, flag.Name, usage) + }) + + for _, subCmd := range cmd.Commands() { + usage := escapeStringForPowerShell(subCmd.Short) + fmt.Fprintf(out, "\n [CompletionResult]::new('%s', '%s', [CompletionResultType]::ParameterValue, '%s')", subCmd.Name(), subCmd.Name(), usage) + } + + fmt.Fprint(out, "\n break\n }") + + for _, subCmd := range cmd.Commands() { + generatePowerShellSubcommandCases(out, subCmd, cmdName) + } +} + +func escapeStringForPowerShell(s string) string { + return strings.Replace(s, "'", "''", -1) +} + +// GenPowerShellCompletion generates PowerShell completion file and writes to the passed writer. +func (c *Command) GenPowerShellCompletion(w io.Writer) error { + buf := new(bytes.Buffer) + + var subCommandCases bytes.Buffer + generatePowerShellSubcommandCases(&subCommandCases, c, "") + fmt.Fprintf(buf, powerShellCompletionTemplate, c.Name(), c.Name(), subCommandCases.String()) + + _, err := buf.WriteTo(w) + return err +} + +// GenPowerShellCompletionFile generates PowerShell completion file. +func (c *Command) GenPowerShellCompletionFile(filename string) error { + outFile, err := os.Create(filename) + if err != nil { + return err + } + defer outFile.Close() + + return c.GenPowerShellCompletion(outFile) +} diff --git a/vendor/github.com/spf13/cobra/powershell_completions.md b/vendor/github.com/spf13/cobra/powershell_completions.md new file mode 100644 index 00000000..afed8024 --- /dev/null +++ b/vendor/github.com/spf13/cobra/powershell_completions.md @@ -0,0 +1,14 @@ +# Generating PowerShell Completions For Your Own cobra.Command + +Cobra can generate PowerShell completion scripts. Users need PowerShell version 5.0 or above, which comes with Windows 10 and can be downloaded separately for Windows 7 or 8.1. They can then write the completions to a file and source this file from their PowerShell profile, which is referenced by the `$Profile` environment variable. See `Get-Help about_Profiles` for more info about PowerShell profiles. + +# What's supported + +- Completion for subcommands using their `.Short` description +- Completion for non-hidden flags using their `.Name` and `.Shorthand` + +# What's not yet supported + +- Command aliases +- Required, filename or custom flags (they will work like normal flags) +- Custom completion scripts diff --git a/vendor/github.com/spf13/cobra/shell_completions.go b/vendor/github.com/spf13/cobra/shell_completions.go new file mode 100644 index 00000000..ba0af9cb --- /dev/null +++ b/vendor/github.com/spf13/cobra/shell_completions.go @@ -0,0 +1,85 @@ +package cobra + +import ( + "github.com/spf13/pflag" +) + +// MarkFlagRequired adds the BashCompOneRequiredFlag annotation to the named flag if it exists, +// and causes your command to report an error if invoked without the flag. +func (c *Command) MarkFlagRequired(name string) error { + return MarkFlagRequired(c.Flags(), name) +} + +// MarkPersistentFlagRequired adds the BashCompOneRequiredFlag annotation to the named persistent flag if it exists, +// and causes your command to report an error if invoked without the flag. +func (c *Command) MarkPersistentFlagRequired(name string) error { + return MarkFlagRequired(c.PersistentFlags(), name) +} + +// MarkFlagRequired adds the BashCompOneRequiredFlag annotation to the named flag if it exists, +// and causes your command to report an error if invoked without the flag. +func MarkFlagRequired(flags *pflag.FlagSet, name string) error { + return flags.SetAnnotation(name, BashCompOneRequiredFlag, []string{"true"}) +} + +// MarkFlagFilename adds the BashCompFilenameExt annotation to the named flag, if it exists. +// Generated bash autocompletion will select filenames for the flag, limiting to named extensions if provided. +func (c *Command) MarkFlagFilename(name string, extensions ...string) error { + return MarkFlagFilename(c.Flags(), name, extensions...) +} + +// MarkFlagCustom adds the BashCompCustom annotation to the named flag, if it exists. +// Generated bash autocompletion will call the bash function f for the flag. +func (c *Command) MarkFlagCustom(name string, f string) error { + return MarkFlagCustom(c.Flags(), name, f) +} + +// MarkPersistentFlagFilename instructs the various shell completion +// implementations to limit completions for this persistent flag to the +// specified extensions (patterns). +// +// Shell Completion compatibility matrix: bash, zsh +func (c *Command) MarkPersistentFlagFilename(name string, extensions ...string) error { + return MarkFlagFilename(c.PersistentFlags(), name, extensions...) +} + +// MarkFlagFilename instructs the various shell completion implementations to +// limit completions for this flag to the specified extensions (patterns). +// +// Shell Completion compatibility matrix: bash, zsh +func MarkFlagFilename(flags *pflag.FlagSet, name string, extensions ...string) error { + return flags.SetAnnotation(name, BashCompFilenameExt, extensions) +} + +// MarkFlagCustom instructs the various shell completion implementations to +// limit completions for this flag to the specified extensions (patterns). +// +// Shell Completion compatibility matrix: bash, zsh +func MarkFlagCustom(flags *pflag.FlagSet, name string, f string) error { + return flags.SetAnnotation(name, BashCompCustom, []string{f}) +} + +// MarkFlagDirname instructs the various shell completion implementations to +// complete only directories with this named flag. +// +// Shell Completion compatibility matrix: zsh +func (c *Command) MarkFlagDirname(name string) error { + return MarkFlagDirname(c.Flags(), name) +} + +// MarkPersistentFlagDirname instructs the various shell completion +// implementations to complete only directories with this persistent named flag. +// +// Shell Completion compatibility matrix: zsh +func (c *Command) MarkPersistentFlagDirname(name string) error { + return MarkFlagDirname(c.PersistentFlags(), name) +} + +// MarkFlagDirname instructs the various shell completion implementations to +// complete only directories with this specified flag. +// +// Shell Completion compatibility matrix: zsh +func MarkFlagDirname(flags *pflag.FlagSet, name string) error { + zshPattern := "-(/)" + return flags.SetAnnotation(name, zshCompDirname, []string{zshPattern}) +} diff --git a/vendor/github.com/spf13/cobra/zsh_completions.go b/vendor/github.com/spf13/cobra/zsh_completions.go index e76d6071..12755482 100644 --- a/vendor/github.com/spf13/cobra/zsh_completions.go +++ b/vendor/github.com/spf13/cobra/zsh_completions.go @@ -1,14 +1,102 @@ package cobra import ( - "bytes" + "encoding/json" "fmt" "io" "os" "sort" "strings" + "text/template" + + "github.com/spf13/pflag" +) + +const ( + zshCompArgumentAnnotation = "cobra_annotations_zsh_completion_argument_annotation" + zshCompArgumentFilenameComp = "cobra_annotations_zsh_completion_argument_file_completion" + zshCompArgumentWordComp = "cobra_annotations_zsh_completion_argument_word_completion" + zshCompDirname = "cobra_annotations_zsh_dirname" +) + +var ( + zshCompFuncMap = template.FuncMap{ + "genZshFuncName": zshCompGenFuncName, + "extractFlags": zshCompExtractFlag, + "genFlagEntryForZshArguments": zshCompGenFlagEntryForArguments, + "extractArgsCompletions": zshCompExtractArgumentCompletionHintsForRendering, + } + zshCompletionText = ` +{{/* should accept Command (that contains subcommands) as parameter */}} +{{define "argumentsC" -}} +{{ $cmdPath := genZshFuncName .}} +function {{$cmdPath}} { + local -a commands + + _arguments -C \{{- range extractFlags .}} + {{genFlagEntryForZshArguments .}} \{{- end}} + "1: :->cmnds" \ + "*::arg:->args" + + case $state in + cmnds) + commands=({{range .Commands}}{{if not .Hidden}} + "{{.Name}}:{{.Short}}"{{end}}{{end}} + ) + _describe "command" commands + ;; + esac + + case "$words[1]" in {{- range .Commands}}{{if not .Hidden}} + {{.Name}}) + {{$cmdPath}}_{{.Name}} + ;;{{end}}{{end}} + esac +} +{{range .Commands}}{{if not .Hidden}} +{{template "selectCmdTemplate" .}} +{{- end}}{{end}} +{{- end}} + +{{/* should accept Command without subcommands as parameter */}} +{{define "arguments" -}} +function {{genZshFuncName .}} { +{{" _arguments"}}{{range extractFlags .}} \ + {{genFlagEntryForZshArguments . -}} +{{end}}{{range extractArgsCompletions .}} \ + {{.}}{{end}} +} +{{end}} + +{{/* dispatcher for commands with or without subcommands */}} +{{define "selectCmdTemplate" -}} +{{if .Hidden}}{{/* ignore hidden*/}}{{else -}} +{{if .Commands}}{{template "argumentsC" .}}{{else}}{{template "arguments" .}}{{end}} +{{- end}} +{{- end}} + +{{/* template entry point */}} +{{define "Main" -}} +#compdef _{{.Name}} {{.Name}} + +{{template "selectCmdTemplate" .}} +{{end}} +` ) +// zshCompArgsAnnotation is used to encode/decode zsh completion for +// arguments to/from Command.Annotations. +type zshCompArgsAnnotation map[int]zshCompArgHint + +type zshCompArgHint struct { + // Indicates the type of the completion to use. One of: + // zshCompArgumentFilenameComp or zshCompArgumentWordComp + Tipe string `json:"type"` + + // A value for the type above (globs for file completion or words) + Options []string `json:"options"` +} + // GenZshCompletionFile generates zsh completion file. func (c *Command) GenZshCompletionFile(filename string) error { outFile, err := os.Create(filename) @@ -20,116 +108,229 @@ func (c *Command) GenZshCompletionFile(filename string) error { return c.GenZshCompletion(outFile) } -// GenZshCompletion generates a zsh completion file and writes to the passed writer. +// GenZshCompletion generates a zsh completion file and writes to the passed +// writer. The completion always run on the root command regardless of the +// command it was called from. func (c *Command) GenZshCompletion(w io.Writer) error { - buf := new(bytes.Buffer) - - writeHeader(buf, c) - maxDepth := maxDepth(c) - writeLevelMapping(buf, maxDepth) - writeLevelCases(buf, maxDepth, c) + tmpl, err := template.New("Main").Funcs(zshCompFuncMap).Parse(zshCompletionText) + if err != nil { + return fmt.Errorf("error creating zsh completion template: %v", err) + } + return tmpl.Execute(w, c.Root()) +} - _, err := buf.WriteTo(w) - return err +// MarkZshCompPositionalArgumentFile marks the specified argument (first +// argument is 1) as completed by file selection. patterns (e.g. "*.txt") are +// optional - if not provided the completion will search for all files. +func (c *Command) MarkZshCompPositionalArgumentFile(argPosition int, patterns ...string) error { + if argPosition < 1 { + return fmt.Errorf("Invalid argument position (%d)", argPosition) + } + annotation, err := c.zshCompGetArgsAnnotations() + if err != nil { + return err + } + if c.zshcompArgsAnnotationnIsDuplicatePosition(annotation, argPosition) { + return fmt.Errorf("Duplicate annotation for positional argument at index %d", argPosition) + } + annotation[argPosition] = zshCompArgHint{ + Tipe: zshCompArgumentFilenameComp, + Options: patterns, + } + return c.zshCompSetArgsAnnotations(annotation) } -func writeHeader(w io.Writer, cmd *Command) { - fmt.Fprintf(w, "#compdef %s\n\n", cmd.Name()) +// MarkZshCompPositionalArgumentWords marks the specified positional argument +// (first argument is 1) as completed by the provided words. At east one word +// must be provided, spaces within words will be offered completion with +// "word\ word". +func (c *Command) MarkZshCompPositionalArgumentWords(argPosition int, words ...string) error { + if argPosition < 1 { + return fmt.Errorf("Invalid argument position (%d)", argPosition) + } + if len(words) == 0 { + return fmt.Errorf("Trying to set empty word list for positional argument %d", argPosition) + } + annotation, err := c.zshCompGetArgsAnnotations() + if err != nil { + return err + } + if c.zshcompArgsAnnotationnIsDuplicatePosition(annotation, argPosition) { + return fmt.Errorf("Duplicate annotation for positional argument at index %d", argPosition) + } + annotation[argPosition] = zshCompArgHint{ + Tipe: zshCompArgumentWordComp, + Options: words, + } + return c.zshCompSetArgsAnnotations(annotation) } -func maxDepth(c *Command) int { - if len(c.Commands()) == 0 { - return 0 +func zshCompExtractArgumentCompletionHintsForRendering(c *Command) ([]string, error) { + var result []string + annotation, err := c.zshCompGetArgsAnnotations() + if err != nil { + return nil, err } - maxDepthSub := 0 - for _, s := range c.Commands() { - subDepth := maxDepth(s) - if subDepth > maxDepthSub { - maxDepthSub = subDepth + for k, v := range annotation { + s, err := zshCompRenderZshCompArgHint(k, v) + if err != nil { + return nil, err } + result = append(result, s) } - return 1 + maxDepthSub + if len(c.ValidArgs) > 0 { + if _, positionOneExists := annotation[1]; !positionOneExists { + s, err := zshCompRenderZshCompArgHint(1, zshCompArgHint{ + Tipe: zshCompArgumentWordComp, + Options: c.ValidArgs, + }) + if err != nil { + return nil, err + } + result = append(result, s) + } + } + sort.Strings(result) + return result, nil } -func writeLevelMapping(w io.Writer, numLevels int) { - fmt.Fprintln(w, `_arguments \`) - for i := 1; i <= numLevels; i++ { - fmt.Fprintf(w, ` '%d: :->level%d' \`, i, i) - fmt.Fprintln(w) +func zshCompRenderZshCompArgHint(i int, z zshCompArgHint) (string, error) { + switch t := z.Tipe; t { + case zshCompArgumentFilenameComp: + var globs []string + for _, g := range z.Options { + globs = append(globs, fmt.Sprintf(`-g "%s"`, g)) + } + return fmt.Sprintf(`'%d: :_files %s'`, i, strings.Join(globs, " ")), nil + case zshCompArgumentWordComp: + var words []string + for _, w := range z.Options { + words = append(words, fmt.Sprintf("%q", w)) + } + return fmt.Sprintf(`'%d: :(%s)'`, i, strings.Join(words, " ")), nil + default: + return "", fmt.Errorf("Invalid zsh argument completion annotation: %s", t) } - fmt.Fprintf(w, ` '%d: :%s'`, numLevels+1, "_files") - fmt.Fprintln(w) } -func writeLevelCases(w io.Writer, maxDepth int, root *Command) { - fmt.Fprintln(w, "case $state in") - defer fmt.Fprintln(w, "esac") +func (c *Command) zshcompArgsAnnotationnIsDuplicatePosition(annotation zshCompArgsAnnotation, position int) bool { + _, dup := annotation[position] + return dup +} - for i := 1; i <= maxDepth; i++ { - fmt.Fprintf(w, " level%d)\n", i) - writeLevel(w, root, i) - fmt.Fprintln(w, " ;;") +func (c *Command) zshCompGetArgsAnnotations() (zshCompArgsAnnotation, error) { + annotation := make(zshCompArgsAnnotation) + annotationString, ok := c.Annotations[zshCompArgumentAnnotation] + if !ok { + return annotation, nil + } + err := json.Unmarshal([]byte(annotationString), &annotation) + if err != nil { + return annotation, fmt.Errorf("Error unmarshaling zsh argument annotation: %v", err) } - fmt.Fprintln(w, " *)") - fmt.Fprintln(w, " _arguments '*: :_files'") - fmt.Fprintln(w, " ;;") + return annotation, nil } -func writeLevel(w io.Writer, root *Command, i int) { - fmt.Fprintf(w, " case $words[%d] in\n", i) - defer fmt.Fprintln(w, " esac") - - commands := filterByLevel(root, i) - byParent := groupByParent(commands) +func (c *Command) zshCompSetArgsAnnotations(annotation zshCompArgsAnnotation) error { + jsn, err := json.Marshal(annotation) + if err != nil { + return fmt.Errorf("Error marshaling zsh argument annotation: %v", err) + } + if c.Annotations == nil { + c.Annotations = make(map[string]string) + } + c.Annotations[zshCompArgumentAnnotation] = string(jsn) + return nil +} - // sort the parents to keep a determinist order - parents := make([]string, len(byParent)) - j := 0 - for parent := range byParent { - parents[j] = parent - j++ +func zshCompGenFuncName(c *Command) string { + if c.HasParent() { + return zshCompGenFuncName(c.Parent()) + "_" + c.Name() } - sort.StringSlice(parents).Sort() + return "_" + c.Name() +} + +func zshCompExtractFlag(c *Command) []*pflag.Flag { + var flags []*pflag.Flag + c.LocalFlags().VisitAll(func(f *pflag.Flag) { + if !f.Hidden { + flags = append(flags, f) + } + }) + c.InheritedFlags().VisitAll(func(f *pflag.Flag) { + if !f.Hidden { + flags = append(flags, f) + } + }) + return flags +} - for _, parent := range parents { - names := names(byParent[parent]) - fmt.Fprintf(w, " %s)\n", parent) - fmt.Fprintf(w, " _arguments '%d: :(%s)'\n", i, strings.Join(names, " ")) - fmt.Fprintln(w, " ;;") +// zshCompGenFlagEntryForArguments returns an entry that matches _arguments +// zsh-completion parameters. It's too complicated to generate in a template. +func zshCompGenFlagEntryForArguments(f *pflag.Flag) string { + if f.Name == "" || f.Shorthand == "" { + return zshCompGenFlagEntryForSingleOptionFlag(f) } - fmt.Fprintln(w, " *)") - fmt.Fprintln(w, " _arguments '*: :_files'") - fmt.Fprintln(w, " ;;") + return zshCompGenFlagEntryForMultiOptionFlag(f) } -func filterByLevel(c *Command, l int) []*Command { - cs := make([]*Command, 0) - if l == 0 { - cs = append(cs, c) - return cs +func zshCompGenFlagEntryForSingleOptionFlag(f *pflag.Flag) string { + var option, multiMark, extras string + + if zshCompFlagCouldBeSpecifiedMoreThenOnce(f) { + multiMark = "*" } - for _, s := range c.Commands() { - cs = append(cs, filterByLevel(s, l-1)...) + + option = "--" + f.Name + if option == "--" { + option = "-" + f.Shorthand } - return cs + extras = zshCompGenFlagEntryExtras(f) + + return fmt.Sprintf(`'%s%s[%s]%s'`, multiMark, option, zshCompQuoteFlagDescription(f.Usage), extras) } -func groupByParent(commands []*Command) map[string][]*Command { - m := make(map[string][]*Command) - for _, c := range commands { - parent := c.Parent() - if parent == nil { - continue - } - m[parent.Name()] = append(m[parent.Name()], c) +func zshCompGenFlagEntryForMultiOptionFlag(f *pflag.Flag) string { + var options, parenMultiMark, curlyMultiMark, extras string + + if zshCompFlagCouldBeSpecifiedMoreThenOnce(f) { + parenMultiMark = "*" + curlyMultiMark = "\\*" } - return m + + options = fmt.Sprintf(`'(%s-%s %s--%s)'{%s-%s,%s--%s}`, + parenMultiMark, f.Shorthand, parenMultiMark, f.Name, curlyMultiMark, f.Shorthand, curlyMultiMark, f.Name) + extras = zshCompGenFlagEntryExtras(f) + + return fmt.Sprintf(`%s'[%s]%s'`, options, zshCompQuoteFlagDescription(f.Usage), extras) } -func names(commands []*Command) []string { - ns := make([]string, len(commands)) - for i, c := range commands { - ns[i] = c.Name() +func zshCompGenFlagEntryExtras(f *pflag.Flag) string { + if f.NoOptDefVal != "" { + return "" } - return ns + + extras := ":" // allow options for flag (even without assistance) + for key, values := range f.Annotations { + switch key { + case zshCompDirname: + extras = fmt.Sprintf(":filename:_files -g %q", values[0]) + case BashCompFilenameExt: + extras = ":filename:_files" + for _, pattern := range values { + extras = extras + fmt.Sprintf(` -g "%s"`, pattern) + } + } + } + + return extras +} + +func zshCompFlagCouldBeSpecifiedMoreThenOnce(f *pflag.Flag) bool { + return strings.Contains(f.Value.Type(), "Slice") || + strings.Contains(f.Value.Type(), "Array") +} + +func zshCompQuoteFlagDescription(s string) string { + return strings.Replace(s, "'", `'\''`, -1) } diff --git a/vendor/github.com/spf13/cobra/zsh_completions.md b/vendor/github.com/spf13/cobra/zsh_completions.md new file mode 100644 index 00000000..df9c2eac --- /dev/null +++ b/vendor/github.com/spf13/cobra/zsh_completions.md @@ -0,0 +1,39 @@ +## Generating Zsh Completion for your cobra.Command + +Cobra supports native Zsh completion generated from the root `cobra.Command`. +The generated completion script should be put somewhere in your `$fpath` named +`_<YOUR COMMAND>`. + +### What's Supported + +* Completion for all non-hidden subcommands using their `.Short` description. +* Completion for all non-hidden flags using the following rules: + * Filename completion works by marking the flag with `cmd.MarkFlagFilename...` + family of commands. + * The requirement for argument to the flag is decided by the `.NoOptDefVal` + flag value - if it's empty then completion will expect an argument. + * Flags of one of the various `*Array` and `*Slice` types supports multiple + specifications (with or without argument depending on the specific type). +* Completion of positional arguments using the following rules: + * Argument position for all options below starts at `1`. If argument position + `0` is requested it will raise an error. + * Use `command.MarkZshCompPositionalArgumentFile` to complete filenames. Glob + patterns (e.g. `"*.log"`) are optional - if not specified it will offer to + complete all file types. + * Use `command.MarkZshCompPositionalArgumentWords` to offer specific words for + completion. At least one word is required. + * It's possible to specify completion for some arguments and leave some + unspecified (e.g. offer words for second argument but nothing for first + argument). This will cause no completion for first argument but words + completion for second argument. + * If no argument completion was specified for 1st argument (but optionally was + specified for 2nd) and the command has `ValidArgs` it will be used as + completion options for 1st argument. + * Argument completions only offered for commands with no subcommands. + +### What's not yet Supported + +* Custom completion scripts are not supported yet (We should probably create zsh + specific one, doesn't make sense to re-use the bash one as the functions will + be different). +* Whatever other feature you're looking for and doesn't exist :) |