Add args struct
This commit is contained in:
parent
dd2171d085
commit
a28594d28e
|
@ -0,0 +1,38 @@
|
|||
package complete
|
||||
|
||||
type args struct {
|
||||
all []string
|
||||
completed []string
|
||||
beingTyped string
|
||||
lastCompleted string
|
||||
}
|
||||
|
||||
func newArgs(line []string) args {
|
||||
completed := removeLast(line)
|
||||
return args{
|
||||
all: line[1:],
|
||||
completed: completed,
|
||||
beingTyped: last(line),
|
||||
lastCompleted: last(completed),
|
||||
}
|
||||
}
|
||||
|
||||
func (a args) from(i int) args {
|
||||
a.all = a.all[i:]
|
||||
a.completed = a.completed[i:]
|
||||
return a
|
||||
}
|
||||
|
||||
func removeLast(a []string) []string {
|
||||
if len(a) > 0 {
|
||||
return a[:len(a)-1]
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func last(args []string) (last string) {
|
||||
if len(args) > 0 {
|
||||
last = args[len(args)-1]
|
||||
}
|
||||
return
|
||||
}
|
51
command.go
51
command.go
|
@ -3,7 +3,7 @@ package complete
|
|||
import "github.com/posener/complete/match"
|
||||
|
||||
// Command represents a command line
|
||||
// It holds the data that enables auto completion of a given typed command line
|
||||
// It holds the data that enables auto completion of command line
|
||||
// Command can also be a sub command.
|
||||
type Command struct {
|
||||
// Sub is map of sub commands of the current command
|
||||
|
@ -12,7 +12,7 @@ type Command struct {
|
|||
Sub Commands
|
||||
|
||||
// Flags is a map of flags that the command accepts.
|
||||
// The key is the flag name, and the value is it's prediction options.
|
||||
// The key is the flag name, and the value is it's prediction predict.
|
||||
Flags Flags
|
||||
|
||||
// Args are extra arguments that the command accepts, those who are
|
||||
|
@ -24,62 +24,52 @@ type Command struct {
|
|||
type Commands map[string]Command
|
||||
|
||||
// Flags is the type Flags of the Flags member, it maps a flag name to the flag
|
||||
// prediction options.
|
||||
// prediction predict.
|
||||
type Flags map[string]Predicate
|
||||
|
||||
// options returns all available complete options for the given command
|
||||
// args are all except the last command line arguments relevant to the command
|
||||
func (c *Command) options(args []string) (options []match.Matcher, only bool) {
|
||||
// predict returns all available complete predict for the given command
|
||||
// all are all except the last command line arguments relevant to the command
|
||||
func (c *Command) predict(a args) (options []match.Matcher, only bool) {
|
||||
|
||||
// remove the first argument, which is the command name
|
||||
args = args[1:]
|
||||
wordCurrent := last(args)
|
||||
wordCompleted := last(removeLast(args))
|
||||
// if wordCompleted has something that needs to follow it,
|
||||
// it is the most relevant completion
|
||||
if predicate, ok := c.Flags[wordCompleted]; ok && predicate != nil {
|
||||
Log("Predicting according to flag %s", wordCurrent)
|
||||
return predicate.predict(wordCurrent), true
|
||||
if predicate, ok := c.Flags[a.lastCompleted]; ok && predicate != nil {
|
||||
Log("Predicting according to flag %s", a.beingTyped)
|
||||
return predicate.predict(a.beingTyped), true
|
||||
}
|
||||
|
||||
sub, options, only := c.searchSub(args)
|
||||
sub, options, only := c.searchSub(a)
|
||||
if only {
|
||||
return
|
||||
}
|
||||
|
||||
// if no subcommand was entered in any of the args, add the
|
||||
// subcommands as complete options.
|
||||
// if no sub command was found, return a list of the sub commands
|
||||
if sub == "" {
|
||||
options = append(options, c.subCommands()...)
|
||||
}
|
||||
|
||||
// add global available complete options
|
||||
// add global available complete predict
|
||||
for flag := range c.Flags {
|
||||
options = append(options, match.Prefix(flag))
|
||||
}
|
||||
|
||||
// add additional expected argument of the command
|
||||
options = append(options, c.Args.predict(wordCurrent)...)
|
||||
options = append(options, c.Args.predict(a.beingTyped)...)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// searchSub searches recursively within sub commands if the sub command appear
|
||||
// in the on of the arguments.
|
||||
func (c *Command) searchSub(args []string) (sub string, all []match.Matcher, only bool) {
|
||||
|
||||
// search for sub command in all arguments except the last one
|
||||
// because that one might not be completed yet
|
||||
searchArgs := removeLast(args)
|
||||
|
||||
for i, arg := range searchArgs {
|
||||
func (c *Command) searchSub(a args) (sub string, all []match.Matcher, only bool) {
|
||||
for i, arg := range a.completed {
|
||||
if cmd, ok := c.Sub[arg]; ok {
|
||||
sub = arg
|
||||
all, only = cmd.options(args[i:])
|
||||
all, only = cmd.predict(a.from(i))
|
||||
return
|
||||
}
|
||||
}
|
||||
return "", nil, false
|
||||
return
|
||||
}
|
||||
|
||||
// suvCommands returns a list of matchers according to the sub command names
|
||||
|
@ -90,10 +80,3 @@ func (c *Command) subCommands() []match.Matcher {
|
|||
}
|
||||
return subs
|
||||
}
|
||||
|
||||
func removeLast(a []string) []string {
|
||||
if len(a) > 0 {
|
||||
return a[:len(a)-1]
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
|
25
complete.go
25
complete.go
|
@ -41,15 +41,17 @@ func New(name string, command Command) *Complete {
|
|||
// returns success if the completion ran or if the cli matched
|
||||
// any of the given flags, false otherwise
|
||||
func (c *Complete) Run() bool {
|
||||
args, ok := getLine()
|
||||
line, ok := getLine()
|
||||
if !ok {
|
||||
// make sure flags parsed,
|
||||
// in case they were not added in the main program
|
||||
return c.CLI.Run()
|
||||
}
|
||||
Log("Completing args: %s", args)
|
||||
Log("Completing line: %s", line)
|
||||
|
||||
options := complete(c.Command, args)
|
||||
a := newArgs(line)
|
||||
|
||||
options := complete(c.Command, a)
|
||||
|
||||
Log("Completion: %s", options)
|
||||
output(options)
|
||||
|
@ -58,14 +60,12 @@ func (c *Complete) Run() bool {
|
|||
|
||||
// complete get a command an command line arguments and returns
|
||||
// matching completion options
|
||||
func complete(c Command, args []string) (matching []string) {
|
||||
options, _ := c.options(args)
|
||||
func complete(c Command, a args) (matching []string) {
|
||||
options, _ := c.predict(a)
|
||||
|
||||
// choose only matching options
|
||||
l := last(args)
|
||||
for _, option := range options {
|
||||
Log("option %T, %s -> %t", option, option, option.Match(l))
|
||||
if option.Match(l) {
|
||||
Log("option %T, %s -> %t", option, option, option.Match(a.beingTyped))
|
||||
if option.Match(a.beingTyped) {
|
||||
matching = append(matching, option.String())
|
||||
}
|
||||
}
|
||||
|
@ -80,13 +80,6 @@ func getLine() ([]string, bool) {
|
|||
return strings.Split(line, " "), true
|
||||
}
|
||||
|
||||
func last(args []string) (last string) {
|
||||
if len(args) > 0 {
|
||||
last = args[len(args)-1]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func output(options []string) {
|
||||
Log("")
|
||||
// stdout of program defines the complete options
|
||||
|
|
|
@ -174,9 +174,9 @@ func TestCompleter_Complete(t *testing.T) {
|
|||
|
||||
tt.args = "cmd " + tt.args
|
||||
os.Setenv(envComplete, tt.args)
|
||||
args, _ := getLine()
|
||||
line, _ := getLine()
|
||||
|
||||
got := complete(c, args)
|
||||
got := complete(c, newArgs(line))
|
||||
|
||||
sort.Strings(tt.want)
|
||||
sort.Strings(got)
|
||||
|
|
Loading…
Reference in New Issue