Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
kardolus
GitHub Repository: kardolus/chatgpt-cli
Path: blob/main/vendor/github.com/chzyer/readline/readline.go
2875 views
1
// Readline is a pure go implementation for GNU-Readline kind library.
2
//
3
// example:
4
// rl, err := readline.New("> ")
5
// if err != nil {
6
// panic(err)
7
// }
8
// defer rl.Close()
9
//
10
// for {
11
// line, err := rl.Readline()
12
// if err != nil { // io.EOF
13
// break
14
// }
15
// println(line)
16
// }
17
//
18
package readline
19
20
import (
21
"io"
22
)
23
24
type Instance struct {
25
Config *Config
26
Terminal *Terminal
27
Operation *Operation
28
}
29
30
type Config struct {
31
// prompt supports ANSI escape sequence, so we can color some characters even in windows
32
Prompt string
33
34
// readline will persist historys to file where HistoryFile specified
35
HistoryFile string
36
// specify the max length of historys, it's 500 by default, set it to -1 to disable history
37
HistoryLimit int
38
DisableAutoSaveHistory bool
39
// enable case-insensitive history searching
40
HistorySearchFold bool
41
42
// AutoCompleter will called once user press TAB
43
AutoComplete AutoCompleter
44
45
// Any key press will pass to Listener
46
// NOTE: Listener will be triggered by (nil, 0, 0) immediately
47
Listener Listener
48
49
Painter Painter
50
51
// If VimMode is true, readline will in vim.insert mode by default
52
VimMode bool
53
54
InterruptPrompt string
55
EOFPrompt string
56
57
FuncGetWidth func() int
58
59
Stdin io.ReadCloser
60
StdinWriter io.Writer
61
Stdout io.Writer
62
Stderr io.Writer
63
64
EnableMask bool
65
MaskRune rune
66
67
// erase the editing line after user submited it
68
// it use in IM usually.
69
UniqueEditLine bool
70
71
// filter input runes (may be used to disable CtrlZ or for translating some keys to different actions)
72
// -> output = new (translated) rune and true/false if continue with processing this one
73
FuncFilterInputRune func(rune) (rune, bool)
74
75
// force use interactive even stdout is not a tty
76
FuncIsTerminal func() bool
77
FuncMakeRaw func() error
78
FuncExitRaw func() error
79
FuncOnWidthChanged func(func())
80
ForceUseInteractive bool
81
82
// private fields
83
inited bool
84
opHistory *opHistory
85
opSearch *opSearch
86
}
87
88
func (c *Config) useInteractive() bool {
89
if c.ForceUseInteractive {
90
return true
91
}
92
return c.FuncIsTerminal()
93
}
94
95
func (c *Config) Init() error {
96
if c.inited {
97
return nil
98
}
99
c.inited = true
100
if c.Stdin == nil {
101
c.Stdin = NewCancelableStdin(Stdin)
102
}
103
104
c.Stdin, c.StdinWriter = NewFillableStdin(c.Stdin)
105
106
if c.Stdout == nil {
107
c.Stdout = Stdout
108
}
109
if c.Stderr == nil {
110
c.Stderr = Stderr
111
}
112
if c.HistoryLimit == 0 {
113
c.HistoryLimit = 500
114
}
115
116
if c.InterruptPrompt == "" {
117
c.InterruptPrompt = "^C"
118
} else if c.InterruptPrompt == "\n" {
119
c.InterruptPrompt = ""
120
}
121
if c.EOFPrompt == "" {
122
c.EOFPrompt = "^D"
123
} else if c.EOFPrompt == "\n" {
124
c.EOFPrompt = ""
125
}
126
127
if c.AutoComplete == nil {
128
c.AutoComplete = &TabCompleter{}
129
}
130
if c.FuncGetWidth == nil {
131
c.FuncGetWidth = GetScreenWidth
132
}
133
if c.FuncIsTerminal == nil {
134
c.FuncIsTerminal = DefaultIsTerminal
135
}
136
rm := new(RawMode)
137
if c.FuncMakeRaw == nil {
138
c.FuncMakeRaw = rm.Enter
139
}
140
if c.FuncExitRaw == nil {
141
c.FuncExitRaw = rm.Exit
142
}
143
if c.FuncOnWidthChanged == nil {
144
c.FuncOnWidthChanged = DefaultOnWidthChanged
145
}
146
147
return nil
148
}
149
150
func (c Config) Clone() *Config {
151
c.opHistory = nil
152
c.opSearch = nil
153
return &c
154
}
155
156
func (c *Config) SetListener(f func(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool)) {
157
c.Listener = FuncListener(f)
158
}
159
160
func (c *Config) SetPainter(p Painter) {
161
c.Painter = p
162
}
163
164
func NewEx(cfg *Config) (*Instance, error) {
165
t, err := NewTerminal(cfg)
166
if err != nil {
167
return nil, err
168
}
169
rl := t.Readline()
170
if cfg.Painter == nil {
171
cfg.Painter = &defaultPainter{}
172
}
173
return &Instance{
174
Config: cfg,
175
Terminal: t,
176
Operation: rl,
177
}, nil
178
}
179
180
func New(prompt string) (*Instance, error) {
181
return NewEx(&Config{Prompt: prompt})
182
}
183
184
func (i *Instance) ResetHistory() {
185
i.Operation.ResetHistory()
186
}
187
188
func (i *Instance) SetPrompt(s string) {
189
i.Operation.SetPrompt(s)
190
}
191
192
func (i *Instance) SetMaskRune(r rune) {
193
i.Operation.SetMaskRune(r)
194
}
195
196
// change history persistence in runtime
197
func (i *Instance) SetHistoryPath(p string) {
198
i.Operation.SetHistoryPath(p)
199
}
200
201
// readline will refresh automatic when write through Stdout()
202
func (i *Instance) Stdout() io.Writer {
203
return i.Operation.Stdout()
204
}
205
206
// readline will refresh automatic when write through Stdout()
207
func (i *Instance) Stderr() io.Writer {
208
return i.Operation.Stderr()
209
}
210
211
// switch VimMode in runtime
212
func (i *Instance) SetVimMode(on bool) {
213
i.Operation.SetVimMode(on)
214
}
215
216
func (i *Instance) IsVimMode() bool {
217
return i.Operation.IsEnableVimMode()
218
}
219
220
func (i *Instance) GenPasswordConfig() *Config {
221
return i.Operation.GenPasswordConfig()
222
}
223
224
// we can generate a config by `i.GenPasswordConfig()`
225
func (i *Instance) ReadPasswordWithConfig(cfg *Config) ([]byte, error) {
226
return i.Operation.PasswordWithConfig(cfg)
227
}
228
229
func (i *Instance) ReadPasswordEx(prompt string, l Listener) ([]byte, error) {
230
return i.Operation.PasswordEx(prompt, l)
231
}
232
233
func (i *Instance) ReadPassword(prompt string) ([]byte, error) {
234
return i.Operation.Password(prompt)
235
}
236
237
type Result struct {
238
Line string
239
Error error
240
}
241
242
func (l *Result) CanContinue() bool {
243
return len(l.Line) != 0 && l.Error == ErrInterrupt
244
}
245
246
func (l *Result) CanBreak() bool {
247
return !l.CanContinue() && l.Error != nil
248
}
249
250
func (i *Instance) Line() *Result {
251
ret, err := i.Readline()
252
return &Result{ret, err}
253
}
254
255
// err is one of (nil, io.EOF, readline.ErrInterrupt)
256
func (i *Instance) Readline() (string, error) {
257
return i.Operation.String()
258
}
259
260
func (i *Instance) ReadlineWithDefault(what string) (string, error) {
261
i.Operation.SetBuffer(what)
262
return i.Operation.String()
263
}
264
265
func (i *Instance) SaveHistory(content string) error {
266
return i.Operation.SaveHistory(content)
267
}
268
269
// same as readline
270
func (i *Instance) ReadSlice() ([]byte, error) {
271
return i.Operation.Slice()
272
}
273
274
// we must make sure that call Close() before process exit.
275
// if there has a pending reading operation, that reading will be interrupted.
276
// so you can capture the signal and call Instance.Close(), it's thread-safe.
277
func (i *Instance) Close() error {
278
i.Config.Stdin.Close()
279
i.Operation.Close()
280
if err := i.Terminal.Close(); err != nil {
281
return err
282
}
283
return nil
284
}
285
286
// call CaptureExitSignal when you want readline exit gracefully.
287
func (i *Instance) CaptureExitSignal() {
288
CaptureExitSignal(func() {
289
i.Close()
290
})
291
}
292
293
func (i *Instance) Clean() {
294
i.Operation.Clean()
295
}
296
297
func (i *Instance) Write(b []byte) (int, error) {
298
return i.Stdout().Write(b)
299
}
300
301
// WriteStdin prefill the next Stdin fetch
302
// Next time you call ReadLine() this value will be writen before the user input
303
// ie :
304
// i := readline.New()
305
// i.WriteStdin([]byte("test"))
306
// _, _= i.Readline()
307
//
308
// gives
309
//
310
// > test[cursor]
311
func (i *Instance) WriteStdin(val []byte) (int, error) {
312
return i.Terminal.WriteStdin(val)
313
}
314
315
func (i *Instance) SetConfig(cfg *Config) *Config {
316
if i.Config == cfg {
317
return cfg
318
}
319
old := i.Config
320
i.Config = cfg
321
i.Operation.SetConfig(cfg)
322
i.Terminal.SetConfig(cfg)
323
return old
324
}
325
326
func (i *Instance) Refresh() {
327
i.Operation.Refresh()
328
}
329
330
// HistoryDisable the save of the commands into the history
331
func (i *Instance) HistoryDisable() {
332
i.Operation.history.Disable()
333
}
334
335
// HistoryEnable the save of the commands into the history (default on)
336
func (i *Instance) HistoryEnable() {
337
i.Operation.history.Enable()
338
}
339
340