Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
kardolus
GitHub Repository: kardolus/chatgpt-cli
Path: blob/main/vendor/github.com/chzyer/readline/search.go
2875 views
1
package readline
2
3
import (
4
"bytes"
5
"container/list"
6
"fmt"
7
"io"
8
)
9
10
const (
11
S_STATE_FOUND = iota
12
S_STATE_FAILING
13
)
14
15
const (
16
S_DIR_BCK = iota
17
S_DIR_FWD
18
)
19
20
type opSearch struct {
21
inMode bool
22
state int
23
dir int
24
source *list.Element
25
w io.Writer
26
buf *RuneBuffer
27
data []rune
28
history *opHistory
29
cfg *Config
30
markStart int
31
markEnd int
32
width int
33
}
34
35
func newOpSearch(w io.Writer, buf *RuneBuffer, history *opHistory, cfg *Config, width int) *opSearch {
36
return &opSearch{
37
w: w,
38
buf: buf,
39
cfg: cfg,
40
history: history,
41
width: width,
42
}
43
}
44
45
func (o *opSearch) OnWidthChange(newWidth int) {
46
o.width = newWidth
47
}
48
49
func (o *opSearch) IsSearchMode() bool {
50
return o.inMode
51
}
52
53
func (o *opSearch) SearchBackspace() {
54
if len(o.data) > 0 {
55
o.data = o.data[:len(o.data)-1]
56
o.search(true)
57
}
58
}
59
60
func (o *opSearch) findHistoryBy(isNewSearch bool) (int, *list.Element) {
61
if o.dir == S_DIR_BCK {
62
return o.history.FindBck(isNewSearch, o.data, o.buf.idx)
63
}
64
return o.history.FindFwd(isNewSearch, o.data, o.buf.idx)
65
}
66
67
func (o *opSearch) search(isChange bool) bool {
68
if len(o.data) == 0 {
69
o.state = S_STATE_FOUND
70
o.SearchRefresh(-1)
71
return true
72
}
73
idx, elem := o.findHistoryBy(isChange)
74
if elem == nil {
75
o.SearchRefresh(-2)
76
return false
77
}
78
o.history.current = elem
79
80
item := o.history.showItem(o.history.current.Value)
81
start, end := 0, 0
82
if o.dir == S_DIR_BCK {
83
start, end = idx, idx+len(o.data)
84
} else {
85
start, end = idx, idx+len(o.data)
86
idx += len(o.data)
87
}
88
o.buf.SetWithIdx(idx, item)
89
o.markStart, o.markEnd = start, end
90
o.SearchRefresh(idx)
91
return true
92
}
93
94
func (o *opSearch) SearchChar(r rune) {
95
o.data = append(o.data, r)
96
o.search(true)
97
}
98
99
func (o *opSearch) SearchMode(dir int) bool {
100
if o.width == 0 {
101
return false
102
}
103
alreadyInMode := o.inMode
104
o.inMode = true
105
o.dir = dir
106
o.source = o.history.current
107
if alreadyInMode {
108
o.search(false)
109
} else {
110
o.SearchRefresh(-1)
111
}
112
return true
113
}
114
115
func (o *opSearch) ExitSearchMode(revert bool) {
116
if revert {
117
o.history.current = o.source
118
o.buf.Set(o.history.showItem(o.history.current.Value))
119
}
120
o.markStart, o.markEnd = 0, 0
121
o.state = S_STATE_FOUND
122
o.inMode = false
123
o.source = nil
124
o.data = nil
125
}
126
127
func (o *opSearch) SearchRefresh(x int) {
128
if x == -2 {
129
o.state = S_STATE_FAILING
130
} else if x >= 0 {
131
o.state = S_STATE_FOUND
132
}
133
if x < 0 {
134
x = o.buf.idx
135
}
136
x = o.buf.CurrentWidth(x)
137
x += o.buf.PromptLen()
138
x = x % o.width
139
140
if o.markStart > 0 {
141
o.buf.SetStyle(o.markStart, o.markEnd, "4")
142
}
143
144
lineCnt := o.buf.CursorLineCount()
145
buf := bytes.NewBuffer(nil)
146
buf.Write(bytes.Repeat([]byte("\n"), lineCnt))
147
buf.WriteString("\033[J")
148
if o.state == S_STATE_FAILING {
149
buf.WriteString("failing ")
150
}
151
if o.dir == S_DIR_BCK {
152
buf.WriteString("bck")
153
} else if o.dir == S_DIR_FWD {
154
buf.WriteString("fwd")
155
}
156
buf.WriteString("-i-search: ")
157
buf.WriteString(string(o.data)) // keyword
158
buf.WriteString("\033[4m \033[0m") // _
159
fmt.Fprintf(buf, "\r\033[%dA", lineCnt) // move prev
160
if x > 0 {
161
fmt.Fprintf(buf, "\033[%dC", x) // move forward
162
}
163
o.w.Write(buf.Bytes())
164
}
165
166