Workaround for flynn cli optionsFirst usage
Refs flynn/flynn#193
Signed-off-by: Jonathan Rudenberg <jonathan@titanous.com>
diff --git a/docopt.go b/docopt.go
index d3a1c4f..0a7cac6 100644
--- a/docopt.go
+++ b/docopt.go
@@ -103,7 +103,7 @@
return
}
- patternArgv, err := parseArgv(newTokenList(argv, errorUser), &options, optionsFirst)
+ patternArgv, err := parseArgv(newTokenList(argv, errorUser), &options, optionsFirst, pat)
if err != nil {
output = handleError(err, usage)
return
@@ -199,7 +199,7 @@
return newRequired(result...), nil
}
-func parseArgv(tokens *tokenList, options *patternList, optionsFirst bool) (patternList, error) {
+func parseArgv(tokens *tokenList, options *patternList, optionsFirst bool, pat *pattern) (patternList, error) {
/*
Parse command-line argument vector.
@@ -209,6 +209,16 @@
argv ::= [ long | shorts | argument ]* [ '--' [ argument ]* ] ;
*/
parsed := patternList{}
+
+ // This is a HACK that skips the command in:
+ // (*docopt.pattern)(0x208333dc0)(required(required(command(foo, false), ...)))
+ // when parsing with optionsFirst set. For more details see
+ // https://github.com/flynn/flynn/pull/193
+ skipArg := optionsFirst && pat != nil &&
+ pat.t == patternRequired && len(pat.children) == 1 &&
+ pat.children[0].t == patternRequired && len(pat.children[0].children) > 0 &&
+ pat.children[0].children[0].t == patternCommand
+
for tokens.current() != nil {
if tokens.current().eq("--") {
for _, v := range tokens.tokens {
@@ -227,13 +237,14 @@
return nil, err
}
parsed = append(parsed, ps...)
- } else if optionsFirst {
+ } else if !skipArg && optionsFirst {
for _, v := range tokens.tokens {
parsed = append(parsed, newArgument("", v))
}
return parsed, nil
} else {
parsed = append(parsed, newArgument("", tokens.move().String()))
+ skipArg = false
}
}
return parsed, nil
diff --git a/docopt_test.go b/docopt_test.go
index 231bc81..65c85c8 100644
--- a/docopt_test.go
+++ b/docopt_test.go
@@ -171,19 +171,19 @@
newOption("-f", "--file", 1, false),
}
- p, err := parseArgv(tokenListFromString(""), &o, false)
+ p, err := parseArgv(tokenListFromString(""), &o, false, nil)
q := patternList{}
if reflect.DeepEqual(p, q) != true {
t.Error(err)
}
- p, err = parseArgv(tokenListFromString("-h"), &o, false)
+ p, err = parseArgv(tokenListFromString("-h"), &o, false, nil)
q = patternList{newOption("-h", "", 0, true)}
if reflect.DeepEqual(p, q) != true {
t.Error(err)
}
- p, err = parseArgv(tokenListFromString("-h --verbose"), &o, false)
+ p, err = parseArgv(tokenListFromString("-h --verbose"), &o, false, nil)
q = patternList{
newOption("-h", "", 0, true),
newOption("-v", "--verbose", 0, true),
@@ -192,7 +192,7 @@
t.Error(err)
}
- p, err = parseArgv(tokenListFromString("-h --file f.txt"), &o, false)
+ p, err = parseArgv(tokenListFromString("-h --file f.txt"), &o, false, nil)
q = patternList{
newOption("-h", "", 0, true),
newOption("-f", "--file", 1, "f.txt"),
@@ -201,7 +201,7 @@
t.Error(err)
}
- p, err = parseArgv(tokenListFromString("-h --file f.txt arg"), &o, false)
+ p, err = parseArgv(tokenListFromString("-h --file f.txt arg"), &o, false, nil)
q = patternList{
newOption("-h", "", 0, true),
newOption("-f", "--file", 1, "f.txt"),
@@ -211,7 +211,7 @@
t.Error(err)
}
- p, err = parseArgv(tokenListFromString("-h --file f.txt arg arg2"), &o, false)
+ p, err = parseArgv(tokenListFromString("-h --file f.txt arg arg2"), &o, false, nil)
q = patternList{
newOption("-h", "", 0, true),
newOption("-f", "--file", 1, "f.txt"),
@@ -222,7 +222,7 @@
t.Error(err)
}
- p, err = parseArgv(tokenListFromString("-h arg -- -v"), &o, false)
+ p, err = parseArgv(tokenListFromString("-h arg -- -v"), &o, false, nil)
q = patternList{
newOption("-h", "", 0, true),
newArgument("", "arg"),
@@ -1295,6 +1295,10 @@
if v, err := Parse("usage: prog [--opt] [<args>...]", []string{"this", "that", "--opt"}, true, "", true, false); reflect.DeepEqual(v.All, map[string]interface{}{"--opt": false, "<args>": []string{"this", "that", "--opt"}}) != true {
t.Error(err)
}
+
+ if v, err := Parse("usage: prog foo [--opt] [<args>...]", []string{"foo", "--opt", "this", "that"}, true, "", true, false); reflect.DeepEqual(v.All, map[string]interface{}{"foo": true, "--opt": true, "<args>": []string{"this", "that"}}) != true {
+ t.Error(err)
+ }
}
func TestIssue68OptionsShortcutDoesNotIncludeOptionsInUsagePattern(t *testing.T) {