kickoff ioloop only needs, solved #1

This commit is contained in:
Cheney 2015-09-25 00:16:49 +08:00
parent 8fcf9da96c
commit a904b314b8
4 changed files with 72 additions and 12 deletions

View File

@ -10,12 +10,14 @@ type Operation struct {
t *Terminal t *Terminal
buf *RuneBuffer buf *RuneBuffer
outchan chan []rune outchan chan []rune
*opHistory *opHistory
*opSearch *opSearch
} }
type wrapWriter struct { type wrapWriter struct {
r *Operation r *Operation
t *Terminal
target io.Writer target io.Writer
} }
@ -23,7 +25,9 @@ func (w *wrapWriter) Write(b []byte) (int, error) {
buf := w.r.buf buf := w.r.buf
buf.Clean() buf.Clean()
n, err := w.target.Write(b) n, err := w.target.Write(b)
w.r.buf.Refresh() if w.t.IsReading() {
w.r.buf.Refresh()
}
if w.r.IsSearchMode() { if w.r.IsSearchMode() {
w.r.SearchRefresh(-1) w.r.SearchRefresh(-1)
} }
@ -135,7 +139,11 @@ func (o *Operation) ioloop() {
} }
func (o *Operation) Stderr() io.Writer { func (o *Operation) Stderr() io.Writer {
return &wrapWriter{target: os.Stderr, r: o} return &wrapWriter{target: os.Stderr, r: o, t: o.t}
}
func (o *Operation) Stdout() io.Writer {
return &wrapWriter{target: os.Stdout, r: o, t: o.t}
} }
func (o *Operation) String() (string, error) { func (o *Operation) String() (string, error) {
@ -148,6 +156,7 @@ func (o *Operation) String() (string, error) {
func (o *Operation) Runes() ([]rune, error) { func (o *Operation) Runes() ([]rune, error) {
o.buf.Refresh() // print prompt o.buf.Refresh() // print prompt
o.t.KickRead()
r := <-o.outchan r := <-o.outchan
if r == nil { if r == nil {
return nil, io.EOF return nil, io.EOF

View File

@ -28,6 +28,10 @@ func New(prompt string) (*Instance, error) {
return NewEx(&Config{Prompt: prompt}) return NewEx(&Config{Prompt: prompt})
} }
func (i *Instance) Stdout() io.Writer {
return i.o.Stdout()
}
func (i *Instance) Stderr() io.Writer { func (i *Instance) Stderr() io.Writer {
return i.o.Stderr() return i.o.Stderr()
} }

View File

@ -4,6 +4,7 @@ import (
"bufio" "bufio"
"fmt" "fmt"
"os" "os"
"sync"
"sync/atomic" "sync/atomic"
"syscall" "syscall"
@ -11,10 +12,14 @@ import (
) )
type Terminal struct { type Terminal struct {
cfg *Config cfg *Config
state *terminal.State state *terminal.State
outchan chan rune outchan chan rune
closed int64 closed int64
stopChan chan struct{}
kickChan chan struct{}
wg sync.WaitGroup
isReading bool
} }
func NewTerminal(cfg *Config) (*Terminal, error) { func NewTerminal(cfg *Config) (*Terminal, error) {
@ -23,9 +28,11 @@ func NewTerminal(cfg *Config) (*Terminal, error) {
return nil, err return nil, err
} }
t := &Terminal{ t := &Terminal{
cfg: cfg, cfg: cfg,
state: state, state: state,
outchan: make(chan rune), kickChan: make(chan struct{}, 1),
outchan: make(chan rune),
stopChan: make(chan struct{}, 1),
} }
go t.ioloop() go t.ioloop()
@ -52,11 +59,38 @@ func (t *Terminal) ReadRune() rune {
return <-t.outchan return <-t.outchan
} }
func (t *Terminal) IsReading() bool {
return t.isReading
}
func (t *Terminal) KickRead() {
select {
case t.kickChan <- struct{}{}:
default:
}
}
func (t *Terminal) ioloop() { func (t *Terminal) ioloop() {
t.wg.Add(1)
defer t.wg.Done()
var (
isEscape bool
isEscapeEx bool
expectNextChar bool
)
buf := bufio.NewReader(os.Stdin) buf := bufio.NewReader(os.Stdin)
isEscape := false
isEscapeEx := false
for { for {
if !expectNextChar {
t.isReading = false
select {
case <-t.kickChan:
t.isReading = true
case <-t.stopChan:
return
}
}
expectNextChar = false
r, _, err := buf.ReadRune() r, _, err := buf.ReadRune()
if err != nil { if err != nil {
break break
@ -65,6 +99,7 @@ func (t *Terminal) ioloop() {
if isEscape { if isEscape {
isEscape = false isEscape = false
if r == CharEscapeEx { if r == CharEscapeEx {
expectNextChar = true
isEscapeEx = true isEscapeEx = true
continue continue
} }
@ -80,7 +115,11 @@ func (t *Terminal) ioloop() {
goto exit goto exit
case CharEsc: case CharEsc:
isEscape = true isEscape = true
expectNextChar = true
case CharEnter, CharCtrlJ:
t.outchan <- r
default: default:
expectNextChar = true
t.outchan <- r t.outchan <- r
} }
} }
@ -91,5 +130,7 @@ func (t *Terminal) Close() error {
if atomic.SwapInt64(&t.closed, 1) != 0 { if atomic.SwapInt64(&t.closed, 1) != 0 {
return nil return nil
} }
t.stopChan <- struct{}{}
t.wg.Wait()
return Restore(syscall.Stdin, t.state) return Restore(syscall.Stdin, t.state)
} }

View File

@ -22,7 +22,13 @@ func MakeRaw(fd int) (*terminal.State, error) {
} }
func Restore(fd int, state *terminal.State) error { func Restore(fd int, state *terminal.State) error {
return terminal.Restore(fd, state) err := terminal.Restore(fd, state)
if err != nil {
if err.Error() == "errno 0" {
err = nil
}
}
return nil
} }
func IsPrintable(key rune) bool { func IsPrintable(key rune) bool {