github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/go/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 "fmt" 11 "io" 12 "os" 13 "strings" 14 "text/template" 15 "unicode" 16 "unicode/utf8" 17 18 "github.com/go-asm/go/cmd/go/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 }