Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
kardolus
GitHub Repository: kardolus/chatgpt-cli
Path: blob/main/vendor/github.com/chzyer/readline/std.go
2875 views
1
package readline
2
3
import (
4
"io"
5
"os"
6
"sync"
7
"sync/atomic"
8
)
9
10
var (
11
Stdin io.ReadCloser = os.Stdin
12
Stdout io.WriteCloser = os.Stdout
13
Stderr io.WriteCloser = os.Stderr
14
)
15
16
var (
17
std *Instance
18
stdOnce sync.Once
19
)
20
21
// global instance will not submit history automatic
22
func getInstance() *Instance {
23
stdOnce.Do(func() {
24
std, _ = NewEx(&Config{
25
DisableAutoSaveHistory: true,
26
})
27
})
28
return std
29
}
30
31
// let readline load history from filepath
32
// and try to persist history into disk
33
// set fp to "" to prevent readline persisting history to disk
34
// so the `AddHistory` will return nil error forever.
35
func SetHistoryPath(fp string) {
36
ins := getInstance()
37
cfg := ins.Config.Clone()
38
cfg.HistoryFile = fp
39
ins.SetConfig(cfg)
40
}
41
42
// set auto completer to global instance
43
func SetAutoComplete(completer AutoCompleter) {
44
ins := getInstance()
45
cfg := ins.Config.Clone()
46
cfg.AutoComplete = completer
47
ins.SetConfig(cfg)
48
}
49
50
// add history to global instance manually
51
// raise error only if `SetHistoryPath` is set with a non-empty path
52
func AddHistory(content string) error {
53
ins := getInstance()
54
return ins.SaveHistory(content)
55
}
56
57
func Password(prompt string) ([]byte, error) {
58
ins := getInstance()
59
return ins.ReadPassword(prompt)
60
}
61
62
// readline with global configs
63
func Line(prompt string) (string, error) {
64
ins := getInstance()
65
ins.SetPrompt(prompt)
66
return ins.Readline()
67
}
68
69
type CancelableStdin struct {
70
r io.Reader
71
mutex sync.Mutex
72
stop chan struct{}
73
closed int32
74
notify chan struct{}
75
data []byte
76
read int
77
err error
78
}
79
80
func NewCancelableStdin(r io.Reader) *CancelableStdin {
81
c := &CancelableStdin{
82
r: r,
83
notify: make(chan struct{}),
84
stop: make(chan struct{}),
85
}
86
go c.ioloop()
87
return c
88
}
89
90
func (c *CancelableStdin) ioloop() {
91
loop:
92
for {
93
select {
94
case <-c.notify:
95
c.read, c.err = c.r.Read(c.data)
96
select {
97
case c.notify <- struct{}{}:
98
case <-c.stop:
99
break loop
100
}
101
case <-c.stop:
102
break loop
103
}
104
}
105
}
106
107
func (c *CancelableStdin) Read(b []byte) (n int, err error) {
108
c.mutex.Lock()
109
defer c.mutex.Unlock()
110
if atomic.LoadInt32(&c.closed) == 1 {
111
return 0, io.EOF
112
}
113
114
c.data = b
115
select {
116
case c.notify <- struct{}{}:
117
case <-c.stop:
118
return 0, io.EOF
119
}
120
select {
121
case <-c.notify:
122
return c.read, c.err
123
case <-c.stop:
124
return 0, io.EOF
125
}
126
}
127
128
func (c *CancelableStdin) Close() error {
129
if atomic.CompareAndSwapInt32(&c.closed, 0, 1) {
130
close(c.stop)
131
}
132
return nil
133
}
134
135
// FillableStdin is a stdin reader which can prepend some data before
136
// reading into the real stdin
137
type FillableStdin struct {
138
sync.Mutex
139
stdin io.Reader
140
stdinBuffer io.ReadCloser
141
buf []byte
142
bufErr error
143
}
144
145
// NewFillableStdin gives you FillableStdin
146
func NewFillableStdin(stdin io.Reader) (io.ReadCloser, io.Writer) {
147
r, w := io.Pipe()
148
s := &FillableStdin{
149
stdinBuffer: r,
150
stdin: stdin,
151
}
152
s.ioloop()
153
return s, w
154
}
155
156
func (s *FillableStdin) ioloop() {
157
go func() {
158
for {
159
bufR := make([]byte, 100)
160
var n int
161
n, s.bufErr = s.stdinBuffer.Read(bufR)
162
if s.bufErr != nil {
163
if s.bufErr == io.ErrClosedPipe {
164
break
165
}
166
}
167
s.Lock()
168
s.buf = append(s.buf, bufR[:n]...)
169
s.Unlock()
170
}
171
}()
172
}
173
174
// Read will read from the local buffer and if no data, read from stdin
175
func (s *FillableStdin) Read(p []byte) (n int, err error) {
176
s.Lock()
177
i := len(s.buf)
178
if len(p) < i {
179
i = len(p)
180
}
181
if i > 0 {
182
n := copy(p, s.buf)
183
s.buf = s.buf[:0]
184
cerr := s.bufErr
185
s.bufErr = nil
186
s.Unlock()
187
return n, cerr
188
}
189
s.Unlock()
190
n, err = s.stdin.Read(p)
191
return n, err
192
}
193
194
func (s *FillableStdin) Close() error {
195
s.stdinBuffer.Close()
196
return nil
197
}
198
199