mirror of https://github.com/chzyer/readline.git
operation: fix SetConfig races (#131)
This commit is contained in:
parent
9cc74fe5ad
commit
6a4bc7b4fe
49
operation.go
49
operation.go
|
@ -3,6 +3,7 @@ package readline
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -18,6 +19,7 @@ func (*InterruptError) Error() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Operation struct {
|
type Operation struct {
|
||||||
|
m sync.Mutex
|
||||||
cfg *Config
|
cfg *Config
|
||||||
t *Terminal
|
t *Terminal
|
||||||
buf *RuneBuffer
|
buf *RuneBuffer
|
||||||
|
@ -95,14 +97,21 @@ func (o *Operation) SetMaskRune(r rune) {
|
||||||
o.buf.SetMask(r)
|
o.buf.SetMask(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *Operation) GetConfig() *Config {
|
||||||
|
o.m.Lock()
|
||||||
|
cfg := *o.cfg
|
||||||
|
o.m.Unlock()
|
||||||
|
return &cfg
|
||||||
|
}
|
||||||
|
|
||||||
func (o *Operation) ioloop() {
|
func (o *Operation) ioloop() {
|
||||||
for {
|
for {
|
||||||
keepInSearchMode := false
|
keepInSearchMode := false
|
||||||
keepInCompleteMode := false
|
keepInCompleteMode := false
|
||||||
r := o.t.ReadRune()
|
r := o.t.ReadRune()
|
||||||
if o.cfg.FuncFilterInputRune != nil {
|
if o.GetConfig().FuncFilterInputRune != nil {
|
||||||
var process bool
|
var process bool
|
||||||
r, process = o.cfg.FuncFilterInputRune(r)
|
r, process = o.GetConfig().FuncFilterInputRune(r)
|
||||||
if !process {
|
if !process {
|
||||||
o.buf.Refresh(nil) // to refresh the line
|
o.buf.Refresh(nil) // to refresh the line
|
||||||
continue // ignore this rune
|
continue // ignore this rune
|
||||||
|
@ -162,7 +171,7 @@ func (o *Operation) ioloop() {
|
||||||
o.buf.Refresh(nil)
|
o.buf.Refresh(nil)
|
||||||
}
|
}
|
||||||
case CharTab:
|
case CharTab:
|
||||||
if o.cfg.AutoComplete == nil {
|
if o.GetConfig().AutoComplete == nil {
|
||||||
o.t.Bell()
|
o.t.Bell()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -234,7 +243,7 @@ func (o *Operation) ioloop() {
|
||||||
}
|
}
|
||||||
o.buf.MoveToLineEnd()
|
o.buf.MoveToLineEnd()
|
||||||
var data []rune
|
var data []rune
|
||||||
if !o.cfg.UniqueEditLine {
|
if !o.GetConfig().UniqueEditLine {
|
||||||
o.buf.WriteRune('\n')
|
o.buf.WriteRune('\n')
|
||||||
data = o.buf.Reset()
|
data = o.buf.Reset()
|
||||||
data = data[:len(data)-1] // trim \n
|
data = data[:len(data)-1] // trim \n
|
||||||
|
@ -243,7 +252,7 @@ func (o *Operation) ioloop() {
|
||||||
data = o.buf.Reset()
|
data = o.buf.Reset()
|
||||||
}
|
}
|
||||||
o.outchan <- data
|
o.outchan <- data
|
||||||
if !o.cfg.DisableAutoSaveHistory {
|
if !o.GetConfig().DisableAutoSaveHistory {
|
||||||
// ignore IO error
|
// ignore IO error
|
||||||
_ = o.history.New(data)
|
_ = o.history.New(data)
|
||||||
} else {
|
} else {
|
||||||
|
@ -277,14 +286,14 @@ func (o *Operation) ioloop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// treat as EOF
|
// treat as EOF
|
||||||
if !o.cfg.UniqueEditLine {
|
if !o.GetConfig().UniqueEditLine {
|
||||||
o.buf.WriteString(o.cfg.EOFPrompt + "\n")
|
o.buf.WriteString(o.GetConfig().EOFPrompt + "\n")
|
||||||
}
|
}
|
||||||
o.buf.Reset()
|
o.buf.Reset()
|
||||||
isUpdateHistory = false
|
isUpdateHistory = false
|
||||||
o.history.Revert()
|
o.history.Revert()
|
||||||
o.errchan <- io.EOF
|
o.errchan <- io.EOF
|
||||||
if o.cfg.UniqueEditLine {
|
if o.GetConfig().UniqueEditLine {
|
||||||
o.buf.Clean()
|
o.buf.Clean()
|
||||||
}
|
}
|
||||||
case CharInterrupt:
|
case CharInterrupt:
|
||||||
|
@ -301,12 +310,12 @@ func (o *Operation) ioloop() {
|
||||||
}
|
}
|
||||||
o.buf.MoveToLineEnd()
|
o.buf.MoveToLineEnd()
|
||||||
o.buf.Refresh(nil)
|
o.buf.Refresh(nil)
|
||||||
hint := o.cfg.InterruptPrompt + "\n"
|
hint := o.GetConfig().InterruptPrompt + "\n"
|
||||||
if !o.cfg.UniqueEditLine {
|
if !o.GetConfig().UniqueEditLine {
|
||||||
o.buf.WriteString(hint)
|
o.buf.WriteString(hint)
|
||||||
}
|
}
|
||||||
remain := o.buf.Reset()
|
remain := o.buf.Reset()
|
||||||
if !o.cfg.UniqueEditLine {
|
if !o.GetConfig().UniqueEditLine {
|
||||||
remain = remain[:len(remain)-len([]rune(hint))]
|
remain = remain[:len(remain)-len([]rune(hint))]
|
||||||
}
|
}
|
||||||
isUpdateHistory = false
|
isUpdateHistory = false
|
||||||
|
@ -325,13 +334,15 @@ func (o *Operation) ioloop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.cfg.Listener != nil {
|
listener := o.GetConfig().Listener
|
||||||
newLine, newPos, ok := o.cfg.Listener.OnChange(o.buf.Runes(), o.buf.Pos(), r)
|
if listener != nil {
|
||||||
|
newLine, newPos, ok := listener.OnChange(o.buf.Runes(), o.buf.Pos(), r)
|
||||||
if ok {
|
if ok {
|
||||||
o.buf.SetWithIdx(newPos, newLine)
|
o.buf.SetWithIdx(newPos, newLine)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
o.m.Lock()
|
||||||
if !keepInSearchMode && o.IsSearchMode() {
|
if !keepInSearchMode && o.IsSearchMode() {
|
||||||
o.ExitSearchMode(false)
|
o.ExitSearchMode(false)
|
||||||
o.buf.Refresh(nil)
|
o.buf.Refresh(nil)
|
||||||
|
@ -348,15 +359,16 @@ func (o *Operation) ioloop() {
|
||||||
// it will cause null history
|
// it will cause null history
|
||||||
o.history.Update(o.buf.Runes(), false)
|
o.history.Update(o.buf.Runes(), false)
|
||||||
}
|
}
|
||||||
|
o.m.Unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Operation) Stderr() io.Writer {
|
func (o *Operation) Stderr() io.Writer {
|
||||||
return &wrapWriter{target: o.cfg.Stderr, r: o, t: o.t}
|
return &wrapWriter{target: o.GetConfig().Stderr, r: o, t: o.t}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Operation) Stdout() io.Writer {
|
func (o *Operation) Stdout() io.Writer {
|
||||||
return &wrapWriter{target: o.cfg.Stdout, r: o, t: o.t}
|
return &wrapWriter{target: o.GetConfig().Stdout, r: o, t: o.t}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Operation) String() (string, error) {
|
func (o *Operation) String() (string, error) {
|
||||||
|
@ -368,8 +380,9 @@ func (o *Operation) Runes() ([]rune, error) {
|
||||||
o.t.EnterRawMode()
|
o.t.EnterRawMode()
|
||||||
defer o.t.ExitRawMode()
|
defer o.t.ExitRawMode()
|
||||||
|
|
||||||
if o.cfg.Listener != nil {
|
listener := o.GetConfig().Listener
|
||||||
o.cfg.Listener.OnChange(nil, 0, 0)
|
if listener != nil {
|
||||||
|
listener.OnChange(nil, 0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
o.buf.Refresh(nil) // print prompt
|
o.buf.Refresh(nil) // print prompt
|
||||||
|
@ -437,6 +450,8 @@ func (o *Operation) IsNormalMode() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (op *Operation) SetConfig(cfg *Config) (*Config, error) {
|
func (op *Operation) SetConfig(cfg *Config) (*Config, error) {
|
||||||
|
op.m.Lock()
|
||||||
|
defer op.m.Unlock()
|
||||||
if op.cfg == cfg {
|
if op.cfg == cfg {
|
||||||
return op.cfg, nil
|
return op.cfg, nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue