1
// Copyright 2012-2015 Canonical Ltd.
2
// Licensed under the LGPLv3, see LICENSE file for details.
12
"launchpad.net/gnuflag"
15
type helpCommand struct {
20
topics map[string]topic
22
target *commandReference
23
targetSuper *SuperCommand
26
func (c *helpCommand) init() {
27
c.topics = map[string]topic{
29
short: "Basic help for all commands",
30
long: func() string { return c.super.describeCommands(true) },
33
short: "Options common to all commands",
34
long: func() string { return c.globalOptions() },
38
long: func() string { return c.topicList() },
43
func echo(s string) func() string {
44
return func() string { return s }
47
func (c *helpCommand) addTopic(name, short string, long func() string, aliases ...string) {
48
if _, found := c.topics[name]; found {
49
panic(fmt.Sprintf("help topic already added: %s", name))
51
c.topics[name] = topic{short, long, false}
52
for _, alias := range aliases {
53
if _, found := c.topics[alias]; found {
54
panic(fmt.Sprintf("help topic already added: %s", alias))
56
c.topics[alias] = topic{short, long, true}
60
func (c *helpCommand) globalOptions() string {
61
buf := &bytes.Buffer{}
62
fmt.Fprintf(buf, `Global Options
64
These options may be used with any command, and may appear in front of any
69
f := gnuflag.NewFlagSet("", gnuflag.ContinueOnError)
70
c.super.SetCommonFlags(f)
76
func (c *helpCommand) topicList() string {
79
for name, topic := range c.topics {
83
if len(name) > longest {
86
topics = append(topics, name)
89
for i, name := range topics {
90
shortHelp := c.topics[name].short
91
topics[i] = fmt.Sprintf("%-*s %s", longest, name, shortHelp)
93
return fmt.Sprintf("%s", strings.Join(topics, "\n"))
96
func (c *helpCommand) Info() *Info {
100
Purpose: helpPurpose,
107
func (c *helpCommand) Init(args []string) error {
108
if c.super.notifyHelp != nil {
109
c.super.notifyHelp(args)
111
logger.Tracef("helpCommand.Init: %#v", args)
113
// If there is no help topic specified, print basic usage if it is
115
if _, ok := c.topics["basics"]; ok {
121
// Before we start walking down the subcommand list, we want to check
122
// to see if the first part is there.
123
if _, ok := c.super.subcmds[args[0]]; !ok {
124
if c.super.missingCallback == nil && len(args) > 1 {
125
return fmt.Errorf("extra arguments to command help: %q", args[1:])
127
logger.Tracef("help not found, setting topic")
128
c.topic, c.topicArgs = args[0], args[1:]
132
c.targetSuper = c.super
134
c.topic, args = args[0], args[1:]
135
commandRef, ok := c.targetSuper.subcmds[c.topic]
137
return fmt.Errorf("subcommand %q not found", c.topic)
139
c.target = &commandRef
140
// If there are more args and the target isn't a super command
142
logger.Tracef("target name: %s", c.target.name)
143
if super, ok := c.target.command.(*SuperCommand); ok {
144
c.targetSuper = super
145
} else if len(args) > 0 {
146
return fmt.Errorf("extra arguments to command help: %q", args)
152
func (c *helpCommand) getCommandHelp(super *SuperCommand, command Command, alias string) []byte {
153
info := command.Info()
155
if command != super {
156
logger.Tracef("command not super")
157
// If the alias is to a subcommand of another super command
158
// the alias string holds the "super sub" name.
160
info.Name = fmt.Sprintf("%s %s", super.Name, info.Name)
162
info.Name = fmt.Sprintf("%s %s", super.Name, alias)
165
if super.usagePrefix != "" {
166
logger.Tracef("adding super prefix")
167
info.Name = fmt.Sprintf("%s %s", super.usagePrefix, info.Name)
169
f := gnuflag.NewFlagSet(info.Name, gnuflag.ContinueOnError)
174
func (c *helpCommand) Run(ctx *Context) error {
175
if c.super.showVersion {
176
v := newVersionCommand(c.super.version)
177
v.SetFlags(c.super.flags)
182
// If the topic is a registered subcommand, then run the help command with it
184
ctx.Stdout.Write(c.getCommandHelp(c.targetSuper, c.target.command, c.target.alias))
188
// If there is no help topic specified, print basic usage.
190
// At this point, "help" is selected as the SuperCommand's
191
// current action, but we want the info to be printed
192
// as if there was nothing selected.
193
c.super.action.command = nil
194
ctx.Stdout.Write(c.getCommandHelp(c.super, c.super, ""))
198
// Look to see if the topic is a registered topic.
199
topic, ok := c.topics[c.topic]
201
fmt.Fprintf(ctx.Stdout, "%s\n", strings.TrimSpace(topic.long()))
204
// If we have a missing callback, call that with --help
205
if c.super.missingCallback != nil {
206
helpArgs := []string{"--help"}
207
if len(c.topicArgs) > 0 {
208
helpArgs = append(helpArgs, c.topicArgs...)
210
command := &missingCommand{
211
callback: c.super.missingCallback,
212
superName: c.super.Name,
216
err := command.Run(ctx)
217
_, isUnrecognized := err.(*UnrecognizedCommand)
222
return fmt.Errorf("unknown command or topic for %s", c.topic)