complete/internal/arg/arg.go

127 lines
2.3 KiB
Go

package arg
import (
"strings"
"github.com/posener/complete/v2/internal/tokener"
)
// Arg is typed a command line argument.
type Arg struct {
Text string
Completed bool
Parsed
}
// Parsed contains information about the argument.
type Parsed struct {
Flag string
HasFlag bool
Value string
Dashes string
HasValue bool
}
// Parse parses a typed command line argument list, and returns a list of arguments.
func Parse(line string) []Arg {
var args []Arg
for {
arg, after := next(line)
if arg.Text != "" {
args = append(args, arg)
}
line = after
if line == "" {
break
}
}
return args
}
// next returns the first argument in the line and the rest of the line.
func next(line string) (arg Arg, after string) {
defer arg.parse()
// Start and end of the argument term.
var start, end int
// Stack of quote marks met during the paring of the argument.
var token tokener.Tokener
// Skip prefix spaces.
for start = 0; start < len(line); start++ {
token.Visit(line[start])
if !token.LastSpace() {
break
}
}
// If line is only spaces, return empty argument and empty leftovers.
if start == len(line) {
return
}
for end = start + 1; end < len(line); end++ {
token.Visit(line[end])
if token.LastSpace() {
arg.Completed = true
break
}
}
arg.Text = line[start:end]
if !arg.Completed {
return
}
start2 := end
// Skip space after word.
for start2 < len(line) {
token.Visit(line[start2])
if !token.LastSpace() {
break
}
start2++
}
after = line[start2:]
return
}
// parse a flag from an argument. The flag can have value attached when it is given in the
// `-key=value` format.
func (a *Arg) parse() {
if len(a.Text) == 0 {
return
}
// A pure value, no flag.
if a.Text[0] != '-' {
a.Value = a.Text
a.HasValue = true
return
}
// Seprate the dashes from the flag name.
dahsI := 1
if len(a.Text) > 1 && a.Text[1] == '-' {
dahsI = 2
}
a.Dashes = a.Text[:dahsI]
a.HasFlag = true
a.Flag = a.Text[dahsI:]
// Empty flag
if a.Flag == "" {
return
}
// Third dash or empty flag with equal is forbidden.
if a.Flag[0] == '-' || a.Flag[0] == '=' {
a.Parsed = Parsed{}
return
}
// The flag is valid.
// Check if flag has a value.
if equal := strings.IndexRune(a.Flag, '='); equal != -1 {
a.Flag, a.Value = a.Flag[:equal], a.Flag[equal+1:]
a.HasValue = true
return
}
}