...

Source file src/pkg/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 "%-11s"}} {{.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 interface{}) {
   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