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