Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
kardolus
GitHub Repository: kardolus/chatgpt-cli
Path: blob/main/vendor/github.com/chzyer/readline/utils.go
2875 views
1
package readline
2
3
import (
4
"bufio"
5
"bytes"
6
"container/list"
7
"fmt"
8
"os"
9
"os/signal"
10
"strconv"
11
"strings"
12
"sync"
13
"syscall"
14
"time"
15
"unicode"
16
)
17
18
var (
19
isWindows = false
20
)
21
22
const (
23
CharLineStart = 1
24
CharBackward = 2
25
CharInterrupt = 3
26
CharDelete = 4
27
CharLineEnd = 5
28
CharForward = 6
29
CharBell = 7
30
CharCtrlH = 8
31
CharTab = 9
32
CharCtrlJ = 10
33
CharKill = 11
34
CharCtrlL = 12
35
CharEnter = 13
36
CharNext = 14
37
CharPrev = 16
38
CharBckSearch = 18
39
CharFwdSearch = 19
40
CharTranspose = 20
41
CharCtrlU = 21
42
CharCtrlW = 23
43
CharCtrlY = 25
44
CharCtrlZ = 26
45
CharEsc = 27
46
CharO = 79
47
CharEscapeEx = 91
48
CharBackspace = 127
49
)
50
51
const (
52
MetaBackward rune = -iota - 1
53
MetaForward
54
MetaDelete
55
MetaBackspace
56
MetaTranspose
57
)
58
59
// WaitForResume need to call before current process got suspend.
60
// It will run a ticker until a long duration is occurs,
61
// which means this process is resumed.
62
func WaitForResume() chan struct{} {
63
ch := make(chan struct{})
64
var wg sync.WaitGroup
65
wg.Add(1)
66
go func() {
67
ticker := time.NewTicker(10 * time.Millisecond)
68
t := time.Now()
69
wg.Done()
70
for {
71
now := <-ticker.C
72
if now.Sub(t) > 100*time.Millisecond {
73
break
74
}
75
t = now
76
}
77
ticker.Stop()
78
ch <- struct{}{}
79
}()
80
wg.Wait()
81
return ch
82
}
83
84
func Restore(fd int, state *State) error {
85
err := restoreTerm(fd, state)
86
if err != nil {
87
// errno 0 means everything is ok :)
88
if err.Error() == "errno 0" {
89
return nil
90
} else {
91
return err
92
}
93
}
94
return nil
95
}
96
97
func IsPrintable(key rune) bool {
98
isInSurrogateArea := key >= 0xd800 && key <= 0xdbff
99
return key >= 32 && !isInSurrogateArea
100
}
101
102
// translate Esc[X
103
func escapeExKey(key *escapeKeyPair) rune {
104
var r rune
105
switch key.typ {
106
case 'D':
107
r = CharBackward
108
case 'C':
109
r = CharForward
110
case 'A':
111
r = CharPrev
112
case 'B':
113
r = CharNext
114
case 'H':
115
r = CharLineStart
116
case 'F':
117
r = CharLineEnd
118
case '~':
119
if key.attr == "3" {
120
r = CharDelete
121
}
122
default:
123
}
124
return r
125
}
126
127
// translate EscOX SS3 codes for up/down/etc.
128
func escapeSS3Key(key *escapeKeyPair) rune {
129
var r rune
130
switch key.typ {
131
case 'D':
132
r = CharBackward
133
case 'C':
134
r = CharForward
135
case 'A':
136
r = CharPrev
137
case 'B':
138
r = CharNext
139
case 'H':
140
r = CharLineStart
141
case 'F':
142
r = CharLineEnd
143
default:
144
}
145
return r
146
}
147
148
type escapeKeyPair struct {
149
attr string
150
typ rune
151
}
152
153
func (e *escapeKeyPair) Get2() (int, int, bool) {
154
sp := strings.Split(e.attr, ";")
155
if len(sp) < 2 {
156
return -1, -1, false
157
}
158
s1, err := strconv.Atoi(sp[0])
159
if err != nil {
160
return -1, -1, false
161
}
162
s2, err := strconv.Atoi(sp[1])
163
if err != nil {
164
return -1, -1, false
165
}
166
return s1, s2, true
167
}
168
169
func readEscKey(r rune, reader *bufio.Reader) *escapeKeyPair {
170
p := escapeKeyPair{}
171
buf := bytes.NewBuffer(nil)
172
for {
173
if r == ';' {
174
} else if unicode.IsNumber(r) {
175
} else {
176
p.typ = r
177
break
178
}
179
buf.WriteRune(r)
180
r, _, _ = reader.ReadRune()
181
}
182
p.attr = buf.String()
183
return &p
184
}
185
186
// translate EscX to Meta+X
187
func escapeKey(r rune, reader *bufio.Reader) rune {
188
switch r {
189
case 'b':
190
r = MetaBackward
191
case 'f':
192
r = MetaForward
193
case 'd':
194
r = MetaDelete
195
case CharTranspose:
196
r = MetaTranspose
197
case CharBackspace:
198
r = MetaBackspace
199
case 'O':
200
d, _, _ := reader.ReadRune()
201
switch d {
202
case 'H':
203
r = CharLineStart
204
case 'F':
205
r = CharLineEnd
206
default:
207
reader.UnreadRune()
208
}
209
case CharEsc:
210
211
}
212
return r
213
}
214
215
func SplitByLine(start, screenWidth int, rs []rune) []string {
216
var ret []string
217
buf := bytes.NewBuffer(nil)
218
currentWidth := start
219
for _, r := range rs {
220
w := runes.Width(r)
221
currentWidth += w
222
buf.WriteRune(r)
223
if currentWidth >= screenWidth {
224
ret = append(ret, buf.String())
225
buf.Reset()
226
currentWidth = 0
227
}
228
}
229
ret = append(ret, buf.String())
230
return ret
231
}
232
233
// calculate how many lines for N character
234
func LineCount(screenWidth, w int) int {
235
r := w / screenWidth
236
if w%screenWidth != 0 {
237
r++
238
}
239
return r
240
}
241
242
func IsWordBreak(i rune) bool {
243
switch {
244
case i >= 'a' && i <= 'z':
245
case i >= 'A' && i <= 'Z':
246
case i >= '0' && i <= '9':
247
default:
248
return true
249
}
250
return false
251
}
252
253
func GetInt(s []string, def int) int {
254
if len(s) == 0 {
255
return def
256
}
257
c, err := strconv.Atoi(s[0])
258
if err != nil {
259
return def
260
}
261
return c
262
}
263
264
type RawMode struct {
265
state *State
266
}
267
268
func (r *RawMode) Enter() (err error) {
269
r.state, err = MakeRaw(GetStdin())
270
return err
271
}
272
273
func (r *RawMode) Exit() error {
274
if r.state == nil {
275
return nil
276
}
277
return Restore(GetStdin(), r.state)
278
}
279
280
// -----------------------------------------------------------------------------
281
282
func sleep(n int) {
283
Debug(n)
284
time.Sleep(2000 * time.Millisecond)
285
}
286
287
// print a linked list to Debug()
288
func debugList(l *list.List) {
289
idx := 0
290
for e := l.Front(); e != nil; e = e.Next() {
291
Debug(idx, fmt.Sprintf("%+v", e.Value))
292
idx++
293
}
294
}
295
296
// append log info to another file
297
func Debug(o ...interface{}) {
298
f, _ := os.OpenFile("debug.tmp", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
299
fmt.Fprintln(f, o...)
300
f.Close()
301
}
302
303
func CaptureExitSignal(f func()) {
304
cSignal := make(chan os.Signal, 1)
305
signal.Notify(cSignal, os.Interrupt, syscall.SIGTERM)
306
go func() {
307
for range cSignal {
308
f()
309
}
310
}()
311
}
312
313