-
Notifications
You must be signed in to change notification settings - Fork 0
/
completion.go
105 lines (92 loc) · 2.63 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
package modes
import (
"errors"
"strings"
"github.com/markusbkk/elvish/pkg/cli"
"github.com/markusbkk/elvish/pkg/cli/tk"
"github.com/markusbkk/elvish/pkg/diag"
"github.com/markusbkk/elvish/pkg/ui"
)
// Completion is a mode specialized for viewing and inserting completion
// candidates. It is based on the ComboBox widget.
type Completion interface {
tk.ComboBox
}
// CompletionSpec specifies the configuration for the completion mode.
type CompletionSpec struct {
Bindings tk.Bindings
Name string
Replace diag.Ranging
Items []CompletionItem
Filter FilterSpec
}
// CompletionItem represents a completion item, also known as a candidate.
type CompletionItem struct {
// Used in the UI and for filtering.
ToShow ui.Text
// Used when inserting a candidate.
ToInsert string
}
type completion struct {
tk.ComboBox
attached tk.CodeArea
}
var errNoCandidates = errors.New("no candidates")
// NewCompletion starts the completion UI.
func NewCompletion(app cli.App, cfg CompletionSpec) (Completion, error) {
codeArea, err := FocusedCodeArea(app)
if err != nil {
return nil, err
}
if len(cfg.Items) == 0 {
return nil, errNoCandidates
}
w := tk.NewComboBox(tk.ComboBoxSpec{
CodeArea: tk.CodeAreaSpec{
Prompt: modePrompt(" COMPLETING "+cfg.Name+" ", true),
Highlighter: cfg.Filter.Highlighter,
},
ListBox: tk.ListBoxSpec{
Horizontal: true,
Bindings: cfg.Bindings,
OnSelect: func(it tk.Items, i int) {
text := it.(completionItems)[i].ToInsert
codeArea.MutateState(func(s *tk.CodeAreaState) {
s.Pending = tk.PendingCode{
From: cfg.Replace.From, To: cfg.Replace.To, Content: text}
})
},
OnAccept: func(it tk.Items, i int) {
codeArea.MutateState((*tk.CodeAreaState).ApplyPending)
app.PopAddon()
},
ExtendStyle: true,
},
OnFilter: func(w tk.ComboBox, p string) {
w.ListBox().Reset(filterCompletionItems(cfg.Items, cfg.Filter.makePredicate(p)), 0)
},
})
return completion{w, codeArea}, nil
}
func (w completion) Dismiss() {
w.attached.MutateState(func(s *tk.CodeAreaState) { s.Pending = tk.PendingCode{} })
}
type completionItems []CompletionItem
func filterCompletionItems(all []CompletionItem, p func(string) bool) completionItems {
var filtered []CompletionItem
for _, candidate := range all {
if p(unstyle(candidate.ToShow)) {
filtered = append(filtered, candidate)
}
}
return filtered
}
func (it completionItems) Show(i int) ui.Text { return it[i].ToShow }
func (it completionItems) Len() int { return len(it) }
func unstyle(t ui.Text) string {
var sb strings.Builder
for _, seg := range t {
sb.WriteString(seg.Text)
}
return sb.String()
}