Prefill user input (#101)

* Using a fillable stdin reader

* Adding some documentation to the new features
This commit is contained in:
mrsinham 2017-10-02 14:43:15 +02:00 committed by chzyer
parent af545c8af6
commit 9cc74fe5ad
3 changed files with 84 additions and 3 deletions

View File

@ -55,6 +55,7 @@ type Config struct {
FuncGetWidth func() int FuncGetWidth func() int
Stdin io.Reader Stdin io.Reader
StdinWriter io.Writer
Stdout io.Writer Stdout io.Writer
Stderr io.Writer Stderr io.Writer
@ -97,6 +98,9 @@ func (c *Config) Init() error {
if c.Stdin == nil { if c.Stdin == nil {
c.Stdin = NewCancelableStdin(Stdin) c.Stdin = NewCancelableStdin(Stdin)
} }
c.Stdin, c.StdinWriter = NewFillableStdin(c.Stdin)
if c.Stdout == nil { if c.Stdout == nil {
c.Stdout = Stdout c.Stdout = Stdout
} }
@ -281,6 +285,20 @@ func (i *Instance) Write(b []byte) (int, error) {
return i.Stdout().Write(b) return i.Stdout().Write(b)
} }
// WriteStdin prefill the next Stdin fetch
// Next time you call ReadLine() this value will be writen before the user input
// ie :
// i := readline.New()
// i.WriteStdin([]byte("test"))
// _, _= i.Readline()
//
// gives
//
// > test[cursor]
func (i *Instance) WriteStdin(val []byte) (int, error) {
return i.Terminal.WriteStdin(val)
}
func (i *Instance) SetConfig(cfg *Config) *Config { func (i *Instance) SetConfig(cfg *Config) *Config {
if i.Config == cfg { if i.Config == cfg {
return cfg return cfg

57
std.go
View File

@ -131,3 +131,60 @@ func (c *CancelableStdin) Close() error {
} }
return nil return nil
} }
// FillableStdin is a stdin reader which can prepend some data before
// reading into the real stdin
type FillableStdin struct {
sync.Mutex
stdin io.Reader
stdinBuffer io.Reader
buf []byte
bufErr error
}
// NewFillableStdin gives you FillableStdin
func NewFillableStdin(stdin io.Reader) (io.Reader, io.Writer) {
r, w := io.Pipe()
s := &FillableStdin{
stdinBuffer: r,
stdin: stdin,
}
s.ioloop()
return s, w
}
func (s *FillableStdin) ioloop() {
go func() {
for {
bufR := make([]byte, 100)
var n int
n, s.bufErr = s.stdinBuffer.Read(bufR)
s.Lock()
s.buf = append(s.buf, bufR[:n]...)
s.Unlock()
}
}()
}
// Read will read from the local buffer and if no data, read from stdin
func (s *FillableStdin) Read(p []byte) (n int, err error) {
s.Lock()
i := len(s.buf)
if len(p) < i {
i = len(p)
}
if i > 0 {
n := copy(p, s.buf)
s.buf = s.buf[:0]
cerr := s.bufErr
s.bufErr = nil
s.Unlock()
return n, cerr
}
s.Unlock()
return s.stdin.Read(p)
}

View File

@ -65,6 +65,12 @@ func (t *Terminal) Write(b []byte) (int, error) {
return t.cfg.Stdout.Write(b) return t.cfg.Stdout.Write(b)
} }
// WriteStdin prefill the next Stdin fetch
// Next time you call ReadLine() this value will be writen before the user input
func (t *Terminal) WriteStdin(b []byte) (int, error) {
return t.cfg.StdinWriter.Write(b)
}
type termSize struct { type termSize struct {
left int left int
top int top int