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  }