Path: blob/main/vendor/github.com/chzyer/readline/std.go
2875 views
package readline12import (3"io"4"os"5"sync"6"sync/atomic"7)89var (10Stdin io.ReadCloser = os.Stdin11Stdout io.WriteCloser = os.Stdout12Stderr io.WriteCloser = os.Stderr13)1415var (16std *Instance17stdOnce sync.Once18)1920// global instance will not submit history automatic21func getInstance() *Instance {22stdOnce.Do(func() {23std, _ = NewEx(&Config{24DisableAutoSaveHistory: true,25})26})27return std28}2930// let readline load history from filepath31// and try to persist history into disk32// set fp to "" to prevent readline persisting history to disk33// so the `AddHistory` will return nil error forever.34func SetHistoryPath(fp string) {35ins := getInstance()36cfg := ins.Config.Clone()37cfg.HistoryFile = fp38ins.SetConfig(cfg)39}4041// set auto completer to global instance42func SetAutoComplete(completer AutoCompleter) {43ins := getInstance()44cfg := ins.Config.Clone()45cfg.AutoComplete = completer46ins.SetConfig(cfg)47}4849// add history to global instance manually50// raise error only if `SetHistoryPath` is set with a non-empty path51func AddHistory(content string) error {52ins := getInstance()53return ins.SaveHistory(content)54}5556func Password(prompt string) ([]byte, error) {57ins := getInstance()58return ins.ReadPassword(prompt)59}6061// readline with global configs62func Line(prompt string) (string, error) {63ins := getInstance()64ins.SetPrompt(prompt)65return ins.Readline()66}6768type CancelableStdin struct {69r io.Reader70mutex sync.Mutex71stop chan struct{}72closed int3273notify chan struct{}74data []byte75read int76err error77}7879func NewCancelableStdin(r io.Reader) *CancelableStdin {80c := &CancelableStdin{81r: r,82notify: make(chan struct{}),83stop: make(chan struct{}),84}85go c.ioloop()86return c87}8889func (c *CancelableStdin) ioloop() {90loop:91for {92select {93case <-c.notify:94c.read, c.err = c.r.Read(c.data)95select {96case c.notify <- struct{}{}:97case <-c.stop:98break loop99}100case <-c.stop:101break loop102}103}104}105106func (c *CancelableStdin) Read(b []byte) (n int, err error) {107c.mutex.Lock()108defer c.mutex.Unlock()109if atomic.LoadInt32(&c.closed) == 1 {110return 0, io.EOF111}112113c.data = b114select {115case c.notify <- struct{}{}:116case <-c.stop:117return 0, io.EOF118}119select {120case <-c.notify:121return c.read, c.err122case <-c.stop:123return 0, io.EOF124}125}126127func (c *CancelableStdin) Close() error {128if atomic.CompareAndSwapInt32(&c.closed, 0, 1) {129close(c.stop)130}131return nil132}133134// FillableStdin is a stdin reader which can prepend some data before135// reading into the real stdin136type FillableStdin struct {137sync.Mutex138stdin io.Reader139stdinBuffer io.ReadCloser140buf []byte141bufErr error142}143144// NewFillableStdin gives you FillableStdin145func NewFillableStdin(stdin io.Reader) (io.ReadCloser, io.Writer) {146r, w := io.Pipe()147s := &FillableStdin{148stdinBuffer: r,149stdin: stdin,150}151s.ioloop()152return s, w153}154155func (s *FillableStdin) ioloop() {156go func() {157for {158bufR := make([]byte, 100)159var n int160n, s.bufErr = s.stdinBuffer.Read(bufR)161if s.bufErr != nil {162if s.bufErr == io.ErrClosedPipe {163break164}165}166s.Lock()167s.buf = append(s.buf, bufR[:n]...)168s.Unlock()169}170}()171}172173// Read will read from the local buffer and if no data, read from stdin174func (s *FillableStdin) Read(p []byte) (n int, err error) {175s.Lock()176i := len(s.buf)177if len(p) < i {178i = len(p)179}180if i > 0 {181n := copy(p, s.buf)182s.buf = s.buf[:0]183cerr := s.bufErr184s.bufErr = nil185s.Unlock()186return n, cerr187}188s.Unlock()189n, err = s.stdin.Read(p)190return n, err191}192193func (s *FillableStdin) Close() error {194s.stdinBuffer.Close()195return nil196}197198199