github.com/goplus/igop@v0.25.0/cmd/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 ``igop help'' command.
     6  package help
     7  
     8  import (
     9  	"bufio"
    10  	"fmt"
    11  	"io"
    12  	"log"
    13  	"os"
    14  	"strings"
    15  	"text/template"
    16  	"unicode"
    17  	"unicode/utf8"
    18  
    19  	"github.com/goplus/igop/cmd/internal/base"
    20  )
    21  
    22  // Help implements the 'help' command.
    23  func Help(w io.Writer, args []string) {
    24  	cmd := base.Igop
    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 := "igop help"
    36  		if i > 0 {
    37  			helpSuccess += " " + strings.Join(args[:i], " ")
    38  		}
    39  		fmt.Fprintf(os.Stderr, "igop help %s: unknown help topic. Run '%s'.\n", strings.Join(args, " "), helpSuccess)
    40  		os.Exit(2)
    41  	}
    42  
    43  	if len(cmd.Commands) > 0 {
    44  		PrintUsage(w, cmd)
    45  	} else {
    46  		cmd.Usage(w)
    47  	}
    48  	// not exit 2: succeeded at 'igop help cmd'.
    49  	return
    50  }
    51  
    52  var usageTemplate = `{{.Short | trim}}
    53  
    54  Usage:
    55  
    56  	{{.UsageLine}} <command> [arguments]
    57  
    58  The commands are:
    59  {{range .Commands}}{{if or (.Runnable) .Commands}}
    60  	{{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}
    61  
    62  Use "igop help{{with .LongName}} {{.}}{{end}} <command>" for more information about a command.
    63  
    64  `
    65  
    66  // commentWriter writes a Go comment to the underlying io.Writer,
    67  // using line comment form (//).
    68  type commentWriter struct {
    69  	W            io.Writer
    70  	wroteSlashes bool // Wrote "//" at the beginning of the current line.
    71  }
    72  
    73  func (c *commentWriter) Write(p []byte) (int, error) {
    74  	var n int
    75  	for i, b := range p {
    76  		if !c.wroteSlashes {
    77  			s := "//"
    78  			if b != '\n' {
    79  				s = "// "
    80  			}
    81  			if _, err := io.WriteString(c.W, s); err != nil {
    82  				return n, err
    83  			}
    84  			c.wroteSlashes = true
    85  		}
    86  		n0, err := c.W.Write(p[i : i+1])
    87  		n += n0
    88  		if err != nil {
    89  			return n, err
    90  		}
    91  		if b == '\n' {
    92  			c.wroteSlashes = false
    93  		}
    94  	}
    95  	return len(p), nil
    96  }
    97  
    98  // An errWriter wraps a writer, recording whether a write error occurred.
    99  type errWriter struct {
   100  	w   io.Writer
   101  	err error
   102  }
   103  
   104  func (w *errWriter) Write(b []byte) (int, error) {
   105  	n, err := w.w.Write(b)
   106  	if err != nil {
   107  		w.err = err
   108  	}
   109  	return n, err
   110  }
   111  
   112  // tmpl executes the given template text on data, writing the result to w.
   113  func tmpl(w io.Writer, text string, data interface{}) {
   114  	t := template.New("top")
   115  	t.Funcs(template.FuncMap{"trim": strings.TrimSpace, "capitalize": capitalize})
   116  	template.Must(t.Parse(text))
   117  	ew := &errWriter{w: w}
   118  	err := t.Execute(ew, data)
   119  	if ew.err != nil {
   120  		// I/O error writing. Ignore write on closed pipe.
   121  		if strings.Contains(ew.err.Error(), "pipe") {
   122  			os.Exit(1)
   123  		}
   124  		log.Fatalf("writing output: %v", ew.err)
   125  	}
   126  	if err != nil {
   127  		panic(err)
   128  	}
   129  }
   130  
   131  func capitalize(s string) string {
   132  	if s == "" {
   133  		return s
   134  	}
   135  	r, n := utf8.DecodeRuneInString(s)
   136  	return string(unicode.ToTitle(r)) + s[n:]
   137  }
   138  
   139  // PrintUsage prints usage information.
   140  func PrintUsage(w io.Writer, cmd *base.Command) {
   141  	bw := bufio.NewWriter(w)
   142  	tmpl(bw, usageTemplate, cmd)
   143  	bw.Flush()
   144  }