github.com/zignig/go-ipfs@v0.0.0-20141111235910-c9e5fdf55a52/commands/cli/helptext.go (about) 1 package cli 2 3 import ( 4 "fmt" 5 "strings" 6 7 cmds "github.com/jbenet/go-ipfs/commands" 8 ) 9 10 const ( 11 requiredArg = "<%v>" 12 optionalArg = "[<%v>]" 13 variadicArg = "%v..." 14 optionFlag = "-%v" 15 optionType = "(%v)" 16 17 whitespace = "\r\n\t " 18 ) 19 20 // HelpText returns a formatted CLI helptext string, generated for the given command 21 func HelpText(rootName string, root *cmds.Command, path []string) (string, error) { 22 cmd, err := root.Get(path) 23 if err != nil { 24 return "", err 25 } 26 27 s := "" 28 usage := usageText(cmd) 29 if len(usage) > 0 { 30 usage += " " 31 } 32 s += fmt.Sprintf("%v %v %v- %v\n\n", rootName, strings.Join(path, " "), usage, cmd.Description) 33 34 if len(cmd.Help) > 0 { 35 s += fmt.Sprintf("%v\n\n", strings.Trim(cmd.Help, whitespace)) 36 } 37 38 if cmd.Arguments != nil { 39 lines := indent(argumentText(cmd), " ") 40 s += fmt.Sprintf("Arguments:\n%v\n\n", strings.Join(lines, "\n")) 41 } 42 43 if cmd.Subcommands != nil { 44 lines := indent(subcommandText(cmd, rootName, path), " ") 45 s += fmt.Sprintf("Subcommands:\n%v\n\n", strings.Join(lines, "\n")) 46 } 47 48 if cmd.Options != nil { 49 lines := indent(optionText(cmd), " ") 50 s += fmt.Sprintf("Options:\n%v\n\n", strings.Join(lines, "\n")) 51 } 52 53 return s, nil 54 } 55 56 func argumentText(cmd *cmds.Command) []string { 57 lines := make([]string, len(cmd.Arguments)) 58 59 for i, arg := range cmd.Arguments { 60 lines[i] = argUsageText(arg) 61 lines[i] += "\n" + arg.Description 62 lines[i] = indentString(lines[i], " ") + "\n" 63 } 64 65 return lines 66 } 67 68 func optionText(cmd ...*cmds.Command) []string { 69 // get a slice of the options we want to list out 70 options := make([]cmds.Option, 0) 71 for _, c := range cmd { 72 for _, opt := range c.Options { 73 options = append(options, opt) 74 } 75 } 76 77 // add option names to output (with each name aligned) 78 lines := make([]string, 0) 79 j := 0 80 for { 81 done := true 82 i := 0 83 for _, opt := range options { 84 if len(lines) < i+1 { 85 lines = append(lines, "") 86 } 87 if len(opt.Names) >= j+1 { 88 lines[i] += fmt.Sprintf(optionFlag, opt.Names[j]) 89 } 90 if len(opt.Names) > j+1 { 91 lines[i] += ", " 92 done = false 93 } 94 95 i++ 96 } 97 98 if done { 99 break 100 } 101 102 lines = align(lines) 103 j++ 104 } 105 106 // add option types to output 107 for i, opt := range options { 108 lines[i] += " " + fmt.Sprintf(optionType, opt.Type) 109 } 110 lines = align(lines) 111 112 // add option descriptions to output 113 for i, opt := range options { 114 lines[i] += "\n" + opt.Description 115 lines[i] = indentString(lines[i], " ") + "\n" 116 } 117 118 return lines 119 } 120 121 func subcommandText(cmd *cmds.Command, rootName string, path []string) []string { 122 prefix := fmt.Sprintf("%v %v", rootName, strings.Join(path, " ")) 123 if len(path) > 0 { 124 prefix += " " 125 } 126 lines := make([]string, len(cmd.Subcommands)) 127 128 i := 0 129 for name, sub := range cmd.Subcommands { 130 usage := usageText(sub) 131 lines[i] = fmt.Sprintf("%v%v %v", prefix, name, usage) 132 lines[i] += fmt.Sprintf("\n%v", sub.Description) 133 lines[i] = indentString(lines[i], " ") + "\n" 134 i++ 135 } 136 137 return lines 138 } 139 140 func usageText(cmd *cmds.Command) string { 141 s := "" 142 for i, arg := range cmd.Arguments { 143 if i != 0 { 144 s += " " 145 } 146 s += argUsageText(arg) 147 } 148 149 return s 150 } 151 152 func argUsageText(arg cmds.Argument) string { 153 s := arg.Name 154 155 if arg.Required { 156 s = fmt.Sprintf(requiredArg, s) 157 } else { 158 s = fmt.Sprintf(optionalArg, s) 159 } 160 161 if arg.Variadic { 162 s = fmt.Sprintf(variadicArg, s) 163 } 164 165 return s 166 } 167 168 func align(lines []string) []string { 169 longest := 0 170 for _, line := range lines { 171 length := len(line) 172 if length > longest { 173 longest = length 174 } 175 } 176 177 for i, line := range lines { 178 length := len(line) 179 if length > 0 { 180 lines[i] += strings.Repeat(" ", longest-length) 181 } 182 } 183 184 return lines 185 } 186 187 func indent(lines []string, prefix string) []string { 188 for i, line := range lines { 189 lines[i] = prefix + indentString(line, prefix) 190 } 191 return lines 192 } 193 194 func indentString(line string, prefix string) string { 195 return strings.Replace(line, "\n", "\n"+prefix, -1) 196 }