package carapace
import (
"strings"
"github.com/carapace-sh/carapace/internal/common"
"github.com/carapace-sh/carapace/internal/env"
"github.com/carapace-sh/carapace/internal/pflagfork"
"github.com/carapace-sh/carapace/pkg/style"
"github.com/spf13/cobra"
)
func traverse(cmd *cobra .Command , args []string ) (Action , Context ) {
LOG .Printf ("traverse called for %#v with args %#v\n" , cmd .Name (), args )
storage .preRun (cmd , args )
if env .Lenient () {
LOG .Printf ("allowing unknown flags" )
cmd .FParseErrWhitelist .UnknownFlags = true
}
inArgs := []string {}
inPositionals := []string {}
var inFlag *pflagfork .Flag
cmd .LocalFlags ()
fs := pflagfork .FlagSet {FlagSet : cmd .Flags ()}
context := NewContext (args ...)
context .cmd = cmd
loop :
for i , arg := range context .Args {
switch {
case inFlag != nil && inFlag .Consumes (arg ):
LOG .Printf ("arg %#v is a flag argument\n" , arg )
inArgs = append (inArgs , arg )
inFlag .Args = append (inFlag .Args , arg )
if !inFlag .Consumes ("" ) {
inFlag = nil
}
continue
case arg == "--" :
LOG .Printf ("arg %#v is dash\n" , arg )
inArgs = append (inArgs , context .Args [i :]...)
break loop
case !cmd .DisableFlagParsing && strings .HasPrefix (arg , "-" ) && (fs .IsInterspersed () || len (inPositionals ) == 0 ):
LOG .Printf ("arg %#v is a flag\n" , arg )
inArgs = append (inArgs , arg )
inFlag = fs .LookupArg (arg )
if inFlag == nil {
LOG .Printf ("flag %#v is unknown" , arg )
}
continue
case subcommand (cmd , arg ) != nil :
LOG .Printf ("arg %#v is a subcommand\n" , arg )
switch {
case cmd .DisableFlagParsing :
LOG .Printf ("flag parsing disabled for %#v\n" , cmd .Name ())
default :
LOG .Printf ("parsing flags for %#v with args %#v\n" , cmd .Name (), inArgs )
if err := cmd .ParseFlags (inArgs ); err != nil {
return ActionMessage (err .Error()), context
}
context .Args = cmd .Flags ().Args ()
}
return traverse (subcommand (cmd , arg ), args [i +1 :])
default :
LOG .Printf ("arg %#v is a positional\n" , arg )
inArgs = append (inArgs , arg )
inPositionals = append (inPositionals , arg )
}
}
toParse := inArgs
if inFlag != nil && len (inFlag .Args ) == 0 && inFlag .Consumes ("" ) {
LOG .Printf ("removing arg %#v since it is a flag missing its argument\n" , toParse [len (toParse )-1 ])
toParse = toParse [:len (toParse )-1 ]
} else if (fs .IsInterspersed () || len (inPositionals ) == 0 ) && fs .IsShorthandSeries (context .Value ) {
LOG .Printf ("arg %#v is a shorthand flag series" , context .Value )
localInFlag := fs .LookupArg (context .Value )
if localInFlag != nil && (len (localInFlag .Args ) == 0 || localInFlag .Args [0 ] == "" ) && (!localInFlag .IsOptarg () || strings .HasSuffix (localInFlag .Prefix , string (localInFlag .OptargDelimiter ()))) {
suffix := localInFlag .Prefix [strings .LastIndex (localInFlag .Prefix , localInFlag .Shorthand ):]
LOG .Printf ("removing suffix %#v since it is a flag missing its argument\n" , suffix )
toParse = append (toParse , strings .TrimSuffix (localInFlag .Prefix , suffix ))
} else {
LOG .Printf ("adding shorthand flag %#v" , context .Value )
toParse = append (toParse , context .Value )
}
}
switch {
case cmd .DisableFlagParsing :
LOG .Printf ("flag parsing is disabled for %#v\n" , cmd .Name ())
default :
LOG .Printf ("parsing flags for %#v with args %#v\n" , cmd .Name (), toParse )
if err := cmd .ParseFlags (toParse ); err != nil {
return ActionMessage (err .Error()), context
}
context .Args = cmd .Flags ().Args ()
}
switch {
case common .IsDash (cmd ):
LOG .Printf ("completing dash for arg %#v\n" , context .Value )
context .Args = cmd .Flags ().Args ()[cmd .ArgsLenAtDash ():]
LOG .Printf ("context: %#v\n" , context .Args )
return storage .getPositional (cmd , len (context .Args )), context
case inFlag != nil && inFlag .Consumes (context .Value ):
LOG .Printf ("completing flag argument of %#v for arg %#v\n" , inFlag .Name , context .Value )
context .Parts = inFlag .Args
return storage .getFlag (cmd , inFlag .Name ), context
case !cmd .DisableFlagParsing && strings .HasPrefix (context .Value , "-" ) && (fs .IsInterspersed () || len (inPositionals ) == 0 ):
if f := fs .LookupArg (context .Value ); f != nil && len (f .Args ) > 0 {
LOG .Printf ("completing optional flag argument for arg %#v with prefix %#v\n" , context .Value , f .Prefix )
switch f .Value .Type () {
case "bool" :
return ActionValues ("true" , "false" ).StyleF (style .ForKeyword ).Usage (f .Usage ).Prefix (f .Prefix ), context
default :
return storage .getFlag (cmd , f .Name ).Prefix (f .Prefix ), context
}
} else if f != nil && fs .IsPosix () && !strings .HasPrefix (context .Value , "--" ) && !f .IsOptarg () && f .Prefix == context .Value {
LOG .Printf ("completing attached flag argument for arg %#v with prefix %#v\n" , context .Value , f .Prefix )
return storage .getFlag (cmd , f .Name ).Prefix (f .Prefix ), context
}
LOG .Printf ("completing flags for arg %#v\n" , context .Value )
return actionFlags (cmd ), context
default :
LOG .Printf ("completing positionals and subcommands for arg %#v\n" , context .Value )
batch := Batch (storage .getPositional (cmd , len (context .Args )))
if cmd .HasAvailableSubCommands () && len (context .Args ) == 0 {
batch = append (batch , ActionCommands (cmd ))
}
return batch .ToA (), context
}
}
func subcommand(cmd *cobra .Command , arg string ) *cobra .Command {
if subcommand , _ , _ := cmd .Find ([]string {arg }); subcommand != cmd {
return subcommand
}
return nil
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .