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