~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/cmd/help.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2012-2015 Canonical Ltd.
 
2
// Licensed under the LGPLv3, see LICENSE file for details.
 
3
 
 
4
package cmd
 
5
 
 
6
import (
 
7
        "bytes"
 
8
        "fmt"
 
9
        "sort"
 
10
        "strings"
 
11
 
 
12
        "launchpad.net/gnuflag"
 
13
)
 
14
 
 
15
type helpCommand struct {
 
16
        CommandBase
 
17
        super     *SuperCommand
 
18
        topic     string
 
19
        topicArgs []string
 
20
        topics    map[string]topic
 
21
 
 
22
        target      *commandReference
 
23
        targetSuper *SuperCommand
 
24
}
 
25
 
 
26
func (c *helpCommand) init() {
 
27
        c.topics = map[string]topic{
 
28
                "commands": {
 
29
                        short: "Basic help for all commands",
 
30
                        long:  func() string { return c.super.describeCommands(true) },
 
31
                },
 
32
                "global-options": {
 
33
                        short: "Options common to all commands",
 
34
                        long:  func() string { return c.globalOptions() },
 
35
                },
 
36
                "topics": {
 
37
                        short: "Topic list",
 
38
                        long:  func() string { return c.topicList() },
 
39
                },
 
40
        }
 
41
}
 
42
 
 
43
func echo(s string) func() string {
 
44
        return func() string { return s }
 
45
}
 
46
 
 
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))
 
50
        }
 
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))
 
55
                }
 
56
                c.topics[alias] = topic{short, long, true}
 
57
        }
 
58
}
 
59
 
 
60
func (c *helpCommand) globalOptions() string {
 
61
        buf := &bytes.Buffer{}
 
62
        fmt.Fprintf(buf, `Global Options
 
63
 
 
64
These options may be used with any command, and may appear in front of any
 
65
command.
 
66
 
 
67
`)
 
68
 
 
69
        f := gnuflag.NewFlagSet("", gnuflag.ContinueOnError)
 
70
        c.super.SetCommonFlags(f)
 
71
        f.SetOutput(buf)
 
72
        f.PrintDefaults()
 
73
        return buf.String()
 
74
}
 
75
 
 
76
func (c *helpCommand) topicList() string {
 
77
        var topics []string
 
78
        longest := 0
 
79
        for name, topic := range c.topics {
 
80
                if topic.alias {
 
81
                        continue
 
82
                }
 
83
                if len(name) > longest {
 
84
                        longest = len(name)
 
85
                }
 
86
                topics = append(topics, name)
 
87
        }
 
88
        sort.Strings(topics)
 
89
        for i, name := range topics {
 
90
                shortHelp := c.topics[name].short
 
91
                topics[i] = fmt.Sprintf("%-*s  %s", longest, name, shortHelp)
 
92
        }
 
93
        return fmt.Sprintf("%s", strings.Join(topics, "\n"))
 
94
}
 
95
 
 
96
func (c *helpCommand) Info() *Info {
 
97
        return &Info{
 
98
                Name:    "help",
 
99
                Args:    "[topic]",
 
100
                Purpose: helpPurpose,
 
101
                Doc: `
 
102
See also: topics
 
103
`,
 
104
        }
 
105
}
 
106
 
 
107
func (c *helpCommand) Init(args []string) error {
 
108
        if c.super.notifyHelp != nil {
 
109
                c.super.notifyHelp(args)
 
110
        }
 
111
        logger.Tracef("helpCommand.Init: %#v", args)
 
112
        if len(args) == 0 {
 
113
                // If there is no help topic specified, print basic usage if it is
 
114
                // there.
 
115
                if _, ok := c.topics["basics"]; ok {
 
116
                        c.topic = "basics"
 
117
                }
 
118
                return nil
 
119
        }
 
120
 
 
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:])
 
126
                }
 
127
                logger.Tracef("help not found, setting topic")
 
128
                c.topic, c.topicArgs = args[0], args[1:]
 
129
                return nil
 
130
        }
 
131
 
 
132
        c.targetSuper = c.super
 
133
        for len(args) > 0 {
 
134
                c.topic, args = args[0], args[1:]
 
135
                commandRef, ok := c.targetSuper.subcmds[c.topic]
 
136
                if !ok {
 
137
                        return fmt.Errorf("subcommand %q not found", c.topic)
 
138
                }
 
139
                c.target = &commandRef
 
140
                // If there are more args and the target isn't a super command
 
141
                // error out.
 
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)
 
147
                }
 
148
        }
 
149
        return nil
 
150
}
 
151
 
 
152
func (c *helpCommand) getCommandHelp(super *SuperCommand, command Command, alias string) []byte {
 
153
        info := command.Info()
 
154
 
 
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.
 
159
                if alias == "" {
 
160
                        info.Name = fmt.Sprintf("%s %s", super.Name, info.Name)
 
161
                } else {
 
162
                        info.Name = fmt.Sprintf("%s %s", super.Name, alias)
 
163
                }
 
164
        }
 
165
        if super.usagePrefix != "" {
 
166
                logger.Tracef("adding super prefix")
 
167
                info.Name = fmt.Sprintf("%s %s", super.usagePrefix, info.Name)
 
168
        }
 
169
        f := gnuflag.NewFlagSet(info.Name, gnuflag.ContinueOnError)
 
170
        command.SetFlags(f)
 
171
        return info.Help(f)
 
172
}
 
173
 
 
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)
 
178
                v.Init(nil)
 
179
                return v.Run(ctx)
 
180
        }
 
181
 
 
182
        // If the topic is a registered subcommand, then run the help command with it
 
183
        if c.target != nil {
 
184
                ctx.Stdout.Write(c.getCommandHelp(c.targetSuper, c.target.command, c.target.alias))
 
185
                return nil
 
186
        }
 
187
 
 
188
        // If there is no help topic specified, print basic usage.
 
189
        if c.topic == "" {
 
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, ""))
 
195
                return nil
 
196
        }
 
197
 
 
198
        // Look to see if the topic is a registered topic.
 
199
        topic, ok := c.topics[c.topic]
 
200
        if ok {
 
201
                fmt.Fprintf(ctx.Stdout, "%s\n", strings.TrimSpace(topic.long()))
 
202
                return nil
 
203
        }
 
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...)
 
209
                }
 
210
                command := &missingCommand{
 
211
                        callback:  c.super.missingCallback,
 
212
                        superName: c.super.Name,
 
213
                        name:      c.topic,
 
214
                        args:      helpArgs,
 
215
                }
 
216
                err := command.Run(ctx)
 
217
                _, isUnrecognized := err.(*UnrecognizedCommand)
 
218
                if !isUnrecognized {
 
219
                        return err
 
220
                }
 
221
        }
 
222
        return fmt.Errorf("unknown command or topic for %s", c.topic)
 
223
}