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  }