mirror of
https://github.com/octoleo/syncthing.git
synced 2024-12-23 03:18:59 +00:00
276 lines
5.3 KiB
Go
276 lines
5.3 KiB
Go
// Copyright 2012 Jesse van den Kieboom. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package flags
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"reflect"
|
|
"strings"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
type alignmentInfo struct {
|
|
maxLongLen int
|
|
hasShort bool
|
|
hasValueName bool
|
|
terminalColumns int
|
|
}
|
|
|
|
func (p *Parser) getAlignmentInfo() alignmentInfo {
|
|
ret := alignmentInfo{
|
|
maxLongLen: 0,
|
|
hasShort: false,
|
|
hasValueName: false,
|
|
terminalColumns: getTerminalColumns(),
|
|
}
|
|
|
|
if ret.terminalColumns <= 0 {
|
|
ret.terminalColumns = 80
|
|
}
|
|
|
|
p.eachActiveGroup(func(grp *Group) {
|
|
for _, info := range grp.options {
|
|
if info.ShortName != 0 {
|
|
ret.hasShort = true
|
|
}
|
|
|
|
lv := utf8.RuneCountInString(info.ValueName)
|
|
|
|
if lv != 0 {
|
|
ret.hasValueName = true
|
|
}
|
|
|
|
l := utf8.RuneCountInString(info.LongName) + lv
|
|
|
|
if l > ret.maxLongLen {
|
|
ret.maxLongLen = l
|
|
}
|
|
}
|
|
})
|
|
|
|
return ret
|
|
}
|
|
|
|
func (p *Parser) writeHelpOption(writer *bufio.Writer, option *Option, info alignmentInfo) {
|
|
line := &bytes.Buffer{}
|
|
|
|
distanceBetweenOptionAndDescription := 2
|
|
paddingBeforeOption := 2
|
|
|
|
line.WriteString(strings.Repeat(" ", paddingBeforeOption))
|
|
|
|
if option.ShortName != 0 {
|
|
line.WriteRune(defaultShortOptDelimiter)
|
|
line.WriteRune(option.ShortName)
|
|
} else if info.hasShort {
|
|
line.WriteString(" ")
|
|
}
|
|
|
|
descstart := info.maxLongLen + paddingBeforeOption + distanceBetweenOptionAndDescription
|
|
|
|
if info.hasShort {
|
|
descstart += 2
|
|
}
|
|
|
|
if info.maxLongLen > 0 {
|
|
descstart += 4
|
|
}
|
|
|
|
if info.hasValueName {
|
|
descstart += 3
|
|
}
|
|
|
|
if len(option.LongName) > 0 {
|
|
if option.ShortName != 0 {
|
|
line.WriteString(", ")
|
|
} else if info.hasShort {
|
|
line.WriteString(" ")
|
|
}
|
|
|
|
line.WriteString(defaultLongOptDelimiter)
|
|
line.WriteString(option.LongName)
|
|
}
|
|
|
|
if option.canArgument() {
|
|
line.WriteRune(defaultNameArgDelimiter)
|
|
|
|
if len(option.ValueName) > 0 {
|
|
line.WriteString(option.ValueName)
|
|
}
|
|
}
|
|
|
|
written := line.Len()
|
|
line.WriteTo(writer)
|
|
|
|
if option.Description != "" {
|
|
dw := descstart - written
|
|
writer.WriteString(strings.Repeat(" ", dw))
|
|
|
|
def := ""
|
|
defs := option.Default
|
|
|
|
if len(option.DefaultMask) != 0 {
|
|
if option.DefaultMask != "-" {
|
|
def = option.DefaultMask
|
|
}
|
|
} else if len(defs) == 0 && option.canArgument() {
|
|
var showdef bool
|
|
|
|
switch option.field.Type.Kind() {
|
|
case reflect.Func, reflect.Ptr:
|
|
showdef = !option.value.IsNil()
|
|
case reflect.Slice, reflect.String, reflect.Array:
|
|
showdef = option.value.Len() > 0
|
|
case reflect.Map:
|
|
showdef = !option.value.IsNil() && option.value.Len() > 0
|
|
default:
|
|
zeroval := reflect.Zero(option.field.Type)
|
|
showdef = !reflect.DeepEqual(zeroval.Interface(), option.value.Interface())
|
|
}
|
|
|
|
if showdef {
|
|
def, _ = convertToString(option.value, option.tag)
|
|
}
|
|
} else if len(defs) != 0 {
|
|
def = strings.Join(defs, ", ")
|
|
}
|
|
|
|
var desc string
|
|
|
|
if def != "" {
|
|
desc = fmt.Sprintf("%s (%v)", option.Description, def)
|
|
} else {
|
|
desc = option.Description
|
|
}
|
|
|
|
writer.WriteString(wrapText(desc,
|
|
info.terminalColumns-descstart,
|
|
strings.Repeat(" ", descstart)))
|
|
}
|
|
|
|
writer.WriteString("\n")
|
|
}
|
|
|
|
func maxCommandLength(s []*Command) int {
|
|
if len(s) == 0 {
|
|
return 0
|
|
}
|
|
|
|
ret := len(s[0].Name)
|
|
|
|
for _, v := range s[1:] {
|
|
l := len(v.Name)
|
|
|
|
if l > ret {
|
|
ret = l
|
|
}
|
|
}
|
|
|
|
return ret
|
|
}
|
|
|
|
// WriteHelp writes a help message containing all the possible options and
|
|
// their descriptions to the provided writer. Note that the HelpFlag parser
|
|
// option provides a convenient way to add a -h/--help option group to the
|
|
// command line parser which will automatically show the help messages using
|
|
// this method.
|
|
func (p *Parser) WriteHelp(writer io.Writer) {
|
|
if writer == nil {
|
|
return
|
|
}
|
|
|
|
wr := bufio.NewWriter(writer)
|
|
aligninfo := p.getAlignmentInfo()
|
|
|
|
cmd := p.Command
|
|
|
|
for cmd.Active != nil {
|
|
cmd = cmd.Active
|
|
}
|
|
|
|
if p.Name != "" {
|
|
wr.WriteString("Usage:\n")
|
|
wr.WriteString(" ")
|
|
|
|
allcmd := p.Command
|
|
|
|
for allcmd != nil {
|
|
var usage string
|
|
|
|
if allcmd == p.Command {
|
|
if len(p.Usage) != 0 {
|
|
usage = p.Usage
|
|
} else {
|
|
usage = "[OPTIONS]"
|
|
}
|
|
} else if us, ok := allcmd.data.(Usage); ok {
|
|
usage = us.Usage()
|
|
} else {
|
|
usage = fmt.Sprintf("[%s-OPTIONS]", allcmd.Name)
|
|
}
|
|
|
|
if len(usage) != 0 {
|
|
fmt.Fprintf(wr, " %s %s", allcmd.Name, usage)
|
|
} else {
|
|
fmt.Fprintf(wr, " %s", allcmd.Name)
|
|
}
|
|
|
|
allcmd = allcmd.Active
|
|
}
|
|
|
|
fmt.Fprintln(wr)
|
|
|
|
if len(cmd.LongDescription) != 0 {
|
|
fmt.Fprintln(wr)
|
|
|
|
t := wrapText(cmd.LongDescription,
|
|
aligninfo.terminalColumns,
|
|
"")
|
|
|
|
fmt.Fprintln(wr, t)
|
|
}
|
|
}
|
|
|
|
p.eachActiveGroup(func(grp *Group) {
|
|
first := true
|
|
|
|
for _, info := range grp.options {
|
|
if info.canCli() {
|
|
if first {
|
|
fmt.Fprintf(wr, "\n%s:\n", grp.ShortDescription)
|
|
first = false
|
|
}
|
|
|
|
p.writeHelpOption(wr, info, aligninfo)
|
|
}
|
|
}
|
|
})
|
|
|
|
scommands := cmd.sortedCommands()
|
|
|
|
if len(scommands) > 0 {
|
|
maxnamelen := maxCommandLength(scommands)
|
|
|
|
fmt.Fprintln(wr)
|
|
fmt.Fprintln(wr, "Available commands:")
|
|
|
|
for _, c := range scommands {
|
|
fmt.Fprintf(wr, " %s", c.Name)
|
|
|
|
if len(c.ShortDescription) > 0 {
|
|
pad := strings.Repeat(" ", maxnamelen-len(c.Name))
|
|
fmt.Fprintf(wr, "%s %s", pad, c.ShortDescription)
|
|
}
|
|
|
|
fmt.Fprintln(wr)
|
|
}
|
|
}
|
|
|
|
wr.Flush()
|
|
}
|