-
-
Notifications
You must be signed in to change notification settings - Fork 5
/
completion.go
141 lines (116 loc) · 3.26 KB
/
completion.go
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//go:build darwin || linux || freebsd || openbsd
package console
import (
"fmt"
"os"
"runtime/debug"
"github.com/pkg/errors"
"github.com/posener/complete"
"github.com/rs/zerolog"
"github.com/symfony-cli/terminal"
)
func init() {
for _, key := range []string{"COMP_LINE", "COMP_POINT", "COMP_DEBUG"} {
if _, hasEnv := os.LookupEnv(key); hasEnv {
// Disable Garbage collection for faster autocompletion
debug.SetGCPercent(-1)
return
}
}
}
var autoCompleteCommand = &Command{
Category: "self",
Name: "autocomplete",
Description: "Internal command to provide shell completion suggestions",
Hidden: Hide,
FlagParsing: FlagParsingSkippedAfterFirstArg,
Args: ArgDefinition{
&Arg{
Slice: true,
Optional: true,
},
},
Action: AutocompleteAppAction,
}
func registerAutocompleteCommands(a *Application) {
if IsGoRun() {
return
}
a.Commands = append(
[]*Command{shellAutoCompleteInstallCommand, autoCompleteCommand},
a.Commands...,
)
}
func AutocompleteAppAction(c *Context) error {
// connect posener/complete logger to our logging facilities
logger := terminal.Logger.WithLevel(zerolog.DebugLevel)
complete.Log = func(format string, args ...interface{}) {
logger.Msgf("completion | "+format, args...)
}
cmd := complete.Command{
GlobalFlags: make(complete.Flags),
Sub: make(complete.Commands),
}
// transpose registered commands and flags to posener/complete equivalence
for _, command := range c.App.Commands {
subCmd := command.convertToPosenerCompleteCommand(c)
if command.Hidden == nil || !command.Hidden() {
cmd.Sub[command.FullName()] = subCmd
}
for _, alias := range command.Aliases {
if !alias.Hidden {
cmd.Sub[alias.String()] = subCmd
}
}
}
for _, f := range c.App.VisibleFlags() {
if vf, ok := f.(*verbosityFlag); ok {
vf.addToPosenerFlags(c, cmd.GlobalFlags)
continue
}
predictor := ContextPredictor{f, c}
for _, name := range f.Names() {
name = fmt.Sprintf("%s%s", prefixFor(name), name)
cmd.GlobalFlags[name] = predictor
}
}
if !complete.New(c.App.HelpName, cmd).Complete() {
return errors.New("Could not run auto-completion")
}
return nil
}
func (c *Command) convertToPosenerCompleteCommand(ctx *Context) complete.Command {
command := complete.Command{
Flags: make(complete.Flags, 0),
}
for _, f := range c.VisibleFlags() {
for _, name := range f.Names() {
name = fmt.Sprintf("%s%s", prefixFor(name), name)
command.Flags[name] = ContextPredictor{f, ctx}
}
}
if len(c.Args) > 0 || c.ShellComplete != nil {
command.Args = ContextPredictor{c, ctx}
}
return command
}
func (c *Command) PredictArgs(ctx *Context, a complete.Args) []string {
if c.ShellComplete != nil {
return c.ShellComplete(ctx, a)
}
return nil
}
type Predictor interface {
PredictArgs(*Context, complete.Args) []string
}
// ContextPredictor determines what terms can follow a command or a flag
// It is used for autocompletion, given the last word in the already completed
// command line, what words can complete it.
type ContextPredictor struct {
predictor Predictor
ctx *Context
}
// Predict invokes the predict function and implements the Predictor interface
func (p ContextPredictor) Predict(a complete.Args) []string {
return p.predictor.PredictArgs(p.ctx, a)
}