From 8af3be87e47a74b191c52f4a35b134e0abf627aa Mon Sep 17 00:00:00 2001 From: "Dr. Stefan Schimanski" Date: Sun, 20 Mar 2016 19:09:12 +0100 Subject: [PATCH] Update cobra godep --- Godeps/Godeps.json | 2 +- .../src/github.com/spf13/cobra/.travis.yml | 6 +- .../src/github.com/spf13/cobra/README.md | 48 +-- .../spf13/cobra/bash_completions.go | 335 +++++++++++++----- .../spf13/cobra/bash_completions.md | 61 +++- .../src/github.com/spf13/cobra/cobra.go | 31 +- .../spf13/cobra/cobra/cmd/helpers.go | 6 +- .../github.com/spf13/cobra/cobra/cmd/init.go | 12 +- .../github.com/spf13/cobra/cobra/cmd/root.go | 2 +- .../src/github.com/spf13/cobra/command.go | 95 +++-- .../github.com/spf13/cobra/command_notwin.go | 5 + .../src/github.com/spf13/cobra/command_win.go | 26 ++ .../spf13/cobra/{ => doc}/man_docs.go | 93 +++-- .../spf13/cobra/{ => doc}/man_docs.md | 3 +- .../src/github.com/spf13/cobra/doc/md_docs.go | 175 +++++++++ .../spf13/cobra/{ => doc}/md_docs.md | 43 ++- .../spf13/cobra/{doc_util.go => doc/util.go} | 20 +- .../src/github.com/spf13/cobra/md_docs.go | 162 --------- 18 files changed, 714 insertions(+), 411 deletions(-) create mode 100644 Godeps/_workspace/src/github.com/spf13/cobra/command_notwin.go create mode 100644 Godeps/_workspace/src/github.com/spf13/cobra/command_win.go rename Godeps/_workspace/src/github.com/spf13/cobra/{ => doc}/man_docs.go (72%) rename Godeps/_workspace/src/github.com/spf13/cobra/{ => doc}/man_docs.md (85%) create mode 100644 Godeps/_workspace/src/github.com/spf13/cobra/doc/md_docs.go rename Godeps/_workspace/src/github.com/spf13/cobra/{ => doc}/md_docs.md (73%) rename Godeps/_workspace/src/github.com/spf13/cobra/{doc_util.go => doc/util.go} (69%) delete mode 100644 Godeps/_workspace/src/github.com/spf13/cobra/md_docs.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 603c01cd1fa..dda701c7b2b 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -980,7 +980,7 @@ }, { "ImportPath": "github.com/spf13/cobra", - "Rev": "1c44ec8d3f1552cac48999f9306da23c4d8a288b" + "Rev": "4c05eb1145f16d0e6bb4a3e1b6d769f4713cb41f" }, { "ImportPath": "github.com/spf13/pflag", diff --git a/Godeps/_workspace/src/github.com/spf13/cobra/.travis.yml b/Godeps/_workspace/src/github.com/spf13/cobra/.travis.yml index 7a6cb7fdbe4..e11869ba320 100644 --- a/Godeps/_workspace/src/github.com/spf13/cobra/.travis.yml +++ b/Godeps/_workspace/src/github.com/spf13/cobra/.travis.yml @@ -4,6 +4,10 @@ go: - 1.4.2 - 1.5.1 - tip +before_install: + - mkdir -p bin + - curl -Lso bin/shellcheck https://github.com/caarlos0/shellcheck-docker/releases/download/v0.4.3/shellcheck + - chmod +x bin/shellcheck script: - - go test -v ./... + - PATH=$PATH:$PWD/bin go test -v ./... - go build diff --git a/Godeps/_workspace/src/github.com/spf13/cobra/README.md b/Godeps/_workspace/src/github.com/spf13/cobra/README.md index caeebb9dc64..f326c7fa843 100644 --- a/Godeps/_workspace/src/github.com/spf13/cobra/README.md +++ b/Godeps/_workspace/src/github.com/spf13/cobra/README.md @@ -7,6 +7,7 @@ Many of the most widely used Go projects are built using Cobra including: * [Kubernetes](http://kubernetes.io/) * [Hugo](http://gohugo.io) * [rkt](https://github.com/coreos/rkt) +* [etcd](https://github.com/coreos/etcd) * [Docker (distribution)](https://github.com/docker/distribution) * [OpenShift](https://www.openshift.com/) * [Delve](https://github.com/derekparker/delve) @@ -15,9 +16,11 @@ Many of the most widely used Go projects are built using Cobra including: * [Bleve](http://www.blevesearch.com/) * [ProjectAtomic (enterprise)](http://www.projectatomic.io/) * [Parse (CLI)](https://parse.com/) +* [GiantSwarm's swarm](https://github.com/giantswarm/cli) +* [Nanobox](https://github.com/nanobox-io/nanobox)/[Nanopack](https://github.com/nanopack) -[![Build Status](https://travis-ci.org/spf13/cobra.svg "Travis CI status")](https://travis-ci.org/spf13/cobra) +[![Build Status](https://travis-ci.org/spf13/cobra.svg "Travis CI status")](https://travis-ci.org/spf13/cobra) [![CircleCI status](https://circleci.com/gh/spf13/cobra.png?circle-token=:circle-token "CircleCI status")](https://circleci.com/gh/spf13/cobra) ![cobra](https://cloud.githubusercontent.com/assets/173412/10911369/84832a8e-8212-11e5-9f82-cc96660a4794.gif) @@ -65,7 +68,7 @@ Cobra is built on a structure of commands, arguments & flags. The best applications will read like sentences when used. Users will know how to use the application because they will natively understand how to use it. -The pattern to follow is +The pattern to follow is `APPNAME VERB NOUN --ADJECTIVE.` or `APPNAME COMMAND ARG --FLAG` @@ -74,7 +77,7 @@ A few good real world examples may better illustrate this point. In the following example, 'server' is a command, and 'port' is a flag: - > hugo serve --port=1313 + > hugo server --port=1313 In this command we are telling Git to clone the url bare. @@ -199,7 +202,7 @@ cobra add config cobra add create -p 'configCmd' ``` -Once you have run these four commands you would have an app structure that would look like: +Once you have run these three commands you would have an app structure that would look like: ``` ▾ app/ @@ -329,7 +332,7 @@ var versionCmd = &cobra.Command{ } ``` -### Attach command to its parent +### Attach command to its parent If you notice in the above example we attach the command to its parent. In @@ -473,18 +476,18 @@ The following output is automatically generated by Cobra. Nothing beyond the command and flag definitions are needed. > hugo help - + hugo is the main command, used to build your Hugo site. - + Hugo is a Fast and Flexible Static Site Generator built with love by spf13 and friends in Go. - + Complete documentation is available at http://gohugo.io/. - + Usage: hugo [flags] hugo [command] - + Available Commands: server Hugo runs its own webserver to render the files version Print the version number of Hugo @@ -499,7 +502,7 @@ command and flag definitions are needed. gendoc Generate Markdown documentation for the Hugo CLI. genman Generate man page for Hugo import Import your site from others. - + Flags: -b, --baseURL="": hostname (and path) to the root, e.g. http://spf13.com/ -D, --buildDrafts[=false]: include content marked as draft @@ -524,7 +527,7 @@ command and flag definitions are needed. -v, --verbose[=false]: verbose output --verboseLog[=false]: verbose logging -w, --watch[=false]: watch filesystem for changes and recreate as needed - + Use "hugo [command] --help" for more information about a command. @@ -533,7 +536,7 @@ around it. In fact, you can provide your own if you want. ### Defining your own help -You can provide your own Help command or you own template for the default command to use. +You can provide your own Help command or your own template for the default command to use. The default help command is @@ -576,7 +579,7 @@ embeds the usage as part of its output. Usage: hugo [flags] hugo [command] - + Available Commands: server Hugo runs its own webserver to render the files version Print the version number of Hugo @@ -591,7 +594,7 @@ embeds the usage as part of its output. gendoc Generate Markdown documentation for the Hugo CLI. genman Generate man page for Hugo import Import your site from others. - + Flags: -b, --baseURL="": hostname (and path) to the root, e.g. http://spf13.com/ -D, --buildDrafts[=false]: include content marked as draft @@ -710,7 +713,8 @@ func main() { ## Alternative Error Handling -Cobra also has functions where the return signature is an error. This allows for errors to bubble up to the top, providing a way to handle the errors in one location. The current list of functions that return an error is: +Cobra also has functions where the return signature is an error. This allows for errors to bubble up to the top, +providing a way to handle the errors in one location. The current list of functions that return an error is: * PersistentPreRunE * PreRunE @@ -718,6 +722,10 @@ Cobra also has functions where the return signature is an error. This allows for * PostRunE * PersistentPostRunE +If you would like to silence the default `error` and `usage` output in favor of your own, you can set `SilenceUsage` +and `SilenceErrors` to `false` on the command. A child command respects these flags if they are set on the parent +command. + **Example Usage using RunE:** ```go @@ -791,11 +799,11 @@ Run 'kubectl help' for usage. ## Generating Markdown-formatted documentation for your command -Cobra can generate a Markdown-formatted document based on the subcommands, flags, etc. A simple example of how to do this for your command can be found in [Markdown Docs](md_docs.md). +Cobra can generate a Markdown-formatted document based on the subcommands, flags, etc. A simple example of how to do this for your command can be found in [Markdown Docs](doc/md_docs.md). ## Generating man pages for your command -Cobra can generate a man page based on the subcommands, flags, etc. A simple example of how to do this for your command can be found in [Man Docs](man_docs.md). +Cobra can generate a man page based on the subcommands, flags, etc. A simple example of how to do this for your command can be found in [Man Docs](doc/man_docs.md). ## Generating bash completions for your command @@ -857,8 +865,8 @@ Libraries for extending Cobra: Names in no particular order: -* [spf13](https://github.com/spf13), -[eparis](https://github.com/eparis), +* [spf13](https://github.com/spf13), +[eparis](https://github.com/eparis), [bep](https://github.com/bep), and many more! ## License diff --git a/Godeps/_workspace/src/github.com/spf13/cobra/bash_completions.go b/Godeps/_workspace/src/github.com/spf13/cobra/bash_completions.go index 965bfdb036f..3f33bb0ec8e 100644 --- a/Godeps/_workspace/src/github.com/spf13/cobra/bash_completions.go +++ b/Godeps/_workspace/src/github.com/spf13/cobra/bash_completions.go @@ -1,8 +1,8 @@ package cobra import ( - "bytes" "fmt" + "io" "os" "sort" "strings" @@ -12,13 +12,17 @@ import ( const ( BashCompFilenameExt = "cobra_annotation_bash_completion_filename_extentions" + BashCompCustom = "cobra_annotation_bash_completion_custom" BashCompOneRequiredFlag = "cobra_annotation_bash_completion_one_required_flag" BashCompSubdirsInDir = "cobra_annotation_bash_completion_subdirs_in_dir" ) -func preamble(out *bytes.Buffer) { - fmt.Fprintf(out, `#!/bin/bash - +func preamble(out io.Writer, name string) error { + _, err := fmt.Fprintf(out, "# bash completion for %-36s -*- shell-script -*-\n", name) + if err != nil { + return err + } + _, err = fmt.Fprint(out, ` __debug() { if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then @@ -31,7 +35,7 @@ __debug() __my_init_completion() { COMPREPLY=() - _get_comp_words_by_ref cur prev words cword + _get_comp_words_by_ref "$@" cur prev words cword } __index_of_word() @@ -57,7 +61,7 @@ __contains_word() __handle_reply() { - __debug "${FUNCNAME}" + __debug "${FUNCNAME[0]}" case $cur in -*) if [[ $(type -t compopt) = "builtin" ]]; then @@ -71,7 +75,28 @@ __handle_reply() fi COMPREPLY=( $(compgen -W "${allflags[*]}" -- "$cur") ) if [[ $(type -t compopt) = "builtin" ]]; then - [[ $COMPREPLY == *= ]] || compopt +o nospace + [[ "${COMPREPLY[0]}" == *= ]] || compopt +o nospace + fi + + # complete after --flag=abc + if [[ $cur == *=* ]]; then + if [[ $(type -t compopt) = "builtin" ]]; then + compopt +o nospace + fi + + local index flag + flag="${cur%%=*}" + __index_of_word "${flag}" "${flags_with_completion[@]}" + if [[ ${index} -ge 0 ]]; then + COMPREPLY=() + PREFIX="" + cur="${cur#*=}" + ${flags_completion[${index}]} + if [ -n "${ZSH_VERSION}" ]; then + # zfs completion needs --flag= prefix + eval "COMPREPLY=( \"\${COMPREPLY[@]/#/${flag}=}\" )" + fi + fi fi return 0; ;; @@ -100,9 +125,15 @@ __handle_reply() fi COMPREPLY=( $(compgen -W "${completions[*]}" -- "$cur") ) + if [[ ${#COMPREPLY[@]} -eq 0 && ${#noun_aliases[@]} -gt 0 && ${#must_have_one_noun[@]} -ne 0 ]]; then + COMPREPLY=( $(compgen -W "${noun_aliases[*]}" -- "$cur") ) + fi + if [[ ${#COMPREPLY[@]} -eq 0 ]]; then declare -F __custom_func >/dev/null && __custom_func fi + + __ltrim_colon_completions "$cur" } # The arguments should be in the form "ext1|ext2|extn" @@ -120,20 +151,31 @@ __handle_subdirs_in_dir_flag() __handle_flag() { - __debug "${FUNCNAME}: c is $c words[c] is ${words[c]}" + __debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" # if a command required a flag, and we found it, unset must_have_one_flag() local flagname=${words[c]} + local flagvalue # if the word contained an = if [[ ${words[c]} == *"="* ]]; then + flagvalue=${flagname#*=} # take in as flagvalue after the = flagname=${flagname%%=*} # strip everything after the = flagname="${flagname}=" # but put the = back fi - __debug "${FUNCNAME}: looking for ${flagname}" + __debug "${FUNCNAME[0]}: looking for ${flagname}" if __contains_word "${flagname}" "${must_have_one_flag[@]}"; then must_have_one_flag=() fi + # keep flag value with flagname as flaghash + if [ -n "${flagvalue}" ] ; then + flaghash[${flagname}]=${flagvalue} + elif [ -n "${words[ $((c+1)) ]}" ] ; then + flaghash[${flagname}]=${words[ $((c+1)) ]} + else + flaghash[${flagname}]="true" # pad "true" for bool flag + fi + # skip the argument to a two word flag if __contains_word "${words[c]}" "${two_word_flags[@]}"; then c=$((c+1)) @@ -143,17 +185,18 @@ __handle_flag() fi fi - # skip the flag itself c=$((c+1)) } __handle_noun() { - __debug "${FUNCNAME}: c is $c words[c] is ${words[c]}" + __debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" if __contains_word "${words[c]}" "${must_have_one_noun[@]}"; then must_have_one_noun=() + elif __contains_word "${words[c]}" "${noun_aliases[@]}"; then + must_have_one_noun=() fi nouns+=("${words[c]}") @@ -162,16 +205,20 @@ __handle_noun() __handle_command() { - __debug "${FUNCNAME}: c is $c words[c] is ${words[c]}" + __debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" local next_command if [[ -n ${last_command} ]]; then - next_command="_${last_command}_${words[c]}" + next_command="_${last_command}_${words[c]//:/__}" else - next_command="_${words[c]}" + if [[ $c -eq 0 ]]; then + next_command="_$(basename "${words[c]//:/__}")" + else + next_command="_${words[c]//:/__}" + fi fi c=$((c+1)) - __debug "${FUNCNAME}: looking for ${next_command}" + __debug "${FUNCNAME[0]}: looking for ${next_command}" declare -F $next_command >/dev/null && $next_command } @@ -181,11 +228,13 @@ __handle_word() __handle_reply return fi - __debug "${FUNCNAME}: c is $c words[c] is ${words[c]}" + __debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" if [[ "${words[c]}" == -* ]]; then __handle_flag elif __contains_word "${words[c]}" "${commands[@]}"; then __handle_command + elif [[ $c -eq 0 ]] && __contains_word "$(basename "${words[c]}")" "${commands[@]}"; then + __handle_command else __handle_noun fi @@ -193,16 +242,22 @@ __handle_word() } `) + return err } -func postscript(out *bytes.Buffer, name string) { - fmt.Fprintf(out, "__start_%s()\n", name) - fmt.Fprintf(out, `{ +func postscript(w io.Writer, name string) error { + name = strings.Replace(name, ":", "__", -1) + _, err := fmt.Fprintf(w, "__start_%s()\n", name) + if err != nil { + return err + } + _, err = fmt.Fprintf(w, `{ local cur prev words cword + declare -A flaghash 2>/dev/null || : if declare -F _init_completion >/dev/null 2>&1; then _init_completion -s || return else - __my_init_completion || return + __my_init_completion -n "=" || return fi local c=0 @@ -220,55 +275,91 @@ func postscript(out *bytes.Buffer, name string) { } `, name) - fmt.Fprintf(out, `if [[ $(type -t compopt) = "builtin" ]]; then - complete -F __start_%s %s + if err != nil { + return err + } + _, err = fmt.Fprintf(w, `if [[ $(type -t compopt) = "builtin" ]]; then + complete -o default -F __start_%s %s else - complete -o nospace -F __start_%s %s + complete -o default -o nospace -F __start_%s %s fi `, name, name, name, name) - fmt.Fprintf(out, "# ex: ts=4 sw=4 et filetype=sh\n") + if err != nil { + return err + } + _, err = fmt.Fprintf(w, "# ex: ts=4 sw=4 et filetype=sh\n") + return err } -func writeCommands(cmd *Command, out *bytes.Buffer) { - fmt.Fprintf(out, " commands=()\n") +func writeCommands(cmd *Command, w io.Writer) error { + if _, err := fmt.Fprintf(w, " commands=()\n"); err != nil { + return err + } for _, c := range cmd.Commands() { if !c.IsAvailableCommand() || c == cmd.helpCommand { continue } - fmt.Fprintf(out, " commands+=(%q)\n", c.Name()) + if _, err := fmt.Fprintf(w, " commands+=(%q)\n", c.Name()); err != nil { + return err + } } - fmt.Fprintf(out, "\n") + _, err := fmt.Fprintf(w, "\n") + return err } -func writeFlagHandler(name string, annotations map[string][]string, out *bytes.Buffer) { +func writeFlagHandler(name string, annotations map[string][]string, w io.Writer) error { for key, value := range annotations { switch key { case BashCompFilenameExt: - fmt.Fprintf(out, " flags_with_completion+=(%q)\n", name) + _, err := fmt.Fprintf(w, " flags_with_completion+=(%q)\n", name) + if err != nil { + return err + } if len(value) > 0 { ext := "__handle_filename_extension_flag " + strings.Join(value, "|") - fmt.Fprintf(out, " flags_completion+=(%q)\n", ext) + _, err = fmt.Fprintf(w, " flags_completion+=(%q)\n", ext) } else { ext := "_filedir" - fmt.Fprintf(out, " flags_completion+=(%q)\n", ext) + _, err = fmt.Fprintf(w, " flags_completion+=(%q)\n", ext) + } + if err != nil { + return err + } + case BashCompCustom: + _, err := fmt.Fprintf(w, " flags_with_completion+=(%q)\n", name) + if err != nil { + return err + } + if len(value) > 0 { + handlers := strings.Join(value, "; ") + _, err = fmt.Fprintf(w, " flags_completion+=(%q)\n", handlers) + } else { + _, err = fmt.Fprintf(w, " flags_completion+=(:)\n") + } + if err != nil { + return err } case BashCompSubdirsInDir: - fmt.Fprintf(out, " flags_with_completion+=(%q)\n", name) + _, err := fmt.Fprintf(w, " flags_with_completion+=(%q)\n", name) if len(value) == 1 { ext := "__handle_subdirs_in_dir_flag " + value[0] - fmt.Fprintf(out, " flags_completion+=(%q)\n", ext) + _, err = fmt.Fprintf(w, " flags_completion+=(%q)\n", ext) } else { ext := "_filedir -d" - fmt.Fprintf(out, " flags_completion+=(%q)\n", ext) + _, err = fmt.Fprintf(w, " flags_completion+=(%q)\n", ext) + } + if err != nil { + return err } } } + return nil } -func writeShortFlag(flag *pflag.Flag, out *bytes.Buffer) { +func writeShortFlag(flag *pflag.Flag, w io.Writer) error { b := (flag.Value.Type() == "bool") name := flag.Shorthand format := " " @@ -276,11 +367,13 @@ func writeShortFlag(flag *pflag.Flag, out *bytes.Buffer) { format += "two_word_" } format += "flags+=(\"-%s\")\n" - fmt.Fprintf(out, format, name) - writeFlagHandler("-"+name, flag.Annotations, out) + if _, err := fmt.Fprintf(w, format, name); err != nil { + return err + } + return writeFlagHandler("-"+name, flag.Annotations, w) } -func writeFlag(flag *pflag.Flag, out *bytes.Buffer) { +func writeFlag(flag *pflag.Flag, w io.Writer) error { b := (flag.Value.Type() == "bool") name := flag.Name format := " flags+=(\"--%s" @@ -288,36 +381,64 @@ func writeFlag(flag *pflag.Flag, out *bytes.Buffer) { format += "=" } format += "\")\n" - fmt.Fprintf(out, format, name) - writeFlagHandler("--"+name, flag.Annotations, out) + if _, err := fmt.Fprintf(w, format, name); err != nil { + return err + } + return writeFlagHandler("--"+name, flag.Annotations, w) } -func writeFlags(cmd *Command, out *bytes.Buffer) { - fmt.Fprintf(out, ` flags=() +func writeFlags(cmd *Command, w io.Writer) error { + _, err := fmt.Fprintf(w, ` flags=() two_word_flags=() flags_with_completion=() flags_completion=() `) + if err != nil { + return err + } + var visitErr error cmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) { - writeFlag(flag, out) + if err := writeFlag(flag, w); err != nil { + visitErr = err + return + } if len(flag.Shorthand) > 0 { - writeShortFlag(flag, out) + if err := writeShortFlag(flag, w); err != nil { + visitErr = err + return + } } }) + if visitErr != nil { + return visitErr + } cmd.InheritedFlags().VisitAll(func(flag *pflag.Flag) { - writeFlag(flag, out) + if err := writeFlag(flag, w); err != nil { + visitErr = err + return + } if len(flag.Shorthand) > 0 { - writeShortFlag(flag, out) + if err := writeShortFlag(flag, w); err != nil { + visitErr = err + return + } } }) + if visitErr != nil { + return visitErr + } - fmt.Fprintf(out, "\n") + _, err = fmt.Fprintf(w, "\n") + return err } -func writeRequiredFlag(cmd *Command, out *bytes.Buffer) { - fmt.Fprintf(out, " must_have_one_flag=()\n") +func writeRequiredFlag(cmd *Command, w io.Writer) error { + if _, err := fmt.Fprintf(w, " must_have_one_flag=()\n"); err != nil { + return err + } flags := cmd.NonInheritedFlags() + var visitErr error flags.VisitAll(func(flag *pflag.Flag) { for key := range flag.Annotations { switch key { @@ -328,67 +449,111 @@ func writeRequiredFlag(cmd *Command, out *bytes.Buffer) { format += "=" } format += "\")\n" - fmt.Fprintf(out, format, flag.Name) + if _, err := fmt.Fprintf(w, format, flag.Name); err != nil { + visitErr = err + return + } if len(flag.Shorthand) > 0 { - fmt.Fprintf(out, " must_have_one_flag+=(\"-%s\")\n", flag.Shorthand) + if _, err := fmt.Fprintf(w, " must_have_one_flag+=(\"-%s\")\n", flag.Shorthand); err != nil { + visitErr = err + return + } } } } }) + return visitErr } -func writeRequiredNoun(cmd *Command, out *bytes.Buffer) { - fmt.Fprintf(out, " must_have_one_noun=()\n") +func writeRequiredNouns(cmd *Command, w io.Writer) error { + if _, err := fmt.Fprintf(w, " must_have_one_noun=()\n"); err != nil { + return err + } sort.Sort(sort.StringSlice(cmd.ValidArgs)) for _, value := range cmd.ValidArgs { - fmt.Fprintf(out, " must_have_one_noun+=(%q)\n", value) + if _, err := fmt.Fprintf(w, " must_have_one_noun+=(%q)\n", value); err != nil { + return err + } } + return nil } -func gen(cmd *Command, out *bytes.Buffer) { +func writeArgAliases(cmd *Command, w io.Writer) error { + if _, err := fmt.Fprintf(w, " noun_aliases=()\n"); err != nil { + return err + } + sort.Sort(sort.StringSlice(cmd.ArgAliases)) + for _, value := range cmd.ArgAliases { + if _, err := fmt.Fprintf(w, " noun_aliases+=(%q)\n", value); err != nil { + return err + } + } + return nil +} + +func gen(cmd *Command, w io.Writer) error { for _, c := range cmd.Commands() { if !c.IsAvailableCommand() || c == cmd.helpCommand { continue } - gen(c, out) + if err := gen(c, w); err != nil { + return err + } } commandName := cmd.CommandPath() commandName = strings.Replace(commandName, " ", "_", -1) - fmt.Fprintf(out, "_%s()\n{\n", commandName) - fmt.Fprintf(out, " last_command=%q\n", commandName) - writeCommands(cmd, out) - writeFlags(cmd, out) - writeRequiredFlag(cmd, out) - writeRequiredNoun(cmd, out) - fmt.Fprintf(out, "}\n\n") + commandName = strings.Replace(commandName, ":", "__", -1) + if _, err := fmt.Fprintf(w, "_%s()\n{\n", commandName); err != nil { + return err + } + if _, err := fmt.Fprintf(w, " last_command=%q\n", commandName); err != nil { + return err + } + if err := writeCommands(cmd, w); err != nil { + return err + } + if err := writeFlags(cmd, w); err != nil { + return err + } + if err := writeRequiredFlag(cmd, w); err != nil { + return err + } + if err := writeRequiredNouns(cmd, w); err != nil { + return err + } + if err := writeArgAliases(cmd, w); err != nil { + return err + } + if _, err := fmt.Fprintf(w, "}\n\n"); err != nil { + return err + } + return nil } -func (cmd *Command) GenBashCompletion(out *bytes.Buffer) { - preamble(out) - if len(cmd.BashCompletionFunction) > 0 { - fmt.Fprintf(out, "%s\n", cmd.BashCompletionFunction) +func (cmd *Command) GenBashCompletion(w io.Writer) error { + if err := preamble(w, cmd.Name()); err != nil { + return err } - gen(cmd, out) - postscript(out, cmd.Name()) + if len(cmd.BashCompletionFunction) > 0 { + if _, err := fmt.Fprintf(w, "%s\n", cmd.BashCompletionFunction); err != nil { + return err + } + } + if err := gen(cmd, w); err != nil { + return err + } + return postscript(w, cmd.Name()) } func (cmd *Command) GenBashCompletionFile(filename string) error { - out := new(bytes.Buffer) - - cmd.GenBashCompletion(out) - outFile, err := os.Create(filename) if err != nil { return err } defer outFile.Close() - _, err = outFile.Write(out.Bytes()) - if err != nil { - return err - } - return nil + return cmd.GenBashCompletion(outFile) } // MarkFlagRequired adds the BashCompOneRequiredFlag annotation to the named flag, if it exists. @@ -412,6 +577,12 @@ func (cmd *Command) MarkFlagFilename(name string, extensions ...string) error { return MarkFlagFilename(cmd.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 (cmd *Command) MarkFlagCustom(name string, f string) error { + return MarkFlagCustom(cmd.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 (cmd *Command) MarkPersistentFlagFilename(name string, extensions ...string) error { @@ -423,3 +594,9 @@ func (cmd *Command) MarkPersistentFlagFilename(name string, extensions ...string 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/Godeps/_workspace/src/github.com/spf13/cobra/bash_completions.md b/Godeps/_workspace/src/github.com/spf13/cobra/bash_completions.md index 204704efc14..84d5b012f41 100644 --- a/Godeps/_workspace/src/github.com/spf13/cobra/bash_completions.md +++ b/Godeps/_workspace/src/github.com/spf13/cobra/bash_completions.md @@ -80,7 +80,7 @@ The `BashCompletionFunction` option is really only valid/useful on the root comm In the above example "pod" was assumed to already be typed. But if you want `kubectl get [tab][tab]` to show a list of valid "nouns" you have to set them. Simplified code from `kubectl get` looks like: ```go -validArgs []string = { "pods", "nodes", "services", "replicationControllers" } +validArgs []string = { "pod", "node", "service", "replicationcontroller" } cmd := &cobra.Command{ Use: "get [(-o|--output=)json|yaml|template|...] (RESOURCE [NAME] | RESOURCE/NAME ...)", @@ -99,9 +99,34 @@ Notice we put the "ValidArgs" on the "get" subcommand. Doing so will give result ```bash # kubectl get [tab][tab] -nodes pods replicationControllers services +node pod replicationcontroller service ``` +## Plural form and shortcuts for nouns + +If your nouns have a number of aliases, you can define them alongside `ValidArgs` using `ArgAliases`: + +```go` +argAliases []string = { "pods", "nodes", "services", "svc", "replicationcontrollers", "rc" } + +cmd := &cobra.Command{ + ... + ValidArgs: validArgs, + ArgAliases: argAliases +} +``` + +The aliases are not shown to the user on tab completion, but they are accepted as valid nouns by +the completion aglorithm if entered manually, e.g. in: + +```bash +# kubectl get rc [tab][tab] +backend frontend database +``` + +Note that without declaring `rc` as an alias, the completion algorithm would show the list of nouns +in this example again instead of the replication controllers. + ## Mark flags as required Most of the time completions will only show subcommands. But if a flag is required to make a subcommand work, you probably want it to show up when the user types [tab][tab]. Marking a flag as 'Required' is incredibly easy. @@ -147,3 +172,35 @@ hello.yml test.json ``` So while there are many other files in the CWD it only shows me subdirs and those with valid extensions. + +# Specifiy custom flag completion + +Similar to the filename completion and filtering usingn cobra.BashCompFilenameExt, you can specifiy +a custom flag completion function with cobra.BashCompCustom: + +```go + annotation := make(map[string][]string) + annotation[cobra.BashCompFilenameExt] = []string{"__kubectl_get_namespaces"} + + flag := &pflag.Flag{ + Name: "namespace", + Usage: usage, + Annotations: annotation, + } + cmd.Flags().AddFlag(flag) +``` + +In addition add the `__handle_namespace_flag` implementation in the `BashCompletionFunction` +value, e.g.: + +```bash +__kubectl_get_namespaces() +{ + local template + template="{{ range .items }}{{ .metadata.name }} {{ end }}" + local kubectl_out + if kubectl_out=$(kubectl get -o template --template="${template}" namespace 2>/dev/null); then + COMPREPLY=( $( compgen -W "${kubectl_out}[*]" -- "$cur" ) ) + fi +} +``` diff --git a/Godeps/_workspace/src/github.com/spf13/cobra/cobra.go b/Godeps/_workspace/src/github.com/spf13/cobra/cobra.go index 20957cdd1fa..0c4e2e5de11 100644 --- a/Godeps/_workspace/src/github.com/spf13/cobra/cobra.go +++ b/Godeps/_workspace/src/github.com/spf13/cobra/cobra.go @@ -26,27 +26,20 @@ import ( "unicode" ) -var templateFuncs template.FuncMap = template.FuncMap{ - "trim": strings.TrimSpace, - "trimRightSpace": trimRightSpace, - "rpad": rpad, - "gt": Gt, - "eq": Eq, +var templateFuncs = template.FuncMap{ + "trim": strings.TrimSpace, + "trimRightSpace": trimRightSpace, + "appendIfNotPresent": appendIfNotPresent, + "rpad": rpad, + "gt": Gt, + "eq": Eq, } var initializers []func() // automatic prefix matching can be a dangerous thing to automatically enable in CLI tools. // Set this to true to enable it -var EnablePrefixMatching bool = false - -// enables an information splash screen on Windows if the CLI is started from explorer.exe. -var EnableWindowsMouseTrap bool = true - -var MousetrapHelpText string = `This is a command line tool - -You need to open cmd.exe and run it from there. -` +var EnablePrefixMatching = false //AddTemplateFunc adds a template function that's available to Usage and Help //template generation. @@ -119,6 +112,14 @@ func trimRightSpace(s string) string { return strings.TrimRightFunc(s, unicode.IsSpace) } +// appendIfNotPresent will append stringToAppend to the end of s, but only if it's not yet present in s +func appendIfNotPresent(s, stringToAppend string) string { + if strings.Contains(s, stringToAppend) { + return s + } + return s + " " + stringToAppend +} + //rpad adds padding to the right of a string func rpad(s string, padding int) string { template := fmt.Sprintf("%%-%ds", padding) diff --git a/Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/helpers.go b/Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/helpers.go index 3f6357f0ea0..f8b79b1fc0c 100644 --- a/Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/helpers.go +++ b/Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/helpers.go @@ -104,7 +104,7 @@ func guessImportPath() string { er("Cobra only supports project within $GOPATH") } - return filepath.Clean(strings.TrimPrefix(projectPath, getSrcPath())) + return filepath.ToSlash(filepath.Clean(strings.TrimPrefix(projectPath, getSrcPath()))) } func getSrcPath() string { @@ -143,7 +143,7 @@ func guessProjectPath() { srcPath := getSrcPath() // if provided, inspect for logical locations if strings.ContainsRune(inputPath, os.PathSeparator) { - if filepath.IsAbs(inputPath) { + if filepath.IsAbs(inputPath) || filepath.HasPrefix(inputPath, string(os.PathSeparator)) { // if Absolute, use it projectPath = filepath.Clean(inputPath) return @@ -196,7 +196,7 @@ func isEmpty(path string) (bool, error) { if err != nil { return false, err } - list, err := f.Readdir(-1) + list, _ := f.Readdir(-1) // f.Close() - see bug fix above return len(list) == 0, nil } diff --git a/Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/init.go b/Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/init.go index e3c0b45f078..342fb9ccdf5 100644 --- a/Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/init.go +++ b/Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/init.go @@ -29,8 +29,8 @@ func init() { // initialize Command var initCmd = &cobra.Command{ Use: "init [name]", - Aliases: []string{"initialize", "initalise", "create"}, - Short: "Initalize a Cobra Application", + Aliases: []string{"initialize", "initialise", "create"}, + Short: "Initialize a Cobra Application", Long: `Initialize (cobra init) will create a new application, with a license and the appropriate structure for a Cobra-based CLI application. @@ -41,7 +41,7 @@ and the appropriate structure for a Cobra-based CLI application. * If an absolute path is provided, it will be created; * If the directory already exists but is empty, it will be used. -Init will not use an exiting directory with contents.`, +Init will not use an existing directory with contents.`, Run: func(cmd *cobra.Command, args []string) { switch len(args) { @@ -55,11 +55,11 @@ Init will not use an exiting directory with contents.`, er("init doesn't support more than 1 parameter") } guessProjectPath() - initalizePath(projectPath) + initializePath(projectPath) }, } -func initalizePath(path string) { +func initializePath(path string) { b, err := exists(path) if err != nil { er(err) @@ -150,7 +150,7 @@ import ( {{if .viper}} var cfgFile string {{ end }} -// This represents the base command when called without any subcommands +// RootCmd represents the base command when called without any subcommands var RootCmd = &cobra.Command{ Use: "{{ .appName }}", Short: "A brief description of your application", diff --git a/Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/root.go b/Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/root.go index 610386ad766..dbe607beb5f 100644 --- a/Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/root.go +++ b/Godeps/_workspace/src/github.com/spf13/cobra/cobra/cmd/root.go @@ -24,7 +24,7 @@ import ( var cfgFile string var userLicense string -// This represents the base command when called without any subcommands +// RootCmd represents the base command when called without any subcommands var RootCmd = &cobra.Command{ Use: "cobra", Short: "A generator for Cobra based Applications", diff --git a/Godeps/_workspace/src/github.com/spf13/cobra/command.go b/Godeps/_workspace/src/github.com/spf13/cobra/command.go index f5fa34e2af3..0efeeb19575 100644 --- a/Godeps/_workspace/src/github.com/spf13/cobra/command.go +++ b/Godeps/_workspace/src/github.com/spf13/cobra/command.go @@ -21,11 +21,8 @@ import ( "io" "os" "path/filepath" - "runtime" "strings" - "time" - "github.com/inconshreveable/mousetrap" flag "github.com/spf13/pflag" ) @@ -48,8 +45,11 @@ type Command struct { Long string // Examples of how to use the command Example string - // List of all valid non-flag arguments, used for bash completions *TODO* actually validate these + // List of all valid non-flag arguments that are accepted in bash completions ValidArgs []string + // List of aliases for ValidArgs. These are not suggested to the user in the bash + // completion, but accepted if entered manually. + ArgAliases []string // Custom functions used by the bash autocompletion generator BashCompletionFunction string // Is this command deprecated and should print this string when used? @@ -135,9 +135,8 @@ func (c *Command) getOut(def io.Writer) io.Writer { if c.HasParent() { return c.parent.Out() - } else { - return def } + return def } func (c *Command) Out() io.Writer { @@ -197,14 +196,13 @@ func (c *Command) UsageFunc() (f func(*Command) error) { if c.HasParent() { return c.parent.UsageFunc() - } else { - return func(c *Command) error { - err := tmpl(c.Out(), c.UsageTemplate(), c) - if err != nil { - fmt.Print(err) - } - return err + } + return func(c *Command) error { + err := tmpl(c.Out(), c.UsageTemplate(), c) + if err != nil { + fmt.Print(err) } + return err } } @@ -226,35 +224,32 @@ func (c *Command) HelpFunc() func(*Command, []string) { } } -var minUsagePadding int = 25 +var minUsagePadding = 25 func (c *Command) UsagePadding() int { if c.parent == nil || minUsagePadding > c.parent.commandsMaxUseLen { return minUsagePadding - } else { - return c.parent.commandsMaxUseLen } + return c.parent.commandsMaxUseLen } -var minCommandPathPadding int = 11 +var minCommandPathPadding = 11 // func (c *Command) CommandPathPadding() int { if c.parent == nil || minCommandPathPadding > c.parent.commandsMaxCommandPathLen { return minCommandPathPadding - } else { - return c.parent.commandsMaxCommandPathLen } + return c.parent.commandsMaxCommandPathLen } -var minNamePadding int = 11 +var minNamePadding = 11 func (c *Command) NamePadding() int { if c.parent == nil || minNamePadding > c.parent.commandsMaxNameLen { return minNamePadding - } else { - return c.parent.commandsMaxNameLen } + return c.parent.commandsMaxNameLen } func (c *Command) UsageTemplate() string { @@ -264,9 +259,9 @@ func (c *Command) UsageTemplate() string { if c.HasParent() { return c.parent.UsageTemplate() - } else { - return `Usage:{{if .Runnable}} - {{.UseLine}}{{if .HasFlags}} [flags]{{end}}{{end}}{{if .HasSubCommands}} + } + return `Usage:{{if .Runnable}} + {{if .HasFlags}}{{appendIfNotPresent .UseLine "[flags]"}}{{else}}{{.UseLine}}{{end}}{{end}}{{if .HasSubCommands}} {{ .CommandPath}} [command]{{end}}{{if gt .Aliases 0}} Aliases: @@ -290,7 +285,6 @@ Additional help topics:{{range .Commands}}{{if .IsHelpCommand}} Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}} ` - } } func (c *Command) HelpTemplate() string { @@ -300,11 +294,10 @@ func (c *Command) HelpTemplate() string { if c.HasParent() { return c.parent.HelpTemplate() - } else { - return `{{with or .Long .Short }}{{. | trim}} + } + return `{{with or .Long .Short }}{{. | trim}} {{end}}{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}` - } } // Really only used when casting a command to a commander @@ -606,9 +599,8 @@ func (c *Command) errorMsgFromParse() string { if len(x) > 0 { return x[0] - } else { - return "" } + return "" } // Call execute to use the args (os.Args[1:] by default) @@ -626,12 +618,9 @@ func (c *Command) ExecuteC() (cmd *Command, err error) { return c.Root().ExecuteC() } - if EnableWindowsMouseTrap && runtime.GOOS == "windows" { - if mousetrap.StartedByExplorer() { - c.Print(MousetrapHelpText) - time.Sleep(5 * time.Second) - os.Exit(1) - } + // windows hook + if preExecHookFn != nil { + preExecHookFn(c) } // initialize help as the last point possible to allow for user @@ -641,7 +630,7 @@ func (c *Command) ExecuteC() (cmd *Command, err error) { var args []string // Workaround FAIL with "go test -v" or "cobra.test -test.v", see #155 - if len(c.args) == 0 && filepath.Base(os.Args[0]) != "cobra.test" { + if c.args == nil && filepath.Base(os.Args[0]) != "cobra.test" { args = os.Args[1:] } else { args = c.args @@ -661,13 +650,16 @@ func (c *Command) ExecuteC() (cmd *Command, err error) { } err = cmd.execute(flags) if err != nil { + // Always show help if requested, even if SilenceErrors is in + // effect + if err == flag.ErrHelp { + cmd.HelpFunc()(cmd, args) + return cmd, nil + } + // If root command has SilentErrors flagged, // all subcommands should respect it if !cmd.SilenceErrors && !c.SilenceErrors { - if err == flag.ErrHelp { - cmd.HelpFunc()(cmd, args) - return cmd, nil - } c.Println("Error:", err.Error()) } @@ -747,7 +739,7 @@ func (c *Command) AddCommand(cmds ...*Command) { if nameLen > c.commandsMaxNameLen { c.commandsMaxNameLen = nameLen } - // If glabal normalization function exists, update all children + // If global normalization function exists, update all children if c.globNormFunc != nil { x.SetGlobalNormalizationFunc(c.globNormFunc) } @@ -789,18 +781,18 @@ main: } } -// Convenience method to Print to the defined output +// Print is a convenience method to Print to the defined output func (c *Command) Print(i ...interface{}) { fmt.Fprint(c.Out(), i...) } -// Convenience method to Println to the defined output +// Println is a convenience method to Println to the defined output func (c *Command) Println(i ...interface{}) { str := fmt.Sprintln(i...) c.Print(str) } -// Convenience method to Printf to the defined output +// Printf is a convenience method to Printf to the defined output func (c *Command) Printf(format string, i ...interface{}) { str := fmt.Sprintf(format, i...) c.Print(str) @@ -911,7 +903,7 @@ func (c *Command) Name() string { return name } -// Determine if a given string is an alias of the command. +// HasAlias determines if a given string is an alias of the command. func (c *Command) HasAlias(s string) bool { for _, a := range c.Aliases { if a == s { @@ -929,12 +921,12 @@ func (c *Command) HasExample() bool { return len(c.Example) > 0 } -// Determine if the command is itself runnable +// Runnable determines if the command is itself runnable func (c *Command) Runnable() bool { return c.Run != nil || c.RunE != nil } -// Determine if the command has children commands +// HasSubCommands determines if the command has children commands func (c *Command) HasSubCommands() bool { return len(c.commands) > 0 } @@ -1126,7 +1118,7 @@ func (c *Command) HasInheritedFlags() bool { return c.InheritedFlags().HasFlags() } -// Climbs up the command tree looking for matching flag +// Flag climbs up the command tree looking for matching flag func (c *Command) Flag(name string) (flag *flag.Flag) { flag = c.Flags().Lookup(name) @@ -1149,13 +1141,14 @@ func (c *Command) persistentFlag(name string) (flag *flag.Flag) { return } -// Parses persistent flag tree & local flags +// ParseFlags parses persistent flag tree & local flags func (c *Command) ParseFlags(args []string) (err error) { c.mergePersistentFlags() err = c.Flags().Parse(args) return } +// Parent returns a commands parent command func (c *Command) Parent() *Command { return c.parent } diff --git a/Godeps/_workspace/src/github.com/spf13/cobra/command_notwin.go b/Godeps/_workspace/src/github.com/spf13/cobra/command_notwin.go new file mode 100644 index 00000000000..6159c1cc19d --- /dev/null +++ b/Godeps/_workspace/src/github.com/spf13/cobra/command_notwin.go @@ -0,0 +1,5 @@ +// +build !windows + +package cobra + +var preExecHookFn func(*Command) diff --git a/Godeps/_workspace/src/github.com/spf13/cobra/command_win.go b/Godeps/_workspace/src/github.com/spf13/cobra/command_win.go new file mode 100644 index 00000000000..4b0eaa1b6bc --- /dev/null +++ b/Godeps/_workspace/src/github.com/spf13/cobra/command_win.go @@ -0,0 +1,26 @@ +// +build windows + +package cobra + +import ( + "os" + "time" + + "github.com/inconshreveable/mousetrap" +) + +var preExecHookFn = preExecHook + +// enables an information splash screen on Windows if the CLI is started from explorer.exe. +var MousetrapHelpText string = `This is a command line tool + +You need to open cmd.exe and run it from there. +` + +func preExecHook(c *Command) { + if mousetrap.StartedByExplorer() { + c.Print(MousetrapHelpText) + time.Sleep(5 * time.Second) + os.Exit(1) + } +} diff --git a/Godeps/_workspace/src/github.com/spf13/cobra/man_docs.go b/Godeps/_workspace/src/github.com/spf13/cobra/doc/man_docs.go similarity index 72% rename from Godeps/_workspace/src/github.com/spf13/cobra/man_docs.go rename to Godeps/_workspace/src/github.com/spf13/cobra/doc/man_docs.go index c168b62874b..a98674bf3aa 100644 --- a/Godeps/_workspace/src/github.com/spf13/cobra/man_docs.go +++ b/Godeps/_workspace/src/github.com/spf13/cobra/doc/man_docs.go @@ -11,63 +11,58 @@ // See the License for the specific language governing permissions and // limitations under the License. -package cobra +package doc import ( "bytes" "fmt" + "io" "os" + "path/filepath" "sort" "strings" "time" mangen "github.com/cpuguy83/go-md2man/md2man" + "github.com/spf13/cobra" "github.com/spf13/pflag" ) -// GenManTree will call cmd.GenManTree(header, dir) -func GenManTree(cmd *Command, header *GenManHeader, dir string) { - cmd.GenManTree(header, dir) -} - // GenManTree will generate a man page for this command and all decendants // in the directory given. The header may be nil. This function may not work // correctly if your command names have - in them. If you have `cmd` with two // subcmds, `sub` and `sub-third`. And `sub` has a subcommand called `third` // it is undefined which help output will be in the file `cmd-sub-third.1`. -func (cmd *Command) GenManTree(header *GenManHeader, dir string) { +func GenManTree(cmd *cobra.Command, header *GenManHeader, dir string) error { if header == nil { header = &GenManHeader{} } for _, c := range cmd.Commands() { - if !c.IsAvailableCommand() || c == cmd.helpCommand { + if !c.IsAvailableCommand() || c.IsHelpCommand() { continue } - GenManTree(c, header, dir) + if err := GenManTree(c, header, dir); err != nil { + return err + } } - out := new(bytes.Buffer) - needToResetTitle := header.Title == "" - cmd.GenMan(header, out) + basename := strings.Replace(cmd.CommandPath(), " ", "_", -1) + ".1" + filename := filepath.Join(dir, basename) + f, err := os.Create(filename) + if err != nil { + return err + } + defer f.Close() + + if err := GenMan(cmd, header, f); err != nil { + return err + } if needToResetTitle { header.Title = "" } - - filename := cmd.CommandPath() - filename = dir + strings.Replace(filename, " ", "-", -1) + ".1" - outFile, err := os.Create(filename) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - defer outFile.Close() - _, err = outFile.Write(out.Bytes()) - if err != nil { - fmt.Println(err) - os.Exit(1) - } + return nil } // GenManHeader is a lot like the .TH header at the start of man pages. These @@ -83,20 +78,16 @@ type GenManHeader struct { Manual string } -// GenMan will call cmd.GenMan(header, out) -func GenMan(cmd *Command, header *GenManHeader, out *bytes.Buffer) { - cmd.GenMan(header, out) -} - -// GenMan will generate a man page for the given command in the out buffer. -// The header argument may be nil, however obviously out may not. -func (cmd *Command) GenMan(header *GenManHeader, out *bytes.Buffer) { +// GenMan will generate a man page for the given command and write it to +// w. The header argument may be nil, however obviously w may not. +func GenMan(cmd *cobra.Command, header *GenManHeader, w io.Writer) error { if header == nil { header = &GenManHeader{} } - buf := genMarkdown(cmd, header) - final := mangen.Render(buf) - out.Write(final) + b := genMan(cmd, header) + final := mangen.Render(b) + _, err := w.Write(final) + return err } func fillHeader(header *GenManHeader, name string) { @@ -116,7 +107,7 @@ func fillHeader(header *GenManHeader, name string) { } } -func manPreamble(out *bytes.Buffer, header *GenManHeader, name, short, long string) { +func manPreamble(out io.Writer, header *GenManHeader, name, short, long string) { dashName := strings.Replace(name, " ", "-", -1) fmt.Fprintf(out, `%% %s(%s)%s %% %s @@ -130,7 +121,7 @@ func manPreamble(out *bytes.Buffer, header *GenManHeader, name, short, long stri fmt.Fprintf(out, "%s\n\n", long) } -func manPrintFlags(out *bytes.Buffer, flags *pflag.FlagSet) { +func manPrintFlags(out io.Writer, flags *pflag.FlagSet) { flags.VisitAll(func(flag *pflag.Flag) { if len(flag.Deprecated) > 0 || flag.Hidden { return @@ -158,7 +149,7 @@ func manPrintFlags(out *bytes.Buffer, flags *pflag.FlagSet) { }) } -func manPrintOptions(out *bytes.Buffer, command *Command) { +func manPrintOptions(out io.Writer, command *cobra.Command) { flags := command.NonInheritedFlags() if flags.HasFlags() { fmt.Fprintf(out, "# OPTIONS\n") @@ -173,7 +164,7 @@ func manPrintOptions(out *bytes.Buffer, command *Command) { } } -func genMarkdown(cmd *Command, header *GenManHeader) []byte { +func genMan(cmd *cobra.Command, header *GenManHeader) []byte { // something like `rootcmd subcmd1 subcmd2` commandName := cmd.CommandPath() // something like `rootcmd-subcmd1-subcmd2` @@ -195,13 +186,15 @@ func genMarkdown(cmd *Command, header *GenManHeader) []byte { fmt.Fprintf(buf, "# EXAMPLE\n") fmt.Fprintf(buf, "```\n%s\n```\n", cmd.Example) } - if cmd.hasSeeAlso() { + if hasSeeAlso(cmd) { fmt.Fprintf(buf, "# SEE ALSO\n") + seealsos := make([]string, 0) if cmd.HasParent() { parentPath := cmd.Parent().CommandPath() dashParentPath := strings.Replace(parentPath, " ", "-", -1) - fmt.Fprintf(buf, "**%s(%s)**", dashParentPath, header.Section) - cmd.VisitParents(func(c *Command) { + seealso := fmt.Sprintf("**%s(%s)**", dashParentPath, header.Section) + seealsos = append(seealsos, seealso) + cmd.VisitParents(func(c *cobra.Command) { if c.DisableAutoGenTag { cmd.DisableAutoGenTag = c.DisableAutoGenTag } @@ -209,16 +202,14 @@ func genMarkdown(cmd *Command, header *GenManHeader) []byte { } children := cmd.Commands() sort.Sort(byName(children)) - for i, c := range children { - if !c.IsAvailableCommand() || c == cmd.helpCommand { + for _, c := range children { + if !c.IsAvailableCommand() || c.IsHelpCommand() { continue } - if cmd.HasParent() || i > 0 { - fmt.Fprintf(buf, ", ") - } - fmt.Fprintf(buf, "**%s-%s(%s)**", dashCommandName, c.Name(), header.Section) + seealso := fmt.Sprintf("**%s-%s(%s)**", dashCommandName, c.Name(), header.Section) + seealsos = append(seealsos, seealso) } - fmt.Fprintf(buf, "\n") + fmt.Fprintf(buf, "%s\n", strings.Join(seealsos, ", ")) } if !cmd.DisableAutoGenTag { fmt.Fprintf(buf, "# HISTORY\n%s Auto generated by spf13/cobra\n", header.Date.Format("2-Jan-2006")) diff --git a/Godeps/_workspace/src/github.com/spf13/cobra/man_docs.md b/Godeps/_workspace/src/github.com/spf13/cobra/doc/man_docs.md similarity index 85% rename from Godeps/_workspace/src/github.com/spf13/cobra/man_docs.md rename to Godeps/_workspace/src/github.com/spf13/cobra/doc/man_docs.md index 3408c30115f..5fe957a3558 100644 --- a/Godeps/_workspace/src/github.com/spf13/cobra/man_docs.md +++ b/Godeps/_workspace/src/github.com/spf13/cobra/doc/man_docs.md @@ -7,6 +7,7 @@ package main import ( "github.com/spf13/cobra" + "github.com/spf13/cobra/doc" ) func main() { @@ -18,7 +19,7 @@ func main() { Title: "MINE", Section: "3", } - cmd.GenManTree(header, "/tmp") + doc.GenManTree(cmd, header, "/tmp") } ``` diff --git a/Godeps/_workspace/src/github.com/spf13/cobra/doc/md_docs.go b/Godeps/_workspace/src/github.com/spf13/cobra/doc/md_docs.go new file mode 100644 index 00000000000..fa136318049 --- /dev/null +++ b/Godeps/_workspace/src/github.com/spf13/cobra/doc/md_docs.go @@ -0,0 +1,175 @@ +//Copyright 2015 Red Hat Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package doc + +import ( + "fmt" + "io" + "os" + "path/filepath" + "sort" + "strings" + "time" + + "github.com/spf13/cobra" +) + +func printOptions(w io.Writer, cmd *cobra.Command, name string) error { + flags := cmd.NonInheritedFlags() + flags.SetOutput(w) + if flags.HasFlags() { + if _, err := fmt.Fprintf(w, "### Options\n\n```\n"); err != nil { + return err + } + flags.PrintDefaults() + if _, err := fmt.Fprintf(w, "```\n\n"); err != nil { + return err + } + } + + parentFlags := cmd.InheritedFlags() + parentFlags.SetOutput(w) + if parentFlags.HasFlags() { + if _, err := fmt.Fprintf(w, "### Options inherited from parent commands\n\n```\n"); err != nil { + return err + } + parentFlags.PrintDefaults() + if _, err := fmt.Fprintf(w, "```\n\n"); err != nil { + return err + } + } + return nil +} + +func GenMarkdown(cmd *cobra.Command, w io.Writer) error { + return GenMarkdownCustom(cmd, w, func(s string) string { return s }) +} + +func GenMarkdownCustom(cmd *cobra.Command, w io.Writer, linkHandler func(string) string) error { + name := cmd.CommandPath() + + short := cmd.Short + long := cmd.Long + if len(long) == 0 { + long = short + } + + if _, err := fmt.Fprintf(w, "## %s\n\n", name); err != nil { + return err + } + if _, err := fmt.Fprintf(w, "%s\n\n", short); err != nil { + return err + } + if _, err := fmt.Fprintf(w, "### Synopsis\n\n"); err != nil { + return err + } + if _, err := fmt.Fprintf(w, "\n%s\n\n", long); err != nil { + return err + } + + if cmd.Runnable() { + if _, err := fmt.Fprintf(w, "```\n%s\n```\n\n", cmd.UseLine()); err != nil { + return err + } + } + + if len(cmd.Example) > 0 { + if _, err := fmt.Fprintf(w, "### Examples\n\n"); err != nil { + return err + } + if _, err := fmt.Fprintf(w, "```\n%s\n```\n\n", cmd.Example); err != nil { + return err + } + } + + if err := printOptions(w, cmd, name); err != nil { + return err + } + if hasSeeAlso(cmd) { + if _, err := fmt.Fprintf(w, "### SEE ALSO\n"); err != nil { + return err + } + if cmd.HasParent() { + parent := cmd.Parent() + pname := parent.CommandPath() + link := pname + ".md" + link = strings.Replace(link, " ", "_", -1) + if _, err := fmt.Fprintf(w, "* [%s](%s)\t - %s\n", pname, linkHandler(link), parent.Short); err != nil { + return err + } + cmd.VisitParents(func(c *cobra.Command) { + if c.DisableAutoGenTag { + cmd.DisableAutoGenTag = c.DisableAutoGenTag + } + }) + } + + children := cmd.Commands() + sort.Sort(byName(children)) + + for _, child := range children { + if !child.IsAvailableCommand() || child.IsHelpCommand() { + continue + } + cname := name + " " + child.Name() + link := cname + ".md" + link = strings.Replace(link, " ", "_", -1) + if _, err := fmt.Fprintf(w, "* [%s](%s)\t - %s\n", cname, linkHandler(link), child.Short); err != nil { + return err + } + } + if _, err := fmt.Fprintf(w, "\n"); err != nil { + return err + } + } + if !cmd.DisableAutoGenTag { + if _, err := fmt.Fprintf(w, "###### Auto generated by spf13/cobra on %s\n", time.Now().Format("2-Jan-2006")); err != nil { + return err + } + } + return nil +} + +func GenMarkdownTree(cmd *cobra.Command, dir string) error { + identity := func(s string) string { return s } + emptyStr := func(s string) string { return "" } + return GenMarkdownTreeCustom(cmd, dir, emptyStr, identity) +} + +func GenMarkdownTreeCustom(cmd *cobra.Command, dir string, filePrepender, linkHandler func(string) string) error { + for _, c := range cmd.Commands() { + if !c.IsAvailableCommand() || c.IsHelpCommand() { + continue + } + if err := GenMarkdownTreeCustom(c, dir, filePrepender, linkHandler); err != nil { + return err + } + } + + basename := strings.Replace(cmd.CommandPath(), " ", "_", -1) + ".md" + filename := filepath.Join(dir, basename) + f, err := os.Create(filename) + if err != nil { + return err + } + defer f.Close() + + if _, err := io.WriteString(f, filePrepender(filename)); err != nil { + return err + } + if err := GenMarkdownCustom(cmd, f, linkHandler); err != nil { + return err + } + return nil +} diff --git a/Godeps/_workspace/src/github.com/spf13/cobra/md_docs.md b/Godeps/_workspace/src/github.com/spf13/cobra/doc/md_docs.md similarity index 73% rename from Godeps/_workspace/src/github.com/spf13/cobra/md_docs.md rename to Godeps/_workspace/src/github.com/spf13/cobra/doc/md_docs.md index 3a0d55ab902..0c3b96e2718 100644 --- a/Godeps/_workspace/src/github.com/spf13/cobra/md_docs.md +++ b/Godeps/_workspace/src/github.com/spf13/cobra/doc/md_docs.md @@ -1,5 +1,26 @@ # Generating Markdown Docs For Your Own cobra.Command +Generating man pages from a cobra command is incredibly easy. An example is as follows: + +```go +package main + +import ( + "github.com/spf13/cobra" + "github.com/spf13/cobra/doc" +) + +func main() { + cmd := &cobra.Command{ + Use: "test", + Short: "my test program", + } + doc.GenMarkdownTree(cmd, "/tmp") +} +``` + +That will get you a Markdown document `/tmp/test.md` + ## Generate markdown docs for the entire command tree This program can actually generate docs for the kubectl command in the kubernetes project @@ -11,13 +32,15 @@ import ( "io/ioutil" "os" - "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd" - "github.com/spf13/cobra" + kubectlcmd "k8s.io/kubernetes/pkg/kubectl/cmd" + cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" + + "github.com/spf13/cobra/doc" ) func main() { - kubectl := cmd.NewFactory(nil).NewKubectlCommand(os.Stdin, ioutil.Discard, ioutil.Discard) - cobra.GenMarkdownTree(kubectl, "./") + cmd := kubectlcmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, ioutil.Discard, ioutil.Discard) + doc.GenMarkdownTree(cmd, "./") } ``` @@ -29,7 +52,7 @@ You may wish to have more control over the output, or only generate for a single ```go out := new(bytes.Buffer) - cobra.GenMarkdown(cmd, out) + doc.GenMarkdown(cmd, out) ``` This will write the markdown doc for ONLY "cmd" into the out, buffer. @@ -39,14 +62,14 @@ This will write the markdown doc for ONLY "cmd" into the out, buffer. Both `GenMarkdown` and `GenMarkdownTree` have alternate versions with callbacks to get some control of the output: ```go -func GenMarkdownTreeCustom(cmd *Command, dir string, filePrepender func(string) string, linkHandler func(string) string) { - //... +func GenMarkdownTreeCustom(cmd *Command, dir string, filePrepender, linkHandler func(string) string) error { + //... } ``` ```go -func GenMarkdownCustom(cmd *Command, out *bytes.Buffer, linkHandler func(string) string) { - //... +func GenMarkdownCustom(cmd *Command, out *bytes.Buffer, linkHandler func(string) string) error { + //... } ``` @@ -78,4 +101,4 @@ linkHandler := func(name string) string { return "/commands/" + strings.ToLower(base) + "/" } ``` - + diff --git a/Godeps/_workspace/src/github.com/spf13/cobra/doc_util.go b/Godeps/_workspace/src/github.com/spf13/cobra/doc/util.go similarity index 69% rename from Godeps/_workspace/src/github.com/spf13/cobra/doc_util.go rename to Godeps/_workspace/src/github.com/spf13/cobra/doc/util.go index e248216284b..a1c6b89ba6c 100644 --- a/Godeps/_workspace/src/github.com/spf13/cobra/doc_util.go +++ b/Godeps/_workspace/src/github.com/spf13/cobra/doc/util.go @@ -11,24 +11,28 @@ // See the License for the specific language governing permissions and // limitations under the License. -package cobra +package doc + +import "github.com/spf13/cobra" // Test to see if we have a reason to print See Also information in docs // Basically this is a test for a parent commend or a subcommand which is // both not deprecated and not the autogenerated help command. -func (cmd *Command) hasSeeAlso() bool { +func hasSeeAlso(cmd *cobra.Command) bool { if cmd.HasParent() { return true } - children := cmd.Commands() - if len(children) == 0 { - return false - } - for _, c := range children { - if !c.IsAvailableCommand() || c == cmd.helpCommand { + for _, c := range cmd.Commands() { + if !c.IsAvailableCommand() || c.IsHelpCommand() { continue } return true } return false } + +type byName []*cobra.Command + +func (s byName) Len() int { return len(s) } +func (s byName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s byName) Less(i, j int) bool { return s[i].Name() < s[j].Name() } diff --git a/Godeps/_workspace/src/github.com/spf13/cobra/md_docs.go b/Godeps/_workspace/src/github.com/spf13/cobra/md_docs.go deleted file mode 100644 index e940ba160ab..00000000000 --- a/Godeps/_workspace/src/github.com/spf13/cobra/md_docs.go +++ /dev/null @@ -1,162 +0,0 @@ -//Copyright 2015 Red Hat Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cobra - -import ( - "bytes" - "fmt" - "os" - "sort" - "strings" - "time" -) - -func printOptions(out *bytes.Buffer, cmd *Command, name string) { - flags := cmd.NonInheritedFlags() - flags.SetOutput(out) - if flags.HasFlags() { - fmt.Fprintf(out, "### Options\n\n```\n") - flags.PrintDefaults() - fmt.Fprintf(out, "```\n\n") - } - - parentFlags := cmd.InheritedFlags() - parentFlags.SetOutput(out) - if parentFlags.HasFlags() { - fmt.Fprintf(out, "### Options inherited from parent commands\n\n```\n") - parentFlags.PrintDefaults() - fmt.Fprintf(out, "```\n\n") - } -} - -type byName []*Command - -func (s byName) Len() int { return len(s) } -func (s byName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -func (s byName) Less(i, j int) bool { return s[i].Name() < s[j].Name() } - -func GenMarkdown(cmd *Command, out *bytes.Buffer) { - cmd.GenMarkdown(out) -} - -func (cmd *Command) GenMarkdown(out *bytes.Buffer) { - cmd.GenMarkdownCustom(out, func(s string) string { return s }) -} - -func GenMarkdownCustom(cmd *Command, out *bytes.Buffer, linkHandler func(string) string) { - cmd.GenMarkdownCustom(out, linkHandler) -} - -func (cmd *Command) GenMarkdownCustom(out *bytes.Buffer, linkHandler func(string) string) { - name := cmd.CommandPath() - - short := cmd.Short - long := cmd.Long - if len(long) == 0 { - long = short - } - - fmt.Fprintf(out, "## %s\n\n", name) - fmt.Fprintf(out, "%s\n\n", short) - fmt.Fprintf(out, "### Synopsis\n\n") - fmt.Fprintf(out, "\n%s\n\n", long) - - if cmd.Runnable() { - fmt.Fprintf(out, "```\n%s\n```\n\n", cmd.UseLine()) - } - - if len(cmd.Example) > 0 { - fmt.Fprintf(out, "### Examples\n\n") - fmt.Fprintf(out, "```\n%s\n```\n\n", cmd.Example) - } - - printOptions(out, cmd, name) - if cmd.hasSeeAlso() { - fmt.Fprintf(out, "### SEE ALSO\n") - if cmd.HasParent() { - parent := cmd.Parent() - pname := parent.CommandPath() - link := pname + ".md" - link = strings.Replace(link, " ", "_", -1) - fmt.Fprintf(out, "* [%s](%s)\t - %s\n", pname, linkHandler(link), parent.Short) - cmd.VisitParents(func(c *Command) { - if c.DisableAutoGenTag { - cmd.DisableAutoGenTag = c.DisableAutoGenTag - } - }) - } - - children := cmd.Commands() - sort.Sort(byName(children)) - - for _, child := range children { - if !child.IsAvailableCommand() || child == cmd.helpCommand { - continue - } - cname := name + " " + child.Name() - link := cname + ".md" - link = strings.Replace(link, " ", "_", -1) - fmt.Fprintf(out, "* [%s](%s)\t - %s\n", cname, linkHandler(link), child.Short) - } - fmt.Fprintf(out, "\n") - } - if !cmd.DisableAutoGenTag { - fmt.Fprintf(out, "###### Auto generated by spf13/cobra on %s\n", time.Now().Format("2-Jan-2006")) - } -} - -func GenMarkdownTree(cmd *Command, dir string) { - cmd.GenMarkdownTree(dir) -} - -func (cmd *Command) GenMarkdownTree(dir string) { - identity := func(s string) string { return s } - emptyStr := func(s string) string { return "" } - cmd.GenMarkdownTreeCustom(dir, emptyStr, identity) -} - -func GenMarkdownTreeCustom(cmd *Command, dir string, filePrepender func(string) string, linkHandler func(string) string) { - cmd.GenMarkdownTreeCustom(dir, filePrepender, linkHandler) -} - -func (cmd *Command) GenMarkdownTreeCustom(dir string, filePrepender func(string) string, linkHandler func(string) string) { - for _, c := range cmd.Commands() { - if !c.IsAvailableCommand() || c == cmd.helpCommand { - continue - } - c.GenMarkdownTreeCustom(dir, filePrepender, linkHandler) - } - out := new(bytes.Buffer) - - cmd.GenMarkdownCustom(out, linkHandler) - - filename := cmd.CommandPath() - filename = dir + strings.Replace(filename, " ", "_", -1) + ".md" - outFile, err := os.Create(filename) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - defer outFile.Close() - _, err = outFile.WriteString(filePrepender(filename)) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - _, err = outFile.Write(out.Bytes()) - if err != nil { - fmt.Println(err) - os.Exit(1) - } -}