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