Padded help output, because why not.
authorAndrey Petrov <andrey.petrov@shazow.net>
Fri, 2 Jan 2015 00:17:52 +0000 (16:17 -0800)
committerAndrey Petrov <andrey.petrov@shazow.net>
Fri, 2 Jan 2015 00:17:58 +0000 (16:17 -0800)
chat/command.go
chat/help.go [new file with mode: 0644]

index 26650ea19d8cd56a48a782e895e844d52a8f386d..9047b4d9870163fa6a46f3a9052e3704bb317182 100644 (file)
@@ -3,7 +3,6 @@ package chat
 import (
        "errors"
        "fmt"
-       "sort"
        "strings"
        "sync"
 )
@@ -23,22 +22,25 @@ var ErrMissingPrefix = errors.New("command missing prefix")
 
 // Command is a definition of a handler for a command.
 type Command struct {
-       Prefix     string
+       // The command's key, such as /foo
+       Prefix string
+       // Extra help regarding arguments
        PrefixHelp string
-       Help       string
-       Handler    func(*Channel, CommandMsg) error
+       // If omitted, command is hidden from /help
+       Help    string
+       Handler func(*Channel, CommandMsg) error
 }
 
 // Commands is a registry of available commands.
 type Commands struct {
-       commands map[string]Command
+       commands map[string]*Command
        sync.RWMutex
 }
 
 // NewCommands returns a new Commands registry.
 func NewCommands() *Commands {
        return &Commands{
-               commands: map[string]Command{},
+               commands: map[string]*Command{},
        }
 }
 
@@ -52,7 +54,7 @@ func (c *Commands) Add(cmd Command) error {
                return ErrMissingPrefix
        }
 
-       c.commands[cmd.Prefix] = cmd
+       c.commands[cmd.Prefix] = &cmd
        return nil
 }
 
@@ -91,13 +93,9 @@ func (c *Commands) Help() string {
        c.RLock()
        defer c.RUnlock()
 
-       r := []string{}
-       for _, cmd := range c.commands {
-               r = append(r, fmt.Sprintf("%s %s - %s", cmd.Prefix, cmd.PrefixHelp, cmd.Help))
-       }
-       sort.Strings(r)
-
-       return strings.Join(r, Newline)
+       // TODO: Could cache this...
+       help := NewCommandsHelp(c)
+       return help.String()
 }
 
 var defaultCmdHandlers *Commands
diff --git a/chat/help.go b/chat/help.go
new file mode 100644 (file)
index 0000000..c6e6856
--- /dev/null
@@ -0,0 +1,58 @@
+package chat
+
+import (
+       "fmt"
+       "sort"
+       "strings"
+)
+
+type helpItem struct {
+       Prefix string
+       Text   string
+}
+
+type help struct {
+       items       []helpItem
+       prefixWidth int
+}
+
+// NewCommandsHelp creates a help container from a commands container.
+func NewCommandsHelp(c *Commands) *help {
+       lookup := map[string]struct{}{}
+       h := help{
+               items: []helpItem{},
+       }
+       for _, cmd := range c.commands {
+               if cmd.Help == "" {
+                       // Skip hidden commands.
+                       continue
+               }
+               _, exists := lookup[cmd.Prefix]
+               if exists {
+                       // Duplicate (alias)
+                       continue
+               }
+               lookup[cmd.Prefix] = struct{}{}
+               prefix := fmt.Sprintf("%s %s", cmd.Prefix, cmd.PrefixHelp)
+               h.add(helpItem{prefix, cmd.Help})
+       }
+       return &h
+}
+
+func (h *help) add(item helpItem) {
+       h.items = append(h.items, item)
+       if len(item.Prefix) > h.prefixWidth {
+               h.prefixWidth = len(item.Prefix)
+       }
+}
+
+func (h help) String() string {
+       r := []string{}
+       format := fmt.Sprintf("%%-%ds - %%s", h.prefixWidth)
+       for _, item := range h.items {
+               r = append(r, fmt.Sprintf(format, item.Prefix, item.Text))
+       }
+
+       sort.Strings(r)
+       return strings.Join(r, Newline)
+}