...
1 package prompt
2
3 import (
4 "strings"
5
6 "github.com/c-bata/go-prompt/internal/debug"
7 runewidth "github.com/mattn/go-runewidth"
8 )
9
10 const (
11 shortenSuffix = "..."
12 leftPrefix = " "
13 leftSuffix = " "
14 rightPrefix = " "
15 rightSuffix = " "
16 )
17
18 var (
19 leftMargin = runewidth.StringWidth(leftPrefix + leftSuffix)
20 rightMargin = runewidth.StringWidth(rightPrefix + rightSuffix)
21 completionMargin = leftMargin + rightMargin
22 )
23
24
25 type Suggest struct {
26 Text string
27 Description string
28 }
29
30
31 type CompletionManager struct {
32 selected int
33 tmp []Suggest
34 max uint16
35 completer Completer
36
37 verticalScroll int
38 wordSeparator string
39 showAtStart bool
40 }
41
42
43 func (c *CompletionManager) GetSelectedSuggestion() (s Suggest, ok bool) {
44 if c.selected == -1 {
45 return Suggest{}, false
46 } else if c.selected < -1 {
47 debug.Assert(false, "must not reach here")
48 c.selected = -1
49 return Suggest{}, false
50 }
51 return c.tmp[c.selected], true
52 }
53
54
55 func (c *CompletionManager) GetSuggestions() []Suggest {
56 return c.tmp
57 }
58
59
60 func (c *CompletionManager) Reset() {
61 c.selected = -1
62 c.verticalScroll = 0
63 c.Update(*NewDocument())
64 }
65
66
67 func (c *CompletionManager) Update(in Document) {
68 c.tmp = c.completer(in)
69 }
70
71
72 func (c *CompletionManager) Previous() {
73 if c.verticalScroll == c.selected && c.selected > 0 {
74 c.verticalScroll--
75 }
76 c.selected--
77 c.update()
78 }
79
80
81 func (c *CompletionManager) Next() {
82 if c.verticalScroll+int(c.max)-1 == c.selected {
83 c.verticalScroll++
84 }
85 c.selected++
86 c.update()
87 }
88
89
90 func (c *CompletionManager) Completing() bool {
91 return c.selected != -1
92 }
93
94 func (c *CompletionManager) update() {
95 max := int(c.max)
96 if len(c.tmp) < max {
97 max = len(c.tmp)
98 }
99
100 if c.selected >= len(c.tmp) {
101 c.Reset()
102 } else if c.selected < -1 {
103 c.selected = len(c.tmp) - 1
104 c.verticalScroll = len(c.tmp) - max
105 }
106 }
107
108 func deleteBreakLineCharacters(s string) string {
109 s = strings.Replace(s, "\n", "", -1)
110 s = strings.Replace(s, "\r", "", -1)
111 return s
112 }
113
114 func formatTexts(o []string, max int, prefix, suffix string) (new []string, width int) {
115 l := len(o)
116 n := make([]string, l)
117
118 lenPrefix := runewidth.StringWidth(prefix)
119 lenSuffix := runewidth.StringWidth(suffix)
120 lenShorten := runewidth.StringWidth(shortenSuffix)
121 min := lenPrefix + lenSuffix + lenShorten
122 for i := 0; i < l; i++ {
123 o[i] = deleteBreakLineCharacters(o[i])
124
125 w := runewidth.StringWidth(o[i])
126 if width < w {
127 width = w
128 }
129 }
130
131 if width == 0 {
132 return n, 0
133 }
134 if min >= max {
135 return n, 0
136 }
137 if lenPrefix+width+lenSuffix > max {
138 width = max - lenPrefix - lenSuffix
139 }
140
141 for i := 0; i < l; i++ {
142 x := runewidth.StringWidth(o[i])
143 if x <= width {
144 spaces := strings.Repeat(" ", width-x)
145 n[i] = prefix + o[i] + spaces + suffix
146 } else if x > width {
147 x := runewidth.Truncate(o[i], width, shortenSuffix)
148
149
150 n[i] = prefix + runewidth.FillRight(x, width) + suffix
151 }
152 }
153 return n, lenPrefix + width + lenSuffix
154 }
155
156 func formatSuggestions(suggests []Suggest, max int) (new []Suggest, width int) {
157 num := len(suggests)
158 new = make([]Suggest, num)
159
160 left := make([]string, num)
161 for i := 0; i < num; i++ {
162 left[i] = suggests[i].Text
163 }
164 right := make([]string, num)
165 for i := 0; i < num; i++ {
166 right[i] = suggests[i].Description
167 }
168
169 left, leftWidth := formatTexts(left, max, leftPrefix, leftSuffix)
170 if leftWidth == 0 {
171 return []Suggest{}, 0
172 }
173 right, rightWidth := formatTexts(right, max-leftWidth, rightPrefix, rightSuffix)
174
175 for i := 0; i < num; i++ {
176 new[i] = Suggest{Text: left[i], Description: right[i]}
177 }
178 return new, leftWidth + rightWidth
179 }
180
181
182 func NewCompletionManager(completer Completer, max uint16) *CompletionManager {
183 return &CompletionManager{
184 selected: -1,
185 max: max,
186 completer: completer,
187
188 verticalScroll: 0,
189 }
190 }
191
View as plain text