liuxiaolong
2020-09-16 4501e38ca66f09b35aaaf43fa5a316554930fcf4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
// 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"
 
    "github.com/posener/complete/cmd"
    "github.com/posener/complete/match"
)
 
const (
    envComplete = "COMP_LINE"
    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, ok := getLine()
    if !ok {
        // make sure flags parsed,
        // in case they were not added in the main program
        return c.CLI.Run()
    }
    Log("Completing line: %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 getLine() (string, bool) {
    line := os.Getenv(envComplete)
    if line == "" {
        return "", false
    }
    return line, true
}
 
func (c *Complete) output(options []string) {
    // stdout of program defines the complete options
    for _, option := range options {
        fmt.Fprintln(c.Out, option)
    }
}