Merge pull request #228 from nickwei84/master
Completion on short option names allows for exploration of short and long option names
diff --git a/completion.go b/completion.go
index cca12a7..7a7a08b 100644
--- a/completion.go
+++ b/completion.go
@@ -136,6 +136,9 @@
}
func (c *completion) completeValue(value reflect.Value, prefix string, match string) []Completion {
+ if value.Kind() == reflect.Slice {
+ value = reflect.New(value.Type().Elem())
+ }
i := value.Interface()
var ret []Completion
@@ -155,16 +158,6 @@
return ret
}
-func (c *completion) completeArg(arg *Arg, prefix string, match string) []Completion {
- if arg.isRemaining() {
- // For remaining positional args (that are parsed into a slice), complete
- // based on the element type.
- return c.completeValue(reflect.New(arg.value.Type().Elem()), prefix, match)
- }
-
- return c.completeValue(arg.value, prefix, match)
-}
-
func (c *completion) complete(args []string) []Completion {
if len(args) == 0 {
args = []string{""}
@@ -279,7 +272,7 @@
}
} else if len(s.positional) > 0 {
// Complete for positional argument
- ret = c.completeArg(s.positional[0], "", lastarg)
+ ret = c.completeValue(s.positional[0].value, "", lastarg)
} else if len(s.command.commands) > 0 {
// Complete for command
ret = c.completeCommands(s, lastarg)
diff --git a/completion_test.go b/completion_test.go
index 3a1a2ce..26f70e4 100644
--- a/completion_test.go
+++ b/completion_test.go
@@ -53,8 +53,13 @@
Positional struct {
Filename []Filename
} `positional-args:"yes"`
+ Extra []Filename `short:"f"`
} `command:"add-multi" description:"add multiple items"`
+ AddMultiCommandFlag struct {
+ Files []Filename `short:"f"`
+ } `command:"add-multi-flag" description:"add multiple items via flags"`
+
RemoveCommand struct {
Other bool `short:"o"`
File Filename `short:"f" long:"filename"`
@@ -130,7 +135,7 @@
{
// Commands
[]string{""},
- []string{"add", "add-multi", "rename", "rm"},
+ []string{"add", "add-multi", "add-multi-flag", "rename", "rm"},
false,
},
@@ -138,10 +143,11 @@
// Commands with descriptions
[]string{""},
[]string{
- "add # add an item",
- "add-multi # add multiple items",
- "rename # rename an item",
- "rm # remove an item",
+ "add # add an item",
+ "add-multi # add multiple items",
+ "add-multi-flag # add multiple items via flags",
+ "rename # rename an item",
+ "rm # remove an item",
},
true,
},
@@ -227,6 +233,12 @@
[]string{"hello universe"},
false,
},
+ {
+ // Multiple flag filename
+ []string{"add-multi-flag", "-f", filepath.Join(completionTestSourcedir, "completion")},
+ completionTestFilename,
+ false,
+ },
}
}
diff --git a/group.go b/group.go
index 51b4704..6133a71 100644
--- a/group.go
+++ b/group.go
@@ -174,6 +174,10 @@
}
}
+func isStringFalsy(s string) bool {
+ return s == "" || s == "false" || s == "no" || s == "0"
+}
+
func (g *Group) scanStruct(realval reflect.Value, sfield *reflect.StructField, handler scanHandler) error {
stype := realval.Type()
@@ -255,10 +259,10 @@
valueName := mtag.Get("value-name")
defaultMask := mtag.Get("default-mask")
- optional := (mtag.Get("optional") != "")
- required := (mtag.Get("required") != "")
+ optional := !isStringFalsy(mtag.Get("optional"))
+ required := !isStringFalsy(mtag.Get("required"))
choices := mtag.GetMany("choice")
- hidden := (mtag.Get("hidden") != "")
+ hidden := !isStringFalsy(mtag.Get("hidden"))
option := &Option{
Description: description,
diff --git a/help.go b/help.go
index 9167847..d380305 100644
--- a/help.go
+++ b/help.go
@@ -216,8 +216,10 @@
var def string
- if len(option.DefaultMask) != 0 && option.DefaultMask != "-" {
- def = option.DefaultMask
+ if len(option.DefaultMask) != 0 {
+ if option.DefaultMask != "-" {
+ def = option.DefaultMask
+ }
} else {
def = option.defaultLiteral
}
diff --git a/help_test.go b/help_test.go
index 01942a2..bb76640 100644
--- a/help_test.go
+++ b/help_test.go
@@ -1,10 +1,12 @@
package flags
import (
+ "bufio"
"bytes"
"fmt"
"os"
"runtime"
+ "strings"
"testing"
"time"
)
@@ -36,8 +38,9 @@
} `group:"Hidden group" hidden:"yes"`
Group struct {
- Opt string `long:"opt" description:"This is a subgroup option"`
- HiddenInsideGroup string `long:"hidden-inside-group" description:"Hidden inside group" hidden:"yes"`
+ Opt string `long:"opt" description:"This is a subgroup option"`
+ HiddenInsideGroup string `long:"hidden-inside-group" description:"Hidden inside group" hidden:"yes"`
+ NotHiddenInsideGroup string `long:"not-hidden-inside-group" description:"Not hidden inside group" hidden:"false"`
Group struct {
Opt string `long:"opt" description:"This is a subsubgroup option"`
@@ -113,6 +116,7 @@
Subgroup:
/sip.opt: This is a subgroup option
+ /sip.not-hidden-inside-group: Not hidden inside group
Subsubgroup:
/sip.sap.opt: This is a subsubgroup option
@@ -159,6 +163,7 @@
Subgroup:
--sip.opt= This is a subgroup option
+ --sip.not-hidden-inside-group= Not hidden inside group
Subsubgroup:
--sip.sap.opt= This is a subsubgroup option
@@ -261,6 +266,9 @@
.TP
\fB\fB\-\-sip.opt\fR\fP
This is a subgroup option
+.TP
+\fB\fB\-\-sip.not-hidden-inside-group\fR\fP
+Not hidden inside group
.SS Subsubgroup
.TP
\fB\fB\-\-sip.sap.opt\fR\fP
@@ -466,3 +474,65 @@
assertDiff(t, got, expected, "wrapped paragraph")
}
+
+func TestHelpDefaultMask(t *testing.T) {
+ var tests = []struct {
+ opts interface{}
+ present string
+ }{
+ {
+ opts: &struct {
+ Value string `short:"v" default:"123" description:"V"`
+ }{},
+ present: "V (default: 123)\n",
+ },
+ {
+ opts: &struct {
+ Value string `short:"v" default:"123" default-mask:"abc" description:"V"`
+ }{},
+ present: "V (default: abc)\n",
+ },
+ {
+ opts: &struct {
+ Value string `short:"v" default:"123" default-mask:"-" description:"V"`
+ }{},
+ present: "V\n",
+ },
+ {
+ opts: &struct {
+ Value string `short:"v" description:"V"`
+ }{Value: "123"},
+ present: "V (default: 123)\n",
+ },
+ {
+ opts: &struct {
+ Value string `short:"v" default-mask:"abc" description:"V"`
+ }{Value: "123"},
+ present: "V (default: abc)\n",
+ },
+ {
+ opts: &struct {
+ Value string `short:"v" default-mask:"-" description:"V"`
+ }{Value: "123"},
+ present: "V\n",
+ },
+ }
+
+ for _, test := range tests {
+ p := NewParser(test.opts, HelpFlag)
+ _, err := p.ParseArgs([]string{"-h"})
+ if flagsErr, ok := err.(*Error); ok && flagsErr.Type == ErrHelp {
+ err = nil
+ }
+ if err != nil {
+ t.Fatalf("Unexpected error: %v", err)
+ }
+ h := &bytes.Buffer{}
+ w := bufio.NewWriter(h)
+ p.writeHelpOption(w, p.FindOptionByShortName('v'), p.getAlignmentInfo())
+ w.Flush()
+ if strings.Index(h.String(), test.present) < 0 {
+ t.Errorf("Not present %q\n%s", test.present, h.String())
+ }
+ }
+}
diff --git a/ini_test.go b/ini_test.go
index e1f22ce..ad4852e 100644
--- a/ini_test.go
+++ b/ini_test.go
@@ -93,6 +93,9 @@
; This is a subgroup option
Opt =
+; Not hidden inside group
+NotHiddenInsideGroup =
+
[Subsubgroup]
; This is a subsubgroup option
Opt =
@@ -153,6 +156,9 @@
; This is a subgroup option
; Opt =
+; Not hidden inside group
+; NotHiddenInsideGroup =
+
[Subsubgroup]
; This is a subsubgroup option
; Opt =
@@ -211,6 +217,9 @@
; This is a subgroup option
; Opt =
+; Not hidden inside group
+; NotHiddenInsideGroup =
+
[Subsubgroup]
; This is a subsubgroup option
; Opt =
diff --git a/option.go b/option.go
index 8270a22..ea09fb4 100644
--- a/option.go
+++ b/option.go
@@ -179,7 +179,7 @@
return option.isSet
}
-// IsSet returns true if option has been set via the default option tag
+// IsSetDefault returns true if option has been set via the default option tag
func (option *Option) IsSetDefault() bool {
return option.isSetDefault
}
diff --git a/parser.go b/parser.go
index fd2fd5f..0a7922a 100644
--- a/parser.go
+++ b/parser.go
@@ -479,7 +479,7 @@
msg = fmt.Sprintf("%s. You should use the %s command",
msg,
cmdnames[0])
- } else {
+ } else if len(cmdnames) > 1 {
msg = fmt.Sprintf("%s. Please specify one command of: %s or %s",
msg,
strings.Join(cmdnames[:len(cmdnames)-1], ", "),
@@ -490,7 +490,7 @@
if len(cmdnames) == 1 {
msg = fmt.Sprintf("Please specify the %s command", cmdnames[0])
- } else {
+ } else if len(cmdnames) > 1 {
msg = fmt.Sprintf("Please specify one command of: %s or %s",
strings.Join(cmdnames[:len(cmdnames)-1], ", "),
cmdnames[len(cmdnames)-1])
diff --git a/short_test.go b/short_test.go
index 95712c1..5f4106b 100644
--- a/short_test.go
+++ b/short_test.go
@@ -35,6 +35,22 @@
assertParseFail(t, ErrRequired, fmt.Sprintf("the required flag `%cv' was not specified", defaultShortOptDelimiter), &opts)
}
+func TestShortRequiredFalsy1(t *testing.T) {
+ var opts = struct {
+ Value bool `short:"v" required:"false"`
+ }{}
+
+ assertParseSuccess(t, &opts)
+}
+
+func TestShortRequiredFalsy2(t *testing.T) {
+ var opts = struct {
+ Value bool `short:"v" required:"no"`
+ }{}
+
+ assertParseSuccess(t, &opts)
+}
+
func TestShortMultiConcat(t *testing.T) {
var opts = struct {
V bool `short:"v"`
@@ -192,3 +208,27 @@
assertStringArray(t, ret, []string{"f"})
assertString(t, opts.Value, "value")
}
+
+func TestShortOptionalFalsy1(t *testing.T) {
+ var opts = struct {
+ F []bool `short:"f"`
+ Value string `short:"v" optional:"false" optional-value:"value"`
+ }{}
+
+ ret := assertParseSuccess(t, &opts, "-fv", "f")
+
+ assertStringArray(t, ret, []string{})
+ assertString(t, opts.Value, "f")
+}
+
+func TestShortOptionalFalsy2(t *testing.T) {
+ var opts = struct {
+ F []bool `short:"f"`
+ Value string `short:"v" optional:"no" optional-value:"value"`
+ }{}
+
+ ret := assertParseSuccess(t, &opts, "-fv", "f")
+
+ assertStringArray(t, ret, []string{})
+ assertString(t, opts.Value, "f")
+}