Path: blob/main/vendor/github.com/chzyer/readline/terminal.go
2875 views
package readline12import (3"bufio"4"fmt"5"io"6"strings"7"sync"8"sync/atomic"9)1011type Terminal struct {12m sync.Mutex13cfg *Config14outchan chan rune15closed int3216stopChan chan struct{}17kickChan chan struct{}18wg sync.WaitGroup19isReading int3220sleeping int322122sizeChan chan string23}2425func NewTerminal(cfg *Config) (*Terminal, error) {26if err := cfg.Init(); err != nil {27return nil, err28}29t := &Terminal{30cfg: cfg,31kickChan: make(chan struct{}, 1),32outchan: make(chan rune),33stopChan: make(chan struct{}, 1),34sizeChan: make(chan string, 1),35}3637go t.ioloop()38return t, nil39}4041// SleepToResume will sleep myself, and return only if I'm resumed.42func (t *Terminal) SleepToResume() {43if !atomic.CompareAndSwapInt32(&t.sleeping, 0, 1) {44return45}46defer atomic.StoreInt32(&t.sleeping, 0)4748t.ExitRawMode()49ch := WaitForResume()50SuspendMe()51<-ch52t.EnterRawMode()53}5455func (t *Terminal) EnterRawMode() (err error) {56return t.cfg.FuncMakeRaw()57}5859func (t *Terminal) ExitRawMode() (err error) {60return t.cfg.FuncExitRaw()61}6263func (t *Terminal) Write(b []byte) (int, error) {64return t.cfg.Stdout.Write(b)65}6667// WriteStdin prefill the next Stdin fetch68// Next time you call ReadLine() this value will be writen before the user input69func (t *Terminal) WriteStdin(b []byte) (int, error) {70return t.cfg.StdinWriter.Write(b)71}7273type termSize struct {74left int75top int76}7778func (t *Terminal) GetOffset(f func(offset string)) {79go func() {80f(<-t.sizeChan)81}()82t.Write([]byte("\033[6n"))83}8485func (t *Terminal) Print(s string) {86fmt.Fprintf(t.cfg.Stdout, "%s", s)87}8889func (t *Terminal) PrintRune(r rune) {90fmt.Fprintf(t.cfg.Stdout, "%c", r)91}9293func (t *Terminal) Readline() *Operation {94return NewOperation(t, t.cfg)95}9697// return rune(0) if meet EOF98func (t *Terminal) ReadRune() rune {99ch, ok := <-t.outchan100if !ok {101return rune(0)102}103return ch104}105106func (t *Terminal) IsReading() bool {107return atomic.LoadInt32(&t.isReading) == 1108}109110func (t *Terminal) KickRead() {111select {112case t.kickChan <- struct{}{}:113default:114}115}116117func (t *Terminal) ioloop() {118t.wg.Add(1)119defer func() {120t.wg.Done()121close(t.outchan)122}()123124var (125isEscape bool126isEscapeEx bool127isEscapeSS3 bool128expectNextChar bool129)130131buf := bufio.NewReader(t.getStdin())132for {133if !expectNextChar {134atomic.StoreInt32(&t.isReading, 0)135select {136case <-t.kickChan:137atomic.StoreInt32(&t.isReading, 1)138case <-t.stopChan:139return140}141}142expectNextChar = false143r, _, err := buf.ReadRune()144if err != nil {145if strings.Contains(err.Error(), "interrupted system call") {146expectNextChar = true147continue148}149break150}151152if isEscape {153isEscape = false154if r == CharEscapeEx {155// ^][156expectNextChar = true157isEscapeEx = true158continue159} else if r == CharO {160// ^]O161expectNextChar = true162isEscapeSS3 = true163continue164}165r = escapeKey(r, buf)166} else if isEscapeEx {167isEscapeEx = false168if key := readEscKey(r, buf); key != nil {169r = escapeExKey(key)170// offset171if key.typ == 'R' {172if _, _, ok := key.Get2(); ok {173select {174case t.sizeChan <- key.attr:175default:176}177}178expectNextChar = true179continue180}181}182if r == 0 {183expectNextChar = true184continue185}186} else if isEscapeSS3 {187isEscapeSS3 = false188if key := readEscKey(r, buf); key != nil {189r = escapeSS3Key(key)190}191if r == 0 {192expectNextChar = true193continue194}195}196197expectNextChar = true198switch r {199case CharEsc:200if t.cfg.VimMode {201t.outchan <- r202break203}204isEscape = true205case CharInterrupt, CharEnter, CharCtrlJ, CharDelete:206expectNextChar = false207fallthrough208default:209t.outchan <- r210}211}212213}214215func (t *Terminal) Bell() {216fmt.Fprintf(t, "%c", CharBell)217}218219func (t *Terminal) Close() error {220if atomic.SwapInt32(&t.closed, 1) != 0 {221return nil222}223if closer, ok := t.cfg.Stdin.(io.Closer); ok {224closer.Close()225}226close(t.stopChan)227t.wg.Wait()228return t.ExitRawMode()229}230231func (t *Terminal) GetConfig() *Config {232t.m.Lock()233cfg := *t.cfg234t.m.Unlock()235return &cfg236}237238func (t *Terminal) getStdin() io.Reader {239t.m.Lock()240r := t.cfg.Stdin241t.m.Unlock()242return r243}244245func (t *Terminal) SetConfig(c *Config) error {246if err := c.Init(); err != nil {247return err248}249t.m.Lock()250t.cfg = c251t.m.Unlock()252return nil253}254255256