Commit 2cab41d5 authored by Russ Cox's avatar Russ Cox

cmd/go: split out cmd/go/internal/help

This is one CL in a long sequence of changes to break up the
go command from one package into a plausible group of packages.

This sequence is concerned only with moving code, not changing
or cleaning up code. There will still be more cleanup after this sequence.

The entire sequence will be submitted together: it is not a goal
for the tree to build at every step.

For #18653.

Change-Id: I4cf05b076d81b780c87a31378523929b5da8964b
Reviewed-on: https://go-review.googlesource.com/36194Reviewed-by: default avatarDavid Crawshaw <crawshaw@golang.org>
parent 6dc9e31f
...@@ -43,6 +43,10 @@ type Command struct { ...@@ -43,6 +43,10 @@ type Command struct {
CustomFlags bool 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. // Name returns the command's name: the first word in the usage line.
func (c *Command) Name() string { func (c *Command) Name() string {
name := c.UsageLine name := c.UsageLine
......
// 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()
}
...@@ -2,11 +2,11 @@ ...@@ -2,11 +2,11 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package main package help
import "cmd/go/internal/base" import "cmd/go/internal/base"
var helpC = &base.Command{ var HelpC = &base.Command{
UsageLine: "c", UsageLine: "c",
Short: "calling between Go and C", Short: "calling between Go and C",
Long: ` Long: `
...@@ -28,7 +28,7 @@ the C or C++ compiler, respectively, to use. ...@@ -28,7 +28,7 @@ the C or C++ compiler, respectively, to use.
`, `,
} }
var helpPackages = &base.Command{ var HelpPackages = &base.Command{
UsageLine: "packages", UsageLine: "packages",
Short: "description of package lists", Short: "description of package lists",
Long: ` Long: `
...@@ -102,7 +102,7 @@ by the go tool, as are directories named "testdata". ...@@ -102,7 +102,7 @@ by the go tool, as are directories named "testdata".
`, `,
} }
var helpImportPath = &base.Command{ var HelpImportPath = &base.Command{
UsageLine: "importpath", UsageLine: "importpath",
Short: "import path syntax", Short: "import path syntax",
Long: ` Long: `
...@@ -279,7 +279,7 @@ See https://golang.org/s/go14customimport for details. ...@@ -279,7 +279,7 @@ See https://golang.org/s/go14customimport for details.
`, `,
} }
var helpGopath = &base.Command{ var HelpGopath = &base.Command{
UsageLine: "gopath", UsageLine: "gopath",
Short: "GOPATH environment variable", Short: "GOPATH environment variable",
Long: ` Long: `
...@@ -431,7 +431,7 @@ See https://golang.org/s/go15vendor for details. ...@@ -431,7 +431,7 @@ See https://golang.org/s/go15vendor for details.
`, `,
} }
var helpEnvironment = &base.Command{ var HelpEnvironment = &base.Command{
UsageLine: "environment", UsageLine: "environment",
Short: "environment variables", Short: "environment variables",
Long: ` Long: `
...@@ -513,7 +513,7 @@ Special-purpose environment variables: ...@@ -513,7 +513,7 @@ Special-purpose environment variables:
`, `,
} }
var helpFileType = &base.Command{ var HelpFileType = &base.Command{
UsageLine: "filetype", UsageLine: "filetype",
Short: "file types", Short: "file types",
Long: ` Long: `
...@@ -559,7 +559,7 @@ for more details. ...@@ -559,7 +559,7 @@ for more details.
`, `,
} }
var helpBuildmode = &base.Command{ var HelpBuildmode = &base.Command{
UsageLine: "buildmode", UsageLine: "buildmode",
Short: "description of build modes", Short: "description of build modes",
Long: ` Long: `
......
...@@ -5,14 +5,9 @@ ...@@ -5,14 +5,9 @@
package main package main
import ( import (
"bufio"
"bytes"
"cmd/go/internal/cfg"
"cmd/go/internal/base"
"flag" "flag"
"fmt" "fmt"
"go/build" "go/build"
"io"
"log" "log"
"os" "os"
"path" "path"
...@@ -20,17 +15,14 @@ import ( ...@@ -20,17 +15,14 @@ import (
"regexp" "regexp"
"runtime" "runtime"
"strings" "strings"
"text/template"
"unicode"
"unicode/utf8"
)
// Commands lists the available commands and help topics. "cmd/go/internal/base"
// The order here is the order in which they are printed by 'go help'. "cmd/go/internal/cfg"
var commands []*base.Command "cmd/go/internal/help"
)
func init() { func init() {
commands = []*base.Command{ base.Commands = []*base.Command{
cmdBuild, cmdBuild,
cmdClean, cmdClean,
cmdDoc, cmdDoc,
...@@ -48,13 +40,13 @@ func init() { ...@@ -48,13 +40,13 @@ func init() {
cmdVersion, cmdVersion,
cmdVet, cmdVet,
helpC, help.HelpC,
helpBuildmode, help.HelpBuildmode,
helpFileType, help.HelpFileType,
helpGopath, help.HelpGopath,
helpEnvironment, help.HelpEnvironment,
helpImportPath, help.HelpImportPath,
helpPackages, help.HelpPackages,
helpTestflag, helpTestflag,
helpTestfunc, helpTestfunc,
} }
...@@ -72,7 +64,7 @@ func main() { ...@@ -72,7 +64,7 @@ func main() {
} }
if args[0] == "help" { if args[0] == "help" {
help(args[1:]) help.Help(args[1:])
return return
} }
...@@ -115,7 +107,7 @@ func main() { ...@@ -115,7 +107,7 @@ func main() {
} }
} }
for _, cmd := range commands { for _, cmd := range base.Commands {
if cmd.Name() == args[0] && cmd.Runnable() { if cmd.Name() == args[0] && cmd.Runnable() {
cmd.Flag.Usage = func() { cmd.Usage() } cmd.Flag.Usage = func() { cmd.Usage() }
if cmd.CustomFlags { if cmd.CustomFlags {
...@@ -135,121 +127,6 @@ func main() { ...@@ -135,121 +127,6 @@ func main() {
base.Exit() 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() var usage func()
func init() { func init() {
...@@ -264,53 +141,10 @@ func mainUsage() { ...@@ -264,53 +141,10 @@ func mainUsage() {
strings.TrimSpace(testFlag2) + "\n") strings.TrimSpace(testFlag2) + "\n")
os.Exit(2) os.Exit(2)
} }
printUsage(os.Stderr) help.PrintUsage(os.Stderr)
os.Exit(2) 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 // importPathsNoDotExpansion returns the import paths to use for the given
// command line, but it does no ... expansion. // command line, but it does no ... expansion.
func importPathsNoDotExpansion(args []string) []string { func importPathsNoDotExpansion(args []string) []string {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment