complete/complete.go

105 lines
2.4 KiB
Go
Raw Normal View History

2017-05-05 08:57:22 -05:00
package complete
import (
2017-05-22 23:33:14 -05:00
"flag"
2017-05-05 08:57:22 -05:00
"fmt"
"io"
2017-05-05 08:57:22 -05:00
"os"
2018-10-19 12:10:37 -05:00
"strconv"
2019-07-05 06:58:49 -05:00
"strings"
2017-05-06 14:06:49 -05:00
"github.com/posener/complete/cmd"
2017-05-05 08:57:22 -05:00
)
const (
2018-10-19 12:10:37 -05:00
envLine = "COMP_LINE"
envPoint = "COMP_POINT"
envDebug = "COMP_DEBUG"
2017-05-05 08:57:22 -05:00
)
// Complete structs define completion for a command with CLI options
type Complete struct {
Command Command
cmd.CLI
Out io.Writer
}
// New creates a new complete command.
2017-05-06 23:59:42 -05:00
// name is the name of command we want to auto complete.
// IMPORTANT: it must be the same name - if the auto complete
// completes the 'go' command, name must be equal to "go".
// command is the struct of the command completion.
func New(name string, command Command) *Complete {
return &Complete{
Command: command,
CLI: cmd.CLI{Name: name},
Out: os.Stdout,
}
}
2017-05-22 23:33:14 -05:00
// Run runs the completion and add installation flags beforehand.
// The flags are added to the main flag CommandLine variable.
func (c *Complete) Run() bool {
c.AddFlags(nil)
flag.Parse()
return c.Complete()
}
// Complete a command from completion line in environment variable,
// and print out the complete options.
// returns success if the completion ran or if the cli matched
// any of the given flags, false otherwise
2017-05-22 23:33:14 -05:00
// For installation: it assumes that flags were added and parsed before
// it was called.
func (c *Complete) Complete() bool {
2018-10-19 12:10:37 -05:00
line, point, ok := getEnv()
if !ok {
// make sure flags parsed,
// in case they were not added in the main program
return c.CLI.Run()
}
2018-10-19 12:10:37 -05:00
if point >= 0 && point < len(line) {
line = line[:point]
}
2018-10-19 12:10:37 -05:00
Log("Completing phrase: %s", line)
a := newArgs(line)
Log("Completing last field: %s", a.Last)
2017-05-11 12:28:31 -05:00
options := c.Command.Predict(a)
Log("Options: %s", options)
2017-05-05 08:57:22 -05:00
// filter only options that match the last argument
matches := []string{}
for _, option := range options {
2019-07-05 06:58:49 -05:00
if strings.HasPrefix(option, a.Last) {
matches = append(matches, option)
}
}
Log("Matches: %s", matches)
c.output(matches)
return true
2017-05-05 08:57:22 -05:00
}
2018-10-19 12:10:37 -05:00
func getEnv() (line string, point int, ok bool) {
line = os.Getenv(envLine)
2017-05-05 08:57:22 -05:00
if line == "" {
2018-10-19 12:10:37 -05:00
return
}
point, err := strconv.Atoi(os.Getenv(envPoint))
if err != nil {
// If failed parsing point for some reason, set it to point
// on the end of the line.
Log("Failed parsing point %s: %v", os.Getenv(envPoint), err)
point = len(line)
2017-05-05 08:57:22 -05:00
}
2018-10-19 12:10:37 -05:00
return line, point, true
2017-05-05 08:57:22 -05:00
}
func (c *Complete) output(options []string) {
2017-05-05 08:57:22 -05:00
// stdout of program defines the complete options
for _, option := range options {
fmt.Fprintln(c.Out, option)
2017-05-05 08:57:22 -05:00
}
}