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

View as plain text