github.com/zignig/go-ipfs@v0.0.0-20141111235910-c9e5fdf55a52/commands/cli/helptext.go (about)

     1  package cli
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	cmds "github.com/jbenet/go-ipfs/commands"
     8  )
     9  
    10  const (
    11  	requiredArg = "<%v>"
    12  	optionalArg = "[<%v>]"
    13  	variadicArg = "%v..."
    14  	optionFlag  = "-%v"
    15  	optionType  = "(%v)"
    16  
    17  	whitespace = "\r\n\t "
    18  )
    19  
    20  // HelpText returns a formatted CLI helptext string, generated for the given command
    21  func HelpText(rootName string, root *cmds.Command, path []string) (string, error) {
    22  	cmd, err := root.Get(path)
    23  	if err != nil {
    24  		return "", err
    25  	}
    26  
    27  	s := ""
    28  	usage := usageText(cmd)
    29  	if len(usage) > 0 {
    30  		usage += " "
    31  	}
    32  	s += fmt.Sprintf("%v %v %v- %v\n\n", rootName, strings.Join(path, " "), usage, cmd.Description)
    33  
    34  	if len(cmd.Help) > 0 {
    35  		s += fmt.Sprintf("%v\n\n", strings.Trim(cmd.Help, whitespace))
    36  	}
    37  
    38  	if cmd.Arguments != nil {
    39  		lines := indent(argumentText(cmd), "  ")
    40  		s += fmt.Sprintf("Arguments:\n%v\n\n", strings.Join(lines, "\n"))
    41  	}
    42  
    43  	if cmd.Subcommands != nil {
    44  		lines := indent(subcommandText(cmd, rootName, path), "  ")
    45  		s += fmt.Sprintf("Subcommands:\n%v\n\n", strings.Join(lines, "\n"))
    46  	}
    47  
    48  	if cmd.Options != nil {
    49  		lines := indent(optionText(cmd), "  ")
    50  		s += fmt.Sprintf("Options:\n%v\n\n", strings.Join(lines, "\n"))
    51  	}
    52  
    53  	return s, nil
    54  }
    55  
    56  func argumentText(cmd *cmds.Command) []string {
    57  	lines := make([]string, len(cmd.Arguments))
    58  
    59  	for i, arg := range cmd.Arguments {
    60  		lines[i] = argUsageText(arg)
    61  		lines[i] += "\n" + arg.Description
    62  		lines[i] = indentString(lines[i], "    ") + "\n"
    63  	}
    64  
    65  	return lines
    66  }
    67  
    68  func optionText(cmd ...*cmds.Command) []string {
    69  	// get a slice of the options we want to list out
    70  	options := make([]cmds.Option, 0)
    71  	for _, c := range cmd {
    72  		for _, opt := range c.Options {
    73  			options = append(options, opt)
    74  		}
    75  	}
    76  
    77  	// add option names to output (with each name aligned)
    78  	lines := make([]string, 0)
    79  	j := 0
    80  	for {
    81  		done := true
    82  		i := 0
    83  		for _, opt := range options {
    84  			if len(lines) < i+1 {
    85  				lines = append(lines, "")
    86  			}
    87  			if len(opt.Names) >= j+1 {
    88  				lines[i] += fmt.Sprintf(optionFlag, opt.Names[j])
    89  			}
    90  			if len(opt.Names) > j+1 {
    91  				lines[i] += ", "
    92  				done = false
    93  			}
    94  
    95  			i++
    96  		}
    97  
    98  		if done {
    99  			break
   100  		}
   101  
   102  		lines = align(lines)
   103  		j++
   104  	}
   105  
   106  	// add option types to output
   107  	for i, opt := range options {
   108  		lines[i] += " " + fmt.Sprintf(optionType, opt.Type)
   109  	}
   110  	lines = align(lines)
   111  
   112  	// add option descriptions to output
   113  	for i, opt := range options {
   114  		lines[i] += "\n" + opt.Description
   115  		lines[i] = indentString(lines[i], "    ") + "\n"
   116  	}
   117  
   118  	return lines
   119  }
   120  
   121  func subcommandText(cmd *cmds.Command, rootName string, path []string) []string {
   122  	prefix := fmt.Sprintf("%v %v", rootName, strings.Join(path, " "))
   123  	if len(path) > 0 {
   124  		prefix += " "
   125  	}
   126  	lines := make([]string, len(cmd.Subcommands))
   127  
   128  	i := 0
   129  	for name, sub := range cmd.Subcommands {
   130  		usage := usageText(sub)
   131  		lines[i] = fmt.Sprintf("%v%v %v", prefix, name, usage)
   132  		lines[i] += fmt.Sprintf("\n%v", sub.Description)
   133  		lines[i] = indentString(lines[i], "    ") + "\n"
   134  		i++
   135  	}
   136  
   137  	return lines
   138  }
   139  
   140  func usageText(cmd *cmds.Command) string {
   141  	s := ""
   142  	for i, arg := range cmd.Arguments {
   143  		if i != 0 {
   144  			s += " "
   145  		}
   146  		s += argUsageText(arg)
   147  	}
   148  
   149  	return s
   150  }
   151  
   152  func argUsageText(arg cmds.Argument) string {
   153  	s := arg.Name
   154  
   155  	if arg.Required {
   156  		s = fmt.Sprintf(requiredArg, s)
   157  	} else {
   158  		s = fmt.Sprintf(optionalArg, s)
   159  	}
   160  
   161  	if arg.Variadic {
   162  		s = fmt.Sprintf(variadicArg, s)
   163  	}
   164  
   165  	return s
   166  }
   167  
   168  func align(lines []string) []string {
   169  	longest := 0
   170  	for _, line := range lines {
   171  		length := len(line)
   172  		if length > longest {
   173  			longest = length
   174  		}
   175  	}
   176  
   177  	for i, line := range lines {
   178  		length := len(line)
   179  		if length > 0 {
   180  			lines[i] += strings.Repeat(" ", longest-length)
   181  		}
   182  	}
   183  
   184  	return lines
   185  }
   186  
   187  func indent(lines []string, prefix string) []string {
   188  	for i, line := range lines {
   189  		lines[i] = prefix + indentString(line, prefix)
   190  	}
   191  	return lines
   192  }
   193  
   194  func indentString(line string, prefix string) string {
   195  	return strings.Replace(line, "\n", "\n"+prefix, -1)
   196  }