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