summaryrefslogtreecommitdiffstats
path: root/src/cmd/go/internal/help/help.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/go/internal/help/help.go')
-rw-r--r--src/cmd/go/internal/help/help.go195
1 files changed, 195 insertions, 0 deletions
diff --git a/src/cmd/go/internal/help/help.go b/src/cmd/go/internal/help/help.go
new file mode 100644
index 0000000..804c910
--- /dev/null
+++ b/src/cmd/go/internal/help/help.go
@@ -0,0 +1,195 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package help implements the “go help” command.
+package help
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "os"
+ "strings"
+ "text/template"
+ "unicode"
+ "unicode/utf8"
+
+ "cmd/go/internal/base"
+ "cmd/go/internal/modload"
+)
+
+// Help implements the 'help' command.
+func Help(w io.Writer, args []string) {
+ // 'go help documentation' generates doc.go.
+ if len(args) == 1 && args[0] == "documentation" {
+ fmt.Fprintln(w, "// Copyright 2011 The Go Authors. All rights reserved.")
+ fmt.Fprintln(w, "// Use of this source code is governed by a BSD-style")
+ fmt.Fprintln(w, "// license that can be found in the LICENSE file.")
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, "// Code generated by mkalldocs.sh; DO NOT EDIT.")
+ fmt.Fprintln(w, "// Edit the documentation in other files and rerun mkalldocs.sh to generate this one.")
+ fmt.Fprintln(w)
+ buf := new(strings.Builder)
+ PrintUsage(buf, base.Go)
+ usage := &base.Command{Long: buf.String()}
+ cmds := []*base.Command{usage}
+ for _, cmd := range base.Go.Commands {
+ // Avoid duplication of the "get" documentation.
+ if cmd.UsageLine == "module-get" && modload.Enabled() {
+ continue
+ } else if cmd.UsageLine == "gopath-get" && !modload.Enabled() {
+ continue
+ }
+ cmds = append(cmds, cmd)
+ cmds = append(cmds, cmd.Commands...)
+ }
+ tmpl(&commentWriter{W: w}, documentationTemplate, cmds)
+ fmt.Fprintln(w, "package main")
+ return
+ }
+
+ cmd := base.Go
+Args:
+ for i, arg := range args {
+ for _, sub := range cmd.Commands {
+ if sub.Name() == arg {
+ cmd = sub
+ continue Args
+ }
+ }
+
+ // helpSuccess is the help command using as many args as possible that would succeed.
+ helpSuccess := "go help"
+ if i > 0 {
+ helpSuccess += " " + strings.Join(args[:i], " ")
+ }
+ fmt.Fprintf(os.Stderr, "go help %s: unknown help topic. Run '%s'.\n", strings.Join(args, " "), helpSuccess)
+ base.SetExitStatus(2) // failed at 'go help cmd'
+ base.Exit()
+ }
+
+ if len(cmd.Commands) > 0 {
+ PrintUsage(os.Stdout, cmd)
+ } else {
+ tmpl(os.Stdout, helpTemplate, cmd)
+ }
+ // not exit 2: succeeded at 'go help cmd'.
+ return
+}
+
+var usageTemplate = `{{.Long | trim}}
+
+Usage:
+
+ {{.UsageLine}} <command> [arguments]
+
+The commands are:
+{{range .Commands}}{{if or (.Runnable) .Commands}}
+ {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}
+
+Use "go help{{with .LongName}} {{.}}{{end}} <command>" for more information about a command.
+{{if eq (.UsageLine) "go"}}
+Additional help topics:
+{{range .Commands}}{{if and (not .Runnable) (not .Commands)}}
+ {{.Name | printf "%-15s"}} {{.Short}}{{end}}{{end}}
+
+Use "go help{{with .LongName}} {{.}}{{end}} <topic>" for more information about that topic.
+{{end}}
+`
+
+var helpTemplate = `{{if .Runnable}}usage: {{.UsageLine}}
+
+{{end}}{{.Long | trim}}
+`
+
+var documentationTemplate = `{{range .}}{{if .Short}}{{.Short | capitalize}}
+
+{{end}}{{if .Commands}}` + usageTemplate + `{{else}}{{if .Runnable}}Usage:
+
+ {{.UsageLine}}
+
+{{end}}{{.Long | trim}}
+
+
+{{end}}{{end}}`
+
+// commentWriter writes a Go comment to the underlying io.Writer,
+// using line comment form (//).
+type commentWriter struct {
+ W io.Writer
+ wroteSlashes bool // Wrote "//" at the beginning of the current line.
+}
+
+func (c *commentWriter) Write(p []byte) (int, error) {
+ var n int
+ for i, b := range p {
+ if !c.wroteSlashes {
+ s := "//"
+ if b != '\n' {
+ s = "// "
+ }
+ if _, err := io.WriteString(c.W, s); err != nil {
+ return n, err
+ }
+ c.wroteSlashes = true
+ }
+ n0, err := c.W.Write(p[i : i+1])
+ n += n0
+ if err != nil {
+ return n, err
+ }
+ if b == '\n' {
+ c.wroteSlashes = false
+ }
+ }
+ return len(p), nil
+}
+
+// An errWriter wraps a writer, recording whether a write error occurred.
+type errWriter struct {
+ w io.Writer
+ err error
+}
+
+func (w *errWriter) Write(b []byte) (int, error) {
+ n, err := w.w.Write(b)
+ if err != nil {
+ w.err = err
+ }
+ return n, err
+}
+
+// tmpl executes the given template text on data, writing the result to w.
+func tmpl(w io.Writer, text string, data any) {
+ t := template.New("top")
+ t.Funcs(template.FuncMap{"trim": strings.TrimSpace, "capitalize": capitalize})
+ template.Must(t.Parse(text))
+ ew := &errWriter{w: w}
+ err := t.Execute(ew, data)
+ if ew.err != nil {
+ // I/O error writing. Ignore write on closed pipe.
+ if strings.Contains(ew.err.Error(), "pipe") {
+ base.SetExitStatus(1)
+ base.Exit()
+ }
+ base.Fatalf("writing output: %v", ew.err)
+ }
+ if err != nil {
+ panic(err)
+ }
+}
+
+func capitalize(s string) string {
+ if s == "" {
+ return s
+ }
+ r, n := utf8.DecodeRuneInString(s)
+ return string(unicode.ToTitle(r)) + s[n:]
+}
+
+func PrintUsage(w io.Writer, cmd *base.Command) {
+ bw := bufio.NewWriter(w)
+ tmpl(bw, usageTemplate, cmd)
+ bw.Flush()
+}