Source file src/cmd/go/internal/help/help.go

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

View as plain text