From 9cc74fe5ad0b00fec8ec9aae93ac4f94dd5cbd1a Mon Sep 17 00:00:00 2001 From: mrsinham Date: Mon, 2 Oct 2017 14:43:15 +0200 Subject: [PATCH] Prefill user input (#101) * Using a fillable stdin reader * Adding some documentation to the new features --- readline.go | 24 +++++++++++++++++++--- std.go | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++ terminal.go | 6 ++++++ 3 files changed, 84 insertions(+), 3 deletions(-) diff --git a/readline.go b/readline.go index a341a82..585d94b 100644 --- a/readline.go +++ b/readline.go @@ -54,9 +54,10 @@ type Config struct { FuncGetWidth func() int - Stdin io.Reader - Stdout io.Writer - Stderr io.Writer + Stdin io.Reader + StdinWriter io.Writer + Stdout io.Writer + Stderr io.Writer EnableMask bool MaskRune rune @@ -97,6 +98,9 @@ func (c *Config) Init() error { if c.Stdin == nil { c.Stdin = NewCancelableStdin(Stdin) } + + c.Stdin, c.StdinWriter = NewFillableStdin(c.Stdin) + if c.Stdout == nil { c.Stdout = Stdout } @@ -281,6 +285,20 @@ func (i *Instance) Write(b []byte) (int, error) { 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 { if i.Config == cfg { return cfg diff --git a/std.go b/std.go index e0c55ee..0fd90ff 100644 --- a/std.go +++ b/std.go @@ -131,3 +131,60 @@ func (c *CancelableStdin) Close() error { } 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) + +} diff --git a/terminal.go b/terminal.go index 8014712..1078631 100644 --- a/terminal.go +++ b/terminal.go @@ -65,6 +65,12 @@ func (t *Terminal) Write(b []byte) (int, error) { 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 { left int top int