• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

jedib0t / go-prompter / 6355076892

29 Sep 2023 05:32PM UTC coverage: 96.836% (+4.6%) from 92.25%
6355076892

push

github

web-flow
prompt: support custom io (Reader/Writer) (#7)

94 of 94 new or added lines in 4 files covered. (100.0%)

2357 of 2434 relevant lines covered (96.84%)

57.87 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.47
/prompt/prompt_autocomplete.go
1
package prompt
2

3
import (
4
        "context"
5
        "fmt"
6
        "strings"
7
        "time"
8

9
        "github.com/jedib0t/go-pretty/v6/text"
10
)
11

12
func (p *prompt) autoComplete(lines []string, cursorPos CursorLocation, startIdx int) []string {
14✔
13
        suggestions, suggestionsIdx := p.getSuggestionsAndIdx()
14✔
14
        if len(suggestions) == 0 {
24✔
15
                p.isInAutoComplete = false
10✔
16
                return lines
10✔
17
        }
10✔
18
        p.isInAutoComplete = true
4✔
19

4✔
20
        // get the line styling
4✔
21
        linePrefix, prefixWidth, _, numLen, _, _ := p.calculateLineStyling(lines)
4✔
22
        word, _ := p.buffer.getWordAtCursor()
4✔
23
        wordLen := len(word)
4✔
24

4✔
25
        // get the suggestions printed to super-impose on the displayed lines
4✔
26
        suggestionsDropDown := printSuggestionsDropDown(suggestions, suggestionsIdx, p.style.AutoComplete)
4✔
27

4✔
28
        // if the suggestions are going beyond the last line, pad the lines
4✔
29
        numEmptyLinesToAppend := (len(suggestionsDropDown) + 1 + cursorPos.Line - startIdx) - len(lines)
4✔
30
        if numEmptyLinesToAppend > 0 {
8✔
31
                prefix := linePrefix
4✔
32
                if p.style.LineNumbers.Enabled {
4✔
33
                        prefix += strings.Repeat("", numLen)
×
34
                }
×
35
                if prefix != "" {
8✔
36
                        prefix += " "
4✔
37
                }
4✔
38
                for numEmptyLinesToAppend > 0 {
14✔
39
                        lines = append(lines, prefix)
10✔
40
                        numEmptyLinesToAppend--
10✔
41
                }
10✔
42
        }
43

44
        displayWidth := p.getDisplayWidth()
4✔
45
        for idx, suggestion := range suggestionsDropDown {
14✔
46
                lineIdx := idx + cursorPos.Line + 1 - startIdx
10✔
47
                lines[lineIdx] = overwriteContents(
10✔
48
                        lines[lineIdx], suggestion, prefixWidth+cursorPos.Column-wordLen-1, displayWidth,
10✔
49
                )
10✔
50
        }
10✔
51
        return lines
4✔
52
}
53

54
func (p *prompt) updateSuggestions(ctx context.Context) {
3✔
55
        lastLine, lastWord, lastIdx := "", "", -1
3✔
56
        tick := time.Tick(p.refreshInterval)
3✔
57
        for {
12✔
58
                select {
9✔
59
                case <-ctx.Done():
3✔
60
                        return
3✔
61
                case <-tick:
6✔
62
                        if p.isRenderPaused() {
6✔
63
                                continue
×
64
                        }
65
                        lastLine, lastWord, lastIdx = p.updateSuggestionsInternal(lastLine, lastWord, lastIdx)
6✔
66
                }
67
        }
68
}
69

70
func (p *prompt) updateSuggestionsInternal(lastLine string, lastWord string, lastIdx int) (string, string, int) {
7✔
71
        // grab the current line, word and index
7✔
72
        p.buffer.mutex.Lock()
7✔
73
        line := p.buffer.getCurrentLine()
7✔
74
        location := uint(p.buffer.cursor.Column)
7✔
75
        word, idx := p.buffer.getWordAtCursor()
7✔
76
        p.buffer.mutex.Unlock()
7✔
77

7✔
78
        // if there is no word currently, clear drop-down
7✔
79
        forced := false
7✔
80
        if p.forcedAutoComplete() {
7✔
81
                forced = true
×
82
        } else if word == "" || idx < 0 {
13✔
83
                p.setSuggestions(make([]Suggestion, 0))
6✔
84
                p.clearDebugData("ac.")
6✔
85
                return line, word, idx
6✔
86
        }
6✔
87

88
        // if there is no change compared to before, return old result
89
        p.setDebugData("ac.idx", fmt.Sprint(idx))
1✔
90
        p.setDebugData("ac.word", fmt.Sprintf("%#v", word))
1✔
91
        if (line == lastLine && word == lastWord && idx == lastIdx) && !forced {
1✔
92
                return line, word, idx
×
93
        }
×
94

95
        // prep
96
        var suggestions []Suggestion
1✔
97
        if p.autoCompleterContextual != nil {
1✔
98
                suggestions = append(suggestions, p.autoCompleterContextual(line, word, location)...)
×
99
        }
×
100
        if p.autoCompleter != nil {
2✔
101
                suggestions = append(suggestions, p.autoCompleter(line, word, location)...)
1✔
102
        }
1✔
103

104
        // update
105
        currentSuggestions, _ := p.getSuggestionsAndIdx()
1✔
106
        if fmt.Sprintf("%#v", suggestions) != fmt.Sprintf("%#v", currentSuggestions) {
2✔
107
                p.setSuggestions(suggestions)
1✔
108
        }
1✔
109
        return line, word, idx
1✔
110
}
111

112
func printSuggestion(value string, color Color, maxLen int) string {
20✔
113
        if maxLen == 0 {
26✔
114
                return ""
6✔
115
        }
6✔
116

117
        if len(value) > maxLen {
14✔
118
                value = text.Trim(value, maxLen-1) + "~"
×
119
        }
×
120
        if len(value) < maxLen {
20✔
121
                value = text.Pad(value, maxLen, ' ')
6✔
122
        }
6✔
123
        value = color.Sprintf(" %s ", value)
14✔
124
        return value
14✔
125
}
126

127
func printSuggestionsDropDown(suggestions []Suggestion, suggestionsIdx int, style StyleAutoComplete) []string {
4✔
128
        // calculate the lengths for the values and hints
4✔
129
        lenValue, lenHint := 0, 0
4✔
130
        for _, s := range suggestions {
14✔
131
                if len(s.Value) > lenValue {
15✔
132
                        lenValue = len(s.Value)
5✔
133
                }
5✔
134
                if len(s.Hint) > lenHint {
11✔
135
                        lenHint = len(s.Hint)
1✔
136
                }
1✔
137
        }
138
        lenValue = clampValue(lenValue, style.ValueLengthMin, style.ValueLengthMax)
4✔
139
        lenHint = clampValueAllowZero(lenHint, style.HintLengthMin, style.HintLengthMax)
4✔
140

4✔
141
        // calculate the view port range (range of suggestions to display)
4✔
142
        start, stop := calculateViewportRange(len(suggestions), suggestionsIdx, style.NumItems)
4✔
143

4✔
144
        // generate the scrollbar for the drop-down
4✔
145
        scrollbar, _ := style.Scrollbar.Generate(len(suggestions), suggestionsIdx, style.NumItems)
4✔
146

4✔
147
        // generate the drop-down
4✔
148
        var lines []string
4✔
149
        for idx, s := range suggestions {
14✔
150
                // skip if out of viewport
10✔
151
                if idx < start || idx > stop {
10✔
152
                        continue
×
153
                }
154

155
                valueColor, hintColor := style.ValueColor, style.HintColor
10✔
156
                if idx == suggestionsIdx {
14✔
157
                        valueColor, hintColor = style.ValueSelectedColor, style.HintSelectedColor
4✔
158
                }
4✔
159
                value := printSuggestion(s.Value, valueColor, lenValue)
10✔
160
                hint := printSuggestion(s.Hint, hintColor, lenHint)
10✔
161
                scroll := scrollbar[idx-start]
10✔
162
                lines = append(lines, value+hint+scroll)
10✔
163
        }
164
        return lines
4✔
165
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc