11package cli
22
33import (
4- "flag"
54 "os"
5+ "strings"
6+
7+ "github.com/spf13/pflag"
68)
79
810func appendParent (parent string , add string ) string {
9- if parent == "" {
10- return add + " "
11- }
1211 return parent + add + " "
1312}
1413
14+ // splitArgs tries to split the args between the parent command's flags/args, and the subcommand's
15+ // flags/args. If a subcommand is found, the parent args, subcommand args, and the subcommand will
16+ // be returned. If a subcommand isn't found, the args will be returned as is, the subcommand args
17+ // will be empty, and the subcommand will be nil.
18+ func splitArgs (subCmds []Command , args []string ) (cmdArgs , subArgs []string , subCmd Command ) {
19+ for i , arg := range args {
20+ if strings .HasPrefix (arg , "-" ) {
21+ continue
22+ }
23+
24+ for _ , subCommand := range subCmds {
25+ if subCommand .Spec ().Name == arg {
26+ return args [:i ], args [i + 1 :], subCommand
27+ }
28+ }
29+ }
30+
31+ return args , []string {}, nil
32+ }
33+
1534// Run sets up flags, helps, and executes the command with the provided
1635// arguments.
1736//
@@ -20,37 +39,51 @@ func appendParent(parent string, add string) string {
2039//
2140// Use RunRoot if this package is managing the entire CLI.
2241func Run (cmd Command , args []string , parent string ) {
23- fl := flag .NewFlagSet (parent + "" + cmd .Spec ().Name , flag .ExitOnError )
42+ name := parent + cmd .Spec ().Name
43+ fl := pflag .NewFlagSet (name , pflag .ContinueOnError )
44+ // Ensure pflag library doesn't print usage for us automatically,
45+ // we'll override this below.
46+ fl .Usage = func () {}
2447
2548 if fc , ok := cmd .(FlaggedCommand ); ok {
2649 fc .RegisterFlags (fl )
2750 }
2851
29- fl .Usage = func () {
30- renderHelp (cmd , fl , os .Stderr )
31- }
32-
3352 if cmd .Spec ().RawArgs {
3453 // Use `--` to return immediately when parsing the flags.
3554 args = append ([]string {"--" }, args ... )
3655 }
37- _ = fl .Parse (args )
3856
39- subcommandArg := fl .Arg (0 )
57+ var (
58+ cmdArgs , subArgs []string
59+ subCmd Command
60+ )
61+ pc , isParentCmd := cmd .(ParentCommand )
62+ if isParentCmd {
63+ cmdArgs , subArgs , subCmd = splitArgs (pc .Subcommands (), args )
64+ if subCmd != nil {
65+ args = cmdArgs
66+ }
67+ }
4068
41- // Route to subcommand.
42- if pc , ok := cmd .(ParentCommand ); ok && subcommandArg != "" {
43- for _ , subcommand := range pc .Subcommands () {
44- if subcommand .Spec ().Name != subcommandArg {
45- continue
46- }
69+ err := fl .Parse (args )
70+ // Reassign the usage now that we've parsed the args
71+ // so that we can render it manually.
72+ fl .Usage = func () {
73+ renderHelp (name , cmd , fl , os .Stderr )
74+ }
75+ if err != nil {
76+ fl .Usage ()
77+ os .Exit (2 )
78+ }
4779
48- Run (
49- subcommand , fl .Args ()[1 :],
50- appendParent (parent , cmd .Spec ().Name ),
51- )
52- return
53- }
80+ // Route to subcommand.
81+ if isParentCmd && subCmd != nil {
82+ Run (
83+ subCmd , subArgs ,
84+ appendParent (parent , cmd .Spec ().Name ),
85+ )
86+ return
5487 }
5588
5689 cmd .Run (fl )
0 commit comments