misc: generate PowerShell command completion

This commit is contained in:
Michael Muré 2019-06-23 14:31:27 +02:00
parent 7461c1fe3a
commit b64587f87a
No known key found for this signature in database
GPG Key ID: A4457C029293126F
17 changed files with 1271 additions and 210 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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)
}
}

View File

@ -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 {

View File

@ -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
}

View File

@ -1,46 +1,396 @@
#compdef git-bug
#compdef _git-bug 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'
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
;;
level2)
case $words[2] in
bridge)
_arguments '2: :(configure pull rm)'
_git-bug_bridge
;;
commands)
_git-bug_commands
;;
comment)
_arguments '2: :(add)'
_git-bug_comment
;;
deselect)
_git-bug_deselect
;;
label)
_arguments '2: :(add rm)'
_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)
_arguments '2: :(close open)'
_git-bug_status
;;
termui)
_git-bug_termui
;;
title)
_arguments '2: :(edit)'
_git-bug_title
;;
user)
_arguments '2: :(adopt create ls)'
_git-bug_user
;;
*)
_arguments '*: :_files'
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
;;
*)
_arguments '*: :_files'
esac
case "$words[1]" in
configure)
_git-bug_bridge_configure
;;
esac
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)]:'
}

View File

@ -34,3 +34,5 @@ tags
*.exe
cobra.test
.idea/*

View File

@ -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

View File

@ -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})
}

View File

@ -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() {

100
vendor/github.com/spf13/cobra/powershell_completions.go generated vendored Normal file
View File

@ -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)
}

View File

@ -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

85
vendor/github.com/spf13/cobra/shell_completions.go generated vendored Normal file
View File

@ -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})
}

View File

@ -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)
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())
}
writeHeader(buf, c)
maxDepth := maxDepth(c)
writeLevelMapping(buf, maxDepth)
writeLevelCases(buf, maxDepth, c)
_, err := buf.WriteTo(w)
// 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)
}
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)
}
}
return 1 + maxDepthSub
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")
for i := 1; i <= maxDepth; i++ {
fmt.Fprintf(w, " level%d)\n", i)
writeLevel(w, root, i)
fmt.Fprintln(w, " ;;")
}
fmt.Fprintln(w, " *)")
fmt.Fprintln(w, " _arguments '*: :_files'")
fmt.Fprintln(w, " ;;")
func (c *Command) zshcompArgsAnnotationnIsDuplicatePosition(annotation zshCompArgsAnnotation, position int) bool {
_, dup := annotation[position]
return dup
}
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)
// sort the parents to keep a determinist order
parents := make([]string, len(byParent))
j := 0
for parent := range byParent {
parents[j] = parent
j++
func (c *Command) zshCompGetArgsAnnotations() (zshCompArgsAnnotation, error) {
annotation := make(zshCompArgsAnnotation)
annotationString, ok := c.Annotations[zshCompArgumentAnnotation]
if !ok {
return annotation, nil
}
sort.StringSlice(parents).Sort()
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, " ;;")
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 filterByLevel(c *Command, l int) []*Command {
cs := make([]*Command, 0)
if l == 0 {
cs = append(cs, c)
return cs
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)
}
for _, s := range c.Commands() {
cs = append(cs, filterByLevel(s, l-1)...)
if c.Annotations == nil {
c.Annotations = make(map[string]string)
}
return cs
c.Annotations[zshCompArgumentAnnotation] = string(jsn)
return nil
}
func groupByParent(commands []*Command) map[string][]*Command {
m := make(map[string][]*Command)
for _, c := range commands {
parent := c.Parent()
if parent == nil {
continue
func zshCompGenFuncName(c *Command) string {
if c.HasParent() {
return zshCompGenFuncName(c.Parent()) + "_" + c.Name()
}
m[parent.Name()] = append(m[parent.Name()], c)
}
return m
return "_" + c.Name()
}
func names(commands []*Command) []string {
ns := make([]string, len(commands))
for i, c := range commands {
ns[i] = 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)
}
return ns
})
c.InheritedFlags().VisitAll(func(f *pflag.Flag) {
if !f.Hidden {
flags = append(flags, f)
}
})
return flags
}
// 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)
}
return zshCompGenFlagEntryForMultiOptionFlag(f)
}
func zshCompGenFlagEntryForSingleOptionFlag(f *pflag.Flag) string {
var option, multiMark, extras string
if zshCompFlagCouldBeSpecifiedMoreThenOnce(f) {
multiMark = "*"
}
option = "--" + f.Name
if option == "--" {
option = "-" + f.Shorthand
}
extras = zshCompGenFlagEntryExtras(f)
return fmt.Sprintf(`'%s%s[%s]%s'`, multiMark, option, zshCompQuoteFlagDescription(f.Usage), extras)
}
func zshCompGenFlagEntryForMultiOptionFlag(f *pflag.Flag) string {
var options, parenMultiMark, curlyMultiMark, extras string
if zshCompFlagCouldBeSpecifiedMoreThenOnce(f) {
parenMultiMark = "*"
curlyMultiMark = "\\*"
}
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 zshCompGenFlagEntryExtras(f *pflag.Flag) string {
if f.NoOptDefVal != "" {
return ""
}
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)
}

39
vendor/github.com/spf13/cobra/zsh_completions.md generated vendored Normal file
View File

@ -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 :)