Add logic to complete when last flag uses the equal sign

If the last flag is of the form -flag=value, complete the value according to -flag.
This commit is contained in:
Eyal Posener 2017-11-04 11:20:50 +02:00
parent 88e59760ad
commit aae7e1e39f
6 changed files with 64 additions and 18 deletions

43
args.go
View File

@ -3,6 +3,8 @@ package complete
import ( import (
"os" "os"
"path/filepath" "path/filepath"
"strings"
"unicode"
) )
// Args describes command line arguments // Args describes command line arguments
@ -37,16 +39,41 @@ func (a Args) Directory() string {
return fixPathForm(a.Last, dir) return fixPathForm(a.Last, dir)
} }
func newArgs(line []string) Args { func newArgs(line string) Args {
completed := removeLast(line[1:]) var (
all []string
completed []string
)
parts := splitFields(line)
if len(parts) > 0 {
all = parts[1:]
completed = removeLast(parts[1:])
}
return Args{ return Args{
All: line[1:], All: all,
Completed: completed, Completed: completed,
Last: last(line), Last: last(parts),
LastCompleted: last(completed), LastCompleted: last(completed),
} }
} }
func splitFields(line string) []string {
parts := strings.Fields(line)
if len(line) > 0 && unicode.IsSpace(rune(line[len(line)-1])) {
parts = append(parts, "")
}
parts = splitLastEqual(parts)
return parts
}
func splitLastEqual(line []string) []string {
if len(line) == 0 {
return line
}
parts := strings.Split(line[len(line)-1], "=")
return append(line[:len(line)-1], parts...)
}
func (a Args) from(i int) Args { func (a Args) from(i int) Args {
if i > len(a.All) { if i > len(a.All) {
i = len(a.All) i = len(a.All)
@ -67,9 +94,9 @@ func removeLast(a []string) []string {
return a return a
} }
func last(args []string) (last string) { func last(args []string) string {
if len(args) > 0 { if len(args) == 0 {
last = args[len(args)-1] return ""
} }
return return args[len(args)-1]
} }

View File

@ -49,7 +49,7 @@ func TestArgs(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.line, func(t *testing.T) { t.Run(tt.line, func(t *testing.T) {
a := newArgs(strings.Split(tt.line, " ")) a := newArgs(tt.line)
if got, want := strings.Join(a.Completed, " "), tt.completed; got != want { if got, want := strings.Join(a.Completed, " "), tt.completed; got != want {
t.Errorf("%s failed: Completed = %q, want %q", t.Name(), got, want) t.Errorf("%s failed: Completed = %q, want %q", t.Name(), got, want)
@ -131,7 +131,7 @@ func TestArgs_From(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(fmt.Sprintf("%s/%d", tt.line, tt.from), func(t *testing.T) { t.Run(fmt.Sprintf("%s/%d", tt.line, tt.from), func(t *testing.T) {
a := newArgs(strings.Split(tt.line, " ")) a := newArgs(tt.line)
n := a.from(tt.from) n := a.from(tt.from)
if got, want := strings.Join(n.All, " "), tt.newLine; got != want { if got, want := strings.Join(n.All, " "), tt.newLine; got != want {
@ -205,7 +205,7 @@ func TestArgs_Directory(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.line, func(t *testing.T) { t.Run(tt.line, func(t *testing.T) {
a := newArgs(strings.Split(tt.line, " ")) a := newArgs(tt.line)
if got, want := a.Directory(), tt.directory; got != want { if got, want := a.Directory(), tt.directory; got != want {
t.Errorf("%s failed: directory = %q, want %q", t.Name(), got, want) t.Errorf("%s failed: directory = %q, want %q", t.Name(), got, want)

View File

@ -9,7 +9,6 @@ import (
"flag" "flag"
"fmt" "fmt"
"os" "os"
"strings"
"github.com/posener/complete/cmd" "github.com/posener/complete/cmd"
) )
@ -61,7 +60,7 @@ func (c *Complete) Complete() bool {
Log("Completing line: %s", line) Log("Completing line: %s", line)
a := newArgs(line) a := newArgs(line)
Log("Completing last field: %s", a.Last)
options := c.Command.Predict(a) options := c.Command.Predict(a)
Log("Completion: %s", options) Log("Completion: %s", options)
@ -69,12 +68,12 @@ func (c *Complete) Complete() bool {
return true return true
} }
func getLine() ([]string, bool) { func getLine() (string, bool) {
line := os.Getenv(envComplete) line := os.Getenv(envComplete)
if line == "" { if line == "" {
return nil, false return "", false
} }
return strings.Split(line, " "), true return line, true
} }
func output(options []string) { func output(options []string) {

View File

@ -143,14 +143,30 @@ func TestCompleter_Complete(t *testing.T) {
args: "-o ./", args: "-o ./",
want: []string{"./a.txt", "./b.txt", "./c.txt", "./.dot.txt", "./", "./dir/", "./outer/"}, want: []string{"./a.txt", "./b.txt", "./c.txt", "./.dot.txt", "./", "./dir/", "./outer/"},
}, },
{
args: "-o=./",
want: []string{"./a.txt", "./b.txt", "./c.txt", "./.dot.txt", "./", "./dir/", "./outer/"},
},
{ {
args: "-o .", args: "-o .",
want: []string{"./a.txt", "./b.txt", "./c.txt", "./.dot.txt", "./", "./dir/", "./outer/"}, want: []string{"./a.txt", "./b.txt", "./c.txt", "./.dot.txt", "./", "./dir/", "./outer/"},
}, },
{
args: "-o ./b",
want: []string{"./b.txt"},
},
{
args: "-o=./b",
want: []string{"./b.txt"},
},
{ {
args: "-o ./read", args: "-o ./read",
want: []string{}, want: []string{},
}, },
{
args: "-o=./read",
want: []string{},
},
{ {
args: "-o ./readme.md", args: "-o ./readme.md",
want: []string{}, want: []string{},
@ -159,6 +175,10 @@ func TestCompleter_Complete(t *testing.T) {
args: "-o ./readme.md ", args: "-o ./readme.md ",
want: []string{"sub1", "sub2"}, want: []string{"sub1", "sub2"},
}, },
{
args: "-o=./readme.md ",
want: []string{"sub1", "sub2"},
},
{ {
args: "-o sub2 -flag3 ", args: "-o sub2 -flag3 ",
want: []string{"opt1", "opt2", "opt12"}, want: []string{"opt1", "opt2", "opt12"},

View File

@ -28,7 +28,7 @@ func main() {
"-asmflags": complete.PredictAnything, "-asmflags": complete.PredictAnything,
"-buildmode": complete.PredictAnything, "-buildmode": complete.PredictAnything,
"-compiler": complete.PredictAnything, "-compiler": complete.PredictAnything,
"-gccgoflags": complete.PredictAnything, "-gccgoflags": complete.PredictSet("gccgo", "gc"),
"-gcflags": complete.PredictAnything, "-gcflags": complete.PredictAnything,
"-installsuffix": complete.PredictAnything, "-installsuffix": complete.PredictAnything,
"-ldflags": complete.PredictAnything, "-ldflags": complete.PredictAnything,

View File

@ -158,7 +158,7 @@ func TestPredicate(t *testing.T) {
for _, arg := range tt.argList { for _, arg := range tt.argList {
t.Run(tt.name+"/arg="+arg, func(t *testing.T) { t.Run(tt.name+"/arg="+arg, func(t *testing.T) {
matches := tt.p.Predict(newArgs(strings.Split(arg, " "))) matches := tt.p.Predict(newArgs(arg))
sort.Strings(matches) sort.Strings(matches)
sort.Strings(tt.want) sort.Strings(tt.want)