110 lines
2.7 KiB
Go
110 lines
2.7 KiB
Go
// Package complete provides a tool for bash writing bash completion in go.
|
|
//
|
|
// Writing bash completion scripts is a hard work. This package provides an easy way
|
|
// to create bash completion scripts for any command, and also an easy way to install/uninstall
|
|
// the completion of the command.
|
|
package complete
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"strconv"
|
|
|
|
"github.com/posener/complete/cmd"
|
|
"github.com/posener/complete/match"
|
|
)
|
|
|
|
const (
|
|
envLine = "COMP_LINE"
|
|
envPoint = "COMP_POINT"
|
|
envDebug = "COMP_DEBUG"
|
|
)
|
|
|
|
// 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.
|
|
// 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,
|
|
}
|
|
}
|
|
|
|
// 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
|
|
// For installation: it assumes that flags were added and parsed before
|
|
// it was called.
|
|
func (c *Complete) Complete() bool {
|
|
line, point, ok := getEnv()
|
|
if !ok {
|
|
// make sure flags parsed,
|
|
// in case they were not added in the main program
|
|
return c.CLI.Run()
|
|
}
|
|
|
|
if point >= 0 && point < len(line) {
|
|
line = line[:point]
|
|
}
|
|
|
|
Log("Completing phrase: %s", line)
|
|
a := newArgs(line)
|
|
Log("Completing last field: %s", a.Last)
|
|
options := c.Command.Predict(a)
|
|
Log("Options: %s", options)
|
|
|
|
// filter only options that match the last argument
|
|
matches := []string{}
|
|
for _, option := range options {
|
|
if match.Prefix(option, a.Last) {
|
|
matches = append(matches, option)
|
|
}
|
|
}
|
|
Log("Matches: %s", matches)
|
|
c.output(matches)
|
|
return true
|
|
}
|
|
|
|
func getEnv() (line string, point int, ok bool) {
|
|
line = os.Getenv(envLine)
|
|
if line == "" {
|
|
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)
|
|
}
|
|
return line, point, true
|
|
}
|
|
|
|
func (c *Complete) output(options []string) {
|
|
// stdout of program defines the complete options
|
|
for _, option := range options {
|
|
fmt.Fprintln(c.Out, option)
|
|
}
|
|
}
|