diff --git a/src/cmd/go/internal/base/base.go b/src/cmd/go/internal/base/base.go index 081c78b51e..4638a99eda 100644 --- a/src/cmd/go/internal/base/base.go +++ b/src/cmd/go/internal/base/base.go @@ -43,6 +43,10 @@ type Command struct { CustomFlags bool } +// Commands lists the available commands and help topics. +// The order here is the order in which they are printed by 'go help'. +var Commands []*Command + // Name returns the command's name: the first word in the usage line. func (c *Command) Name() string { name := c.UsageLine diff --git a/src/cmd/go/internal/help/help.go b/src/cmd/go/internal/help/help.go new file mode 100644 index 0000000000..1558fa4587 --- /dev/null +++ b/src/cmd/go/internal/help/help.go @@ -0,0 +1,178 @@ +// 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 "go help". +package help + +import ( + "bufio" + "bytes" + "fmt" + "html/template" + "io" + "os" + "strings" + "unicode" + "unicode/utf8" + + "cmd/go/internal/base" +) + +// Help implements the 'help' command. +func Help(args []string) { + if len(args) == 0 { + PrintUsage(os.Stdout) + // not exit 2: succeeded at 'go help'. + return + } + if len(args) != 1 { + fmt.Fprintf(os.Stderr, "usage: go help command\n\nToo many arguments given.\n") + os.Exit(2) // failed at 'go help' + } + + arg := args[0] + + // 'go help documentation' generates doc.go. + if arg == "documentation" { + fmt.Println("// Copyright 2011 The Go Authors. All rights reserved.") + fmt.Println("// Use of this source code is governed by a BSD-style") + fmt.Println("// license that can be found in the LICENSE file.") + fmt.Println() + fmt.Println("// DO NOT EDIT THIS FILE. GENERATED BY mkalldocs.sh.") + fmt.Println("// Edit the documentation in other files and rerun mkalldocs.sh to generate this one.") + fmt.Println() + buf := new(bytes.Buffer) + PrintUsage(buf) + usage := &base.Command{Long: buf.String()} + tmpl(&commentWriter{W: os.Stdout}, documentationTemplate, append([]*base.Command{usage}, base.Commands...)) + fmt.Println("package main") + return + } + + for _, cmd := range base.Commands { + if cmd.Name() == arg { + tmpl(os.Stdout, helpTemplate, cmd) + // not exit 2: succeeded at 'go help cmd'. + return + } + } + + fmt.Fprintf(os.Stderr, "Unknown help topic %#q. Run 'go help'.\n", arg) + os.Exit(2) // failed at 'go help cmd' +} + +var usageTemplate = `Go is a tool for managing Go source code. + +Usage: + + go command [arguments] + +The commands are: +{{range .}}{{if .Runnable}} + {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}} + +Use "go help [command]" for more information about a command. + +Additional help topics: +{{range .}}{{if not .Runnable}} + {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}} + +Use "go help [topic]" for more information about that topic. + +` + +var helpTemplate = `{{if .Runnable}}usage: go {{.UsageLine}} + +{{end}}{{.Long | trim}} +` + +var documentationTemplate = `{{range .}}{{if .Short}}{{.Short | capitalize}} + +{{end}}{{if .Runnable}}Usage: + + go {{.UsageLine}} + +{{end}}{{.Long | trim}} + + +{{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 interface{}) { + 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") { + os.Exit(1) + } + 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) { + bw := bufio.NewWriter(w) + tmpl(bw, usageTemplate, base.Commands) + bw.Flush() +} diff --git a/src/cmd/go/help.go b/src/cmd/go/internal/help/helpdoc.go similarity index 98% rename from src/cmd/go/help.go rename to src/cmd/go/internal/help/helpdoc.go index 734916c453..37e2b3b28f 100644 --- a/src/cmd/go/help.go +++ b/src/cmd/go/internal/help/helpdoc.go @@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package main +package help import "cmd/go/internal/base" -var helpC = &base.Command{ +var HelpC = &base.Command{ UsageLine: "c", Short: "calling between Go and C", Long: ` @@ -28,7 +28,7 @@ the C or C++ compiler, respectively, to use. `, } -var helpPackages = &base.Command{ +var HelpPackages = &base.Command{ UsageLine: "packages", Short: "description of package lists", Long: ` @@ -102,7 +102,7 @@ by the go tool, as are directories named "testdata". `, } -var helpImportPath = &base.Command{ +var HelpImportPath = &base.Command{ UsageLine: "importpath", Short: "import path syntax", Long: ` @@ -279,7 +279,7 @@ See https://golang.org/s/go14customimport for details. `, } -var helpGopath = &base.Command{ +var HelpGopath = &base.Command{ UsageLine: "gopath", Short: "GOPATH environment variable", Long: ` @@ -431,7 +431,7 @@ See https://golang.org/s/go15vendor for details. `, } -var helpEnvironment = &base.Command{ +var HelpEnvironment = &base.Command{ UsageLine: "environment", Short: "environment variables", Long: ` @@ -513,7 +513,7 @@ Special-purpose environment variables: `, } -var helpFileType = &base.Command{ +var HelpFileType = &base.Command{ UsageLine: "filetype", Short: "file types", Long: ` @@ -559,7 +559,7 @@ for more details. `, } -var helpBuildmode = &base.Command{ +var HelpBuildmode = &base.Command{ UsageLine: "buildmode", Short: "description of build modes", Long: ` diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go index aa019f2cea..626e7287f8 100644 --- a/src/cmd/go/main.go +++ b/src/cmd/go/main.go @@ -5,14 +5,9 @@ package main import ( - "bufio" - "bytes" - "cmd/go/internal/cfg" - "cmd/go/internal/base" "flag" "fmt" "go/build" - "io" "log" "os" "path" @@ -20,17 +15,14 @@ import ( "regexp" "runtime" "strings" - "text/template" - "unicode" - "unicode/utf8" + + "cmd/go/internal/base" + "cmd/go/internal/cfg" + "cmd/go/internal/help" ) -// Commands lists the available commands and help topics. -// The order here is the order in which they are printed by 'go help'. -var commands []*base.Command - func init() { - commands = []*base.Command{ + base.Commands = []*base.Command{ cmdBuild, cmdClean, cmdDoc, @@ -48,13 +40,13 @@ func init() { cmdVersion, cmdVet, - helpC, - helpBuildmode, - helpFileType, - helpGopath, - helpEnvironment, - helpImportPath, - helpPackages, + help.HelpC, + help.HelpBuildmode, + help.HelpFileType, + help.HelpGopath, + help.HelpEnvironment, + help.HelpImportPath, + help.HelpPackages, helpTestflag, helpTestfunc, } @@ -72,7 +64,7 @@ func main() { } if args[0] == "help" { - help(args[1:]) + help.Help(args[1:]) return } @@ -115,7 +107,7 @@ func main() { } } - for _, cmd := range commands { + for _, cmd := range base.Commands { if cmd.Name() == args[0] && cmd.Runnable() { cmd.Flag.Usage = func() { cmd.Usage() } if cmd.CustomFlags { @@ -135,121 +127,6 @@ func main() { base.Exit() } -var usageTemplate = `Go is a tool for managing Go source code. - -Usage: - - go command [arguments] - -The commands are: -{{range .}}{{if .Runnable}} - {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}} - -Use "go help [command]" for more information about a command. - -Additional help topics: -{{range .}}{{if not .Runnable}} - {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}} - -Use "go help [topic]" for more information about that topic. - -` - -var helpTemplate = `{{if .Runnable}}usage: go {{.UsageLine}} - -{{end}}{{.Long | trim}} -` - -var documentationTemplate = `{{range .}}{{if .Short}}{{.Short | capitalize}} - -{{end}}{{if .Runnable}}Usage: - - go {{.UsageLine}} - -{{end}}{{.Long | trim}} - - -{{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 interface{}) { - 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") { - os.Exit(1) - } - 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) { - bw := bufio.NewWriter(w) - tmpl(bw, usageTemplate, commands) - bw.Flush() -} - var usage func() func init() { @@ -264,53 +141,10 @@ func mainUsage() { strings.TrimSpace(testFlag2) + "\n") os.Exit(2) } - printUsage(os.Stderr) + help.PrintUsage(os.Stderr) os.Exit(2) } -// help implements the 'help' command. -func help(args []string) { - if len(args) == 0 { - printUsage(os.Stdout) - // not exit 2: succeeded at 'go help'. - return - } - if len(args) != 1 { - fmt.Fprintf(os.Stderr, "usage: go help command\n\nToo many arguments given.\n") - os.Exit(2) // failed at 'go help' - } - - arg := args[0] - - // 'go help documentation' generates doc.go. - if arg == "documentation" { - fmt.Println("// Copyright 2011 The Go Authors. All rights reserved.") - fmt.Println("// Use of this source code is governed by a BSD-style") - fmt.Println("// license that can be found in the LICENSE file.") - fmt.Println() - fmt.Println("// DO NOT EDIT THIS FILE. GENERATED BY mkalldocs.sh.") - fmt.Println("// Edit the documentation in other files and rerun mkalldocs.sh to generate this one.") - fmt.Println() - buf := new(bytes.Buffer) - printUsage(buf) - usage := &base.Command{Long: buf.String()} - tmpl(&commentWriter{W: os.Stdout}, documentationTemplate, append([]*base.Command{usage}, commands...)) - fmt.Println("package main") - return - } - - for _, cmd := range commands { - if cmd.Name() == arg { - tmpl(os.Stdout, helpTemplate, cmd) - // not exit 2: succeeded at 'go help cmd'. - return - } - } - - fmt.Fprintf(os.Stderr, "Unknown help topic %#q. Run 'go help'.\n", arg) - os.Exit(2) // failed at 'go help cmd' -} - // importPathsNoDotExpansion returns the import paths to use for the given // command line, but it does no ... expansion. func importPathsNoDotExpansion(args []string) []string {