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