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