github.com/elopio/cli@v6.21.2-0.20160902224010-ea909d1fdb2f+incompatible/commands/v2/help_command.go (about) 1 package v2 2 3 import ( 4 "fmt" 5 "math" 6 "sort" 7 "strings" 8 9 "code.cloudfoundry.org/cli/actors/v2actions" 10 "code.cloudfoundry.org/cli/cf" 11 "code.cloudfoundry.org/cli/commands" 12 "code.cloudfoundry.org/cli/commands/flags" 13 "code.cloudfoundry.org/cli/commands/v2/internal" 14 "code.cloudfoundry.org/cli/utils/config" 15 "code.cloudfoundry.org/cli/utils/sortutils" 16 ) 17 18 //go:generate counterfeiter . HelpActor 19 20 // HelpActor handles the business logic of the help command 21 type HelpActor interface { 22 // CommandInfoByName returns back a help command information for the given 23 // command 24 CommandInfoByName(interface{}, string) (v2actions.CommandInfo, error) 25 26 // CommandInfos returns a list of all commands 27 CommandInfos(interface{}) map[string]v2actions.CommandInfo 28 } 29 30 type HelpCommand struct { 31 UI commands.UI 32 Actor HelpActor 33 Config commands.Config 34 35 OptionalArgs flags.CommandName `positional-args:"yes"` 36 AllCommands bool `short:"a" description:"All available CLI commands"` 37 usage interface{} `usage:"CF_NAME help [COMMAND]"` 38 } 39 40 func (cmd *HelpCommand) Setup(config commands.Config, ui commands.UI) error { 41 cmd.Actor = v2actions.NewActor() 42 cmd.Config = config 43 cmd.UI = ui 44 45 return nil 46 } 47 48 func (cmd HelpCommand) Execute(args []string) error { 49 var err error 50 if cmd.OptionalArgs.CommandName == "" { 51 cmd.displayFullHelp() 52 } else { 53 err = cmd.displayCommand() 54 } 55 56 return err 57 } 58 59 func (cmd HelpCommand) displayFullHelp() { 60 cmd.displayHelpPreamble() 61 if cmd.AllCommands { 62 cmd.displayAllCommands() 63 cmd.displayHelpFooter() 64 } else { 65 cmd.displayCommonCommands() 66 } 67 } 68 69 func (cmd HelpCommand) displayHelpPreamble() { 70 cmd.UI.DisplayHelpHeader("NAME:") 71 cmd.UI.DisplayTextWithKeyTranslations(" {{.CommandName}} - {{.CommandDescription}}", 72 []string{"CommandDescription"}, 73 map[string]interface{}{ 74 "CommandName": cmd.Config.BinaryName(), 75 "CommandDescription": "A command line tool to interact with Cloud Foundry", 76 }) 77 cmd.UI.DisplayNewline() 78 79 cmd.UI.DisplayHelpHeader("USAGE:") 80 cmd.UI.DisplayTextWithKeyTranslations(" {{.CommandName}} {{.CommandUsage}}", 81 []string{"CommandUsage"}, 82 map[string]interface{}{ 83 "CommandName": cmd.Config.BinaryName(), 84 "CommandUsage": "[global options] command [arguments...] [command options]", 85 }) 86 cmd.UI.DisplayNewline() 87 88 cmd.UI.DisplayHelpHeader("VERSION:") 89 cmd.UI.DisplayText(" {{.Version}}-{{.Time}}", map[string]interface{}{ 90 "Version": cf.Version, 91 "Time": cf.BuiltOnDate, 92 }) 93 cmd.UI.DisplayNewline() 94 } 95 96 func (cmd HelpCommand) displayCommonCommands() { 97 cmdInfo := cmd.Actor.CommandInfos(Commands) 98 99 for _, category := range internal.CommonHelpCategoryList { 100 cmd.UI.DisplayHelpHeader(category.CategoryName) 101 table := [][]string{} 102 103 for _, row := range category.CommandList { 104 finalRow := []string{} 105 106 for _, command := range row { 107 separator := "" 108 if info, ok := cmdInfo[command]; ok { 109 if len(info.Alias) > 0 { 110 separator = "," 111 } 112 finalRow = append(finalRow, fmt.Sprintf("%s%s%s", info.Name, separator, info.Alias)) 113 } 114 } 115 116 table = append(table, finalRow) 117 } 118 119 cmd.UI.DisplayTable(" ", table) 120 cmd.UI.DisplayNewline() 121 } 122 123 pluginCommands := cmd.getSortedPluginCommands() 124 cmd.UI.DisplayHelpHeader("Commands offered by installed plugins:") 125 126 size := int(math.Ceil(float64(len(pluginCommands)) / 3)) 127 table := make([][]string, size) 128 for i := 0; i < size; i++ { 129 table[i] = make([]string, 3) 130 for j := 0; j < 3; j++ { 131 index := i + j*size 132 if index < len(pluginCommands) { 133 table[i][j] = pluginCommands[index].Name 134 } 135 } 136 } 137 138 cmd.UI.DisplayTable(" ", table) 139 cmd.UI.DisplayNewline() 140 141 cmd.UI.DisplayHelpHeader("Global options:") 142 cmd.UI.DisplayTextWithKeyTranslations(" {{.ENVName}} {{.Description}}", 143 []string{"Description"}, 144 map[string]interface{}{ 145 "ENVName": "--help, -h", 146 "Description": "Show help", 147 }) 148 cmd.UI.DisplayTextWithKeyTranslations(" {{.ENVName}} {{.Description}}", 149 []string{"Description"}, 150 map[string]interface{}{ 151 "ENVName": "-v", 152 "Description": "Print API request diagnostics to stdout", 153 }) 154 cmd.UI.DisplayNewline() 155 cmd.UI.DisplayText("'cf help -a' lists all commands with short descriptions. See 'cf help <command>' to read about a specific command.") 156 } 157 158 func (cmd HelpCommand) displayAllCommands() { 159 pluginCommands := cmd.getSortedPluginCommands() 160 cmdInfo := cmd.Actor.CommandInfos(Commands) 161 longestCmd := internal.LongestCommandName(cmdInfo, pluginCommands) 162 163 for _, category := range internal.HelpCategoryList { 164 cmd.UI.DisplayHelpHeader(category.CategoryName) 165 166 for _, row := range category.CommandList { 167 for _, command := range row { 168 cmd.UI.DisplayTextWithKeyTranslations(" {{.CommandName}}{{.Gap}}{{.CommandDescription}}", 169 []string{"CommandDescription"}, 170 map[string]interface{}{ 171 "CommandName": cmdInfo[command].Name, 172 "CommandDescription": cmdInfo[command].Description, 173 "Gap": strings.Repeat(" ", longestCmd+1-len(command)), 174 }) 175 } 176 177 cmd.UI.DisplayNewline() 178 } 179 } 180 181 cmd.UI.DisplayHelpHeader("INSTALLED PLUGIN COMMANDS:") 182 for _, pluginCommand := range pluginCommands { 183 cmd.UI.DisplayText(" {{.CommandName}}{{.Gap}}{{.CommandDescription}}", map[string]interface{}{ 184 "CommandName": pluginCommand.Name, 185 "CommandDescription": pluginCommand.HelpText, 186 "Gap": strings.Repeat(" ", longestCmd+1-len(pluginCommand.Name)), 187 }) 188 } 189 cmd.UI.DisplayNewline() 190 } 191 192 func (cmd HelpCommand) displayHelpFooter() { 193 cmd.UI.DisplayHelpHeader("ENVIRONMENT VARIABLES:") 194 cmd.UI.DisplayTextWithKeyTranslations(" {{.ENVName}} {{.Description}}", 195 []string{"Description"}, 196 map[string]interface{}{ 197 "ENVName": "CF_COLOR=false", 198 "Description": "Do not colorize output", 199 }) 200 cmd.UI.DisplayTextWithKeyTranslations(" {{.ENVName}} {{.Description}}", 201 []string{"Description"}, 202 map[string]interface{}{ 203 "ENVName": "CF_HOME=path/to/dir/", 204 "Description": "Override path to default config directory", 205 }) 206 cmd.UI.DisplayTextWithKeyTranslations(" {{.ENVName}} {{.Description}}", 207 []string{"Description"}, 208 map[string]interface{}{ 209 "ENVName": "CF_PLUGIN_HOME=path/to/dir/", 210 "Description": "Override path to default plugin config directory", 211 }) 212 cmd.UI.DisplayTextWithKeyTranslations(" {{.ENVName}} {{.Description}}", 213 []string{"Description"}, 214 map[string]interface{}{ 215 "ENVName": "CF_STAGING_TIMEOUT=15", 216 "Description": "Max wait time for buildpack staging, in minutes", 217 }) 218 cmd.UI.DisplayTextWithKeyTranslations(" {{.ENVName}} {{.Description}}", 219 []string{"Description"}, 220 map[string]interface{}{ 221 "ENVName": "CF_STARTUP_TIMEOUT=5", 222 "Description": "Max wait time for app instance startup, in minutes", 223 }) 224 cmd.UI.DisplayTextWithKeyTranslations(" {{.ENVName}} {{.Description}}", 225 []string{"Description"}, 226 map[string]interface{}{ 227 "ENVName": "CF_TRACE=true", 228 "Description": "Print API request diagnostics to stdout", 229 }) 230 cmd.UI.DisplayTextWithKeyTranslations(" {{.ENVName}} {{.Description}}", 231 []string{"Description"}, 232 map[string]interface{}{ 233 "ENVName": "CF_TRACE=path/to/trace.log", 234 "Description": "Append API request diagnostics to a log file", 235 }) 236 cmd.UI.DisplayTextWithKeyTranslations(" {{.ENVName}} {{.Description}}", 237 []string{"Description"}, 238 map[string]interface{}{ 239 "ENVName": "https_proxy=proxy.example.com:8080", 240 "Description": "Enable HTTP proxying for API requests", 241 }) 242 cmd.UI.DisplayNewline() 243 244 cmd.UI.DisplayHelpHeader("GLOBAL OPTIONS:") 245 cmd.UI.DisplayTextWithKeyTranslations(" {{.ENVName}} {{.Description}}", 246 []string{"Description"}, 247 map[string]interface{}{ 248 "ENVName": "--help, -h", 249 "Description": "Show help", 250 }) 251 cmd.UI.DisplayTextWithKeyTranslations(" {{.ENVName}} {{.Description}}", 252 []string{"Description"}, 253 map[string]interface{}{ 254 "ENVName": "-v", 255 "Description": "Print API request diagnostics to stdout", 256 }) 257 } 258 259 func (cmd HelpCommand) displayCommand() error { 260 cmdInfo, err := cmd.Actor.CommandInfoByName(Commands, cmd.OptionalArgs.CommandName) 261 if err != nil { 262 if err, ok := err.(v2actions.ErrorInvalidCommand); ok { 263 var found bool 264 if cmdInfo, found = cmd.findPlugin(); !found { 265 return err 266 } 267 } else { 268 return err 269 } 270 } 271 272 cmd.UI.DisplayText("NAME:") 273 cmd.UI.DisplayTextWithKeyTranslations(" {{.CommandName}} - {{.CommandDescription}}", 274 []string{"CommandDescription"}, 275 map[string]interface{}{ 276 "CommandName": cmdInfo.Name, 277 "CommandDescription": cmdInfo.Description, 278 }) 279 280 cmd.UI.DisplayNewline() 281 usageString := strings.Replace(cmdInfo.Usage, "CF_NAME", cmd.Config.BinaryName(), -1) 282 cmd.UI.DisplayText("USAGE:") 283 cmd.UI.DisplayTextWithKeyTranslations(" {{.CommandUsage}}", 284 []string{"CommandUsage"}, 285 map[string]interface{}{ 286 "CommandUsage": usageString, 287 }) 288 289 if cmdInfo.Alias != "" { 290 cmd.UI.DisplayNewline() 291 cmd.UI.DisplayText("ALIAS:") 292 cmd.UI.DisplayText(" {{.Alias}}", 293 map[string]interface{}{ 294 "Alias": cmdInfo.Alias, 295 }) 296 } 297 298 if len(cmdInfo.Flags) != 0 { 299 cmd.UI.DisplayNewline() 300 cmd.UI.DisplayText("OPTIONS:") 301 nameWidth := internal.LongestFlagWidth(cmdInfo.Flags) + 6 302 for _, flag := range cmdInfo.Flags { 303 var name string 304 if flag.Short != "" && flag.Long != "" { 305 name = fmt.Sprintf("--%s, -%s", flag.Long, flag.Short) 306 } else if flag.Short != "" { 307 name = "-" + flag.Short 308 } else { 309 name = "--" + flag.Long 310 } 311 312 cmd.UI.DisplayTextWithKeyTranslations(" {{.Flags}}{{.Spaces}}{{.Description}}", 313 []string{"Description"}, 314 map[string]interface{}{ 315 "Flags": name, 316 "Spaces": strings.Repeat(" ", nameWidth-len(name)), 317 "Description": flag.Description, 318 }) 319 } 320 } 321 322 if len(cmdInfo.RelatedCommands) > 0 { 323 cmd.UI.DisplayNewline() 324 cmd.UI.DisplayText("SEE ALSO:") 325 cmd.UI.DisplayText(" {{.RelatedCommands}}", 326 map[string]interface{}{ 327 "RelatedCommands": strings.Join(cmdInfo.RelatedCommands, ", "), 328 }) 329 } 330 331 return nil 332 } 333 334 func (cmd HelpCommand) findPlugin() (v2actions.CommandInfo, bool) { 335 for _, pluginConfig := range cmd.Config.PluginConfig() { 336 for _, command := range pluginConfig.Commands { 337 if command.Name == cmd.OptionalArgs.CommandName { 338 return internal.ConvertPluginToCommandInfo(command), true 339 } 340 } 341 } 342 343 return v2actions.CommandInfo{}, false 344 } 345 346 func (cmd HelpCommand) getSortedPluginCommands() config.PluginCommands { 347 plugins := cmd.Config.PluginConfig() 348 349 sortedPluginNames := sortutils.Alphabetic{} 350 for plugin, _ := range plugins { 351 sortedPluginNames = append(sortedPluginNames, plugin) 352 } 353 sort.Sort(sortedPluginNames) 354 355 pluginCommands := config.PluginCommands{} 356 for _, pluginName := range sortedPluginNames { 357 sortedCommands := plugins[pluginName].Commands 358 sort.Sort(sortedCommands) 359 pluginCommands = append(pluginCommands, sortedCommands...) 360 } 361 362 return pluginCommands 363 }