github.com/henvic/wedeploycli@v1.7.6-0.20200319005353-3630f582f284/command/internal/template/template.go (about) 1 package template 2 3 import ( 4 "bytes" 5 "fmt" 6 "reflect" 7 "runtime" 8 "strings" 9 10 "github.com/henvic/wedeploycli/color" 11 "github.com/henvic/wedeploycli/formatter" 12 "github.com/spf13/cobra" 13 "github.com/spf13/pflag" 14 ) 15 16 // Configure template for cobra commands 17 func Configure(rootCmd *cobra.Command) { 18 cobra.AddTemplateFunc("printCommandsAndFlags", printCommandsAndFlags) 19 rootCmd.SetUsageTemplate(`{{printCommandsAndFlags .}}`) 20 rootCmd.SetHelpTemplate(`{{if not (eq .CommandPath "` + rootCmd.Name() + `")}}{{with or .Long .Short }}{{color FgYellow BgHiYellow "!"}} {{. | trim | color FgHiYellow}} 21 {{end}}{{end}}{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`) 22 } 23 24 type usagePrinter struct { 25 cmd *cobra.Command 26 cs []*cobra.Command 27 f *pflag.FlagSet 28 buf *bytes.Buffer 29 tw *formatter.TabWriter 30 flags flagsDescriptions 31 showFlagsParamField bool 32 33 longHelp bool 34 } 35 36 type flagsDescriptions []flagDescription 37 38 type flagDescription struct { 39 Name string 40 Description []byte 41 Used bool 42 } 43 44 func colorSpacingOffset() string { 45 if formatter.Human { 46 return " " 47 } 48 49 return "" 50 } 51 52 func printCommandsAndFlags(cmd *cobra.Command) string { 53 up := &usagePrinter{ 54 cmd: cmd, 55 cs: cmd.Commands(), 56 f: cmd.Flags(), 57 } 58 59 switch longHelp, err := up.f.GetBool("long-help"); { 60 case err != nil: 61 panic(err) 62 case longHelp: 63 up.longHelp = true 64 } 65 66 up.buf = new(bytes.Buffer) 67 up.tw = formatter.NewTabWriter(up.buf) 68 up.printAll() 69 return up.buf.String() 70 } 71 72 func (up *usagePrinter) printAll() { 73 var cmdPart = " [command]" 74 75 if len(up.cs) == 0 { 76 cmdPart = "" 77 } 78 79 useLine := strings.TrimSuffix(up.cmd.UseLine(), " [flags]") 80 81 usage := fmt.Sprintf("\n Usage: %s%s", 82 useLine, 83 cmdPart) 84 85 if !up.cmd.DisableFlagsInUseLine && up.cmd.HasAvailableFlags() && !strings.Contains(usage, "[flag]") { 86 usage += " [flag]" 87 } 88 89 if up.cmd.Args == nil || runtime.FuncForPC(reflect.ValueOf(up.cmd.Args).Pointer()).Name() != 90 runtime.FuncForPC(reflect.ValueOf(cobra.NoArgs).Pointer()).Name() { 91 usage += " [<args>]" 92 } 93 94 _, _ = fmt.Fprintf(up.buf, "%s\n\n", usage) 95 96 up.printCommands() 97 up.printFlags() 98 _ = up.tw.Flush() 99 up.printExamples() 100 } 101 102 func (up *usagePrinter) printExamples() { 103 if up.cmd.Example == "" { 104 return 105 } 106 107 _, _ = fmt.Fprintf(up.buf, 108 "\n%s%s\n\n", 109 color.Format(color.FgHiBlack, " Examples\n"), 110 up.cmd.Example, 111 ) 112 } 113 114 func (up *usagePrinter) printCommands() { 115 if len(up.cs) == 0 { 116 return 117 } 118 119 _, _ = fmt.Fprint(up.tw, color.Format(color.FgHiBlack, " Command\t"+colorSpacingOffset()+"Description")+"\n") 120 121 for _, c := range up.cs { 122 if up.longHelp || c.IsAvailableCommand() { 123 _, _ = fmt.Fprintf(up.tw, " %v\t%v\n", c.Name(), c.Short) 124 } 125 } 126 127 _, _ = fmt.Fprintln(up.tw, "\t") // \t here keeps the alignment between commands and flags 128 } 129 130 func (up *usagePrinter) printFlags() { 131 up.f.VisitAll(func(flag *pflag.Flag) { 132 if (up.longHelp || !flag.Hidden) && flag.Value.Type() != "bool" { 133 up.showFlagsParamField = true 134 } 135 }) 136 137 if up.showFlagsParamField { 138 _, _ = fmt.Fprintf(up.tw, "%s\n", 139 color.Format(color.FgHiBlack, 140 " Flag\t"+colorSpacingOffset()+"Parameter\t"+colorSpacingOffset()+"Description")) 141 } else { 142 _, _ = fmt.Fprintf(up.tw, "%s\n", 143 color.Format(color.FgHiBlack, 144 " Flag\t"+colorSpacingOffset()+"Description")) 145 } 146 147 up.f.VisitAll(up.preparePrintFlag) 148 149 var begin = up.useFlagsHelpDescriptionFiltered([]string{ 150 "service", 151 "environment", 152 "project", 153 "remote", 154 "url", 155 }) 156 157 var end = up.useFlagsHelpDescriptionFiltered([]string{ 158 "help", 159 "long-help", 160 "quiet", 161 "no-color", 162 "no-tty", 163 "verbose", 164 "defer-verbose", 165 "no-verbose-requests", 166 }) 167 168 var middle = up.useFlagsHelpDescription() 169 _, _ = fmt.Fprintf(up.tw, "%s%s%s", string(begin), string(middle), string(end)) 170 } 171 172 func (up *usagePrinter) useFlagsHelpDescriptionFiltered(list []string) []byte { 173 var buf bytes.Buffer 174 175 for _, filtered := range list { 176 for i, flag := range up.flags { 177 if flag.Name == filtered && !flag.Used { 178 up.flags[i].Used = true 179 buf.Write(flag.Description) 180 } 181 } 182 } 183 184 return buf.Bytes() 185 } 186 187 func (up *usagePrinter) useFlagsHelpDescription() []byte { 188 var buf bytes.Buffer 189 190 for i, flag := range up.flags { 191 if !flag.Used { 192 up.flags[i].Used = true 193 buf.Write(flag.Description) 194 } 195 } 196 197 return buf.Bytes() 198 } 199 200 func (up *usagePrinter) preparePrintFlag(flag *pflag.Flag) { 201 if flag.Deprecated != "" || (!up.longHelp && flag.Hidden) { 202 return 203 } 204 205 var buf = bytes.NewBufferString(" ") 206 207 if flag.Shorthand != "" && flag.ShorthandDeprecated == "" { 208 buf.WriteString(fmt.Sprintf("-%s, ", flag.Shorthand)) 209 } else { 210 buf.WriteString(" ") 211 } 212 213 buf.WriteString(fmt.Sprintf("--%s", flag.Name)) 214 215 if flag.NoOptDefVal != "" { 216 switch flag.Value.Type() { 217 case "string": 218 buf.WriteString(fmt.Sprintf("[=\"%s\"]", flag.NoOptDefVal)) 219 case "bool": 220 if flag.NoOptDefVal != "true" { 221 buf.WriteString(fmt.Sprintf("[=%s]", flag.NoOptDefVal)) 222 } 223 default: 224 buf.WriteString(fmt.Sprintf("[=%s]", flag.NoOptDefVal)) 225 } 226 } 227 228 var flagType = flag.Value.Type() 229 230 if flagType == "bool" { 231 flagType = "" 232 } 233 234 if up.showFlagsParamField { 235 buf.WriteString(fmt.Sprintf("\t%s\t%s", flagType, flag.Usage)) 236 } else { 237 buf.WriteString(fmt.Sprintf("\t%s", flag.Usage)) 238 } 239 240 if !isDefaultFlagValueZero(flag) { 241 if flag.Value.Type() == "string" { 242 buf.WriteString(fmt.Sprintf(" (default %q)", flag.DefValue)) 243 } else { 244 buf.WriteString(fmt.Sprintf(" (default %s)", flag.DefValue)) 245 } 246 } 247 248 buf.WriteString("\n") 249 up.flags = append(up.flags, flagDescription{ 250 Name: flag.Name, 251 Description: buf.Bytes(), 252 }) 253 } 254 255 func isDefaultFlagValueZero(f *pflag.Flag) bool { 256 switch f.DefValue { 257 case "", "0", "0s", "false", "[]": 258 return true 259 } 260 261 return false 262 }