github.com/knieriem/gointernal@v0.2.0-pre2/cmd/cli/internal/help/help.go (about)

     1  // Copyright 2017 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package help implements the “go help” command.
     6  package help
     7  
     8  import (
     9  	"bufio"
    10  	"fmt"
    11  	"io"
    12  	"os"
    13  	"strings"
    14  	"text/template"
    15  	"unicode"
    16  	"unicode/utf8"
    17  
    18  	"github.com/knieriem/gointernal/cmd/go/base"
    19  )
    20  
    21  // Help implements the 'help' command.
    22  func Help(w io.Writer, args []string) {
    23  	cmd := base.Prog
    24  	progName := cmd.UsageLine
    25  Args:
    26  	for i, arg := range args {
    27  		for _, sub := range cmd.Commands {
    28  			if sub.Name() == arg {
    29  				cmd = sub
    30  				continue Args
    31  			}
    32  		}
    33  
    34  		// helpSuccess is the help command using as many args as possible that would succeed.
    35  		helpSuccess := progName + " help"
    36  		if i > 0 {
    37  			helpSuccess += " " + strings.Join(args[:i], " ")
    38  		}
    39  		fmt.Fprintf(os.Stderr, "%s help %s: unknown help topic. Run '%s'.\n", progName, strings.Join(args, " "), helpSuccess)
    40  		base.SetExitStatus(2) // failed at 'go help cmd'
    41  		base.Exit()
    42  	}
    43  
    44  	if len(cmd.Commands) > 0 {
    45  		PrintUsage(os.Stdout, cmd)
    46  	} else {
    47  		tmpl(os.Stdout, helpTemplate, cmd)
    48  	}
    49  	// not exit 2: succeeded at 'go help cmd'.
    50  	return
    51  }
    52  
    53  var usageTemplate = `{{.Long | trim}}
    54  
    55  Usage:
    56  
    57  	{{.UsageLine | strip " --"}} <command> [arguments]
    58  
    59  The commands are:
    60  {{range .Commands}}{{if or (.Runnable) .Commands}}
    61  	{{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}
    62  
    63  Use "{{prog}} help{{with .LongName}} {{.}}{{end}} <command>" for more information about a command.
    64  {{if eq (.UsageLine) prog}}
    65  	{{- $anyTopics := false}}
    66  	{{- range .Commands}}{{if and (not .Runnable) (not .Commands)}}{{$anyTopics = true}}{{end}}
    67  	{{- end}}
    68  	{{- if $anyTopics}}
    69  Additional help topics:
    70  {{range .Commands}}{{if and (not .Runnable) (not .Commands)}}
    71  	{{.Name | printf "%-15s"}} {{.Short}}{{end}}{{end}}
    72  
    73  Use "{{prog}} help{{with .LongName}} {{.}}{{end}} <topic>" for more information about that topic.
    74  {{end}}{{end}}
    75  `
    76  
    77  var helpTemplate = `{{if .Runnable}}usage: {{.UsageLine | strip " --"}}
    78  
    79  {{end}}{{.Long | trim}}
    80  `
    81  
    82  var documentationTemplate = `{{range .}}{{if .Short}}{{.Short | capitalize}}
    83  
    84  {{end}}{{if .Commands}}` + usageTemplate + `{{else}}{{if .Runnable}}Usage:
    85  
    86  	{{.UsageLine | strip " --"}}
    87  
    88  {{end}}{{.Long | trim}}
    89  
    90  
    91  {{end}}{{end}}`
    92  
    93  // commentWriter writes a Go comment to the underlying io.Writer,
    94  // using line comment form (//).
    95  type commentWriter struct {
    96  	W            io.Writer
    97  	wroteSlashes bool // Wrote "//" at the beginning of the current line.
    98  }
    99  
   100  func (c *commentWriter) Write(p []byte) (int, error) {
   101  	var n int
   102  	for i, b := range p {
   103  		if !c.wroteSlashes {
   104  			s := "//"
   105  			if b != '\n' {
   106  				s = "// "
   107  			}
   108  			if _, err := io.WriteString(c.W, s); err != nil {
   109  				return n, err
   110  			}
   111  			c.wroteSlashes = true
   112  		}
   113  		n0, err := c.W.Write(p[i : i+1])
   114  		n += n0
   115  		if err != nil {
   116  			return n, err
   117  		}
   118  		if b == '\n' {
   119  			c.wroteSlashes = false
   120  		}
   121  	}
   122  	return len(p), nil
   123  }
   124  
   125  // An errWriter wraps a writer, recording whether a write error occurred.
   126  type errWriter struct {
   127  	w   io.Writer
   128  	err error
   129  }
   130  
   131  func (w *errWriter) Write(b []byte) (int, error) {
   132  	n, err := w.w.Write(b)
   133  	if err != nil {
   134  		w.err = err
   135  	}
   136  	return n, err
   137  }
   138  
   139  // tmpl executes the given template text on data, writing the result to w.
   140  func tmpl(w io.Writer, text string, data interface{}) {
   141  	t := template.New("top")
   142  	t.Funcs(template.FuncMap{
   143  		"prog": func() string {
   144  			return base.Prog.UsageLine
   145  		},
   146  		"strip": func(sub, s string) string {
   147  			return strings.Replace(s, sub, "", -1)
   148  		},
   149  		"trim":       strings.TrimSpace,
   150  		"capitalize": capitalize,
   151  	})
   152  	template.Must(t.Parse(text))
   153  	ew := &errWriter{w: w}
   154  	err := t.Execute(ew, data)
   155  	if ew.err != nil {
   156  		// I/O error writing. Ignore write on closed pipe.
   157  		if strings.Contains(ew.err.Error(), "pipe") {
   158  			base.SetExitStatus(1)
   159  			base.Exit()
   160  		}
   161  		base.Fatalf("writing output: %v", ew.err)
   162  	}
   163  	if err != nil {
   164  		panic(err)
   165  	}
   166  }
   167  
   168  func capitalize(s string) string {
   169  	if s == "" {
   170  		return s
   171  	}
   172  	r, n := utf8.DecodeRuneInString(s)
   173  	return string(unicode.ToTitle(r)) + s[n:]
   174  }
   175  
   176  func PrintUsage(w io.Writer, cmd *base.Command) {
   177  	bw := bufio.NewWriter(w)
   178  	tmpl(bw, usageTemplate, cmd)
   179  	bw.Flush()
   180  }