forked from mirror/readline
kickoff ioloop only needs, solved #1
This commit is contained in:
parent
8fcf9da96c
commit
a904b314b8
13
operation.go
13
operation.go
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
59
terminal.go
59
terminal.go
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
8
utils.go
8
utils.go
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue