Add vendor dependencies as part git repo
This commit is contained in:
531
vendor/github.com/chzyer/readline/operation.go
generated
vendored
Normal file
531
vendor/github.com/chzyer/readline/operation.go
generated
vendored
Normal file
@ -0,0 +1,531 @@
|
||||
package readline
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInterrupt = errors.New("Interrupt")
|
||||
)
|
||||
|
||||
type InterruptError struct {
|
||||
Line []rune
|
||||
}
|
||||
|
||||
func (*InterruptError) Error() string {
|
||||
return "Interrupted"
|
||||
}
|
||||
|
||||
type Operation struct {
|
||||
m sync.Mutex
|
||||
cfg *Config
|
||||
t *Terminal
|
||||
buf *RuneBuffer
|
||||
outchan chan []rune
|
||||
errchan chan error
|
||||
w io.Writer
|
||||
|
||||
history *opHistory
|
||||
*opSearch
|
||||
*opCompleter
|
||||
*opPassword
|
||||
*opVim
|
||||
}
|
||||
|
||||
func (o *Operation) SetBuffer(what string) {
|
||||
o.buf.Set([]rune(what))
|
||||
}
|
||||
|
||||
type wrapWriter struct {
|
||||
r *Operation
|
||||
t *Terminal
|
||||
target io.Writer
|
||||
}
|
||||
|
||||
func (w *wrapWriter) Write(b []byte) (int, error) {
|
||||
if !w.t.IsReading() {
|
||||
return w.target.Write(b)
|
||||
}
|
||||
|
||||
var (
|
||||
n int
|
||||
err error
|
||||
)
|
||||
w.r.buf.Refresh(func() {
|
||||
n, err = w.target.Write(b)
|
||||
})
|
||||
|
||||
if w.r.IsSearchMode() {
|
||||
w.r.SearchRefresh(-1)
|
||||
}
|
||||
if w.r.IsInCompleteMode() {
|
||||
w.r.CompleteRefresh()
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func NewOperation(t *Terminal, cfg *Config) *Operation {
|
||||
width := cfg.FuncGetWidth()
|
||||
op := &Operation{
|
||||
t: t,
|
||||
buf: NewRuneBuffer(t, cfg.Prompt, cfg, width),
|
||||
outchan: make(chan []rune),
|
||||
errchan: make(chan error, 1),
|
||||
}
|
||||
op.w = op.buf.w
|
||||
op.SetConfig(cfg)
|
||||
op.opVim = newVimMode(op)
|
||||
op.opCompleter = newOpCompleter(op.buf.w, op, width)
|
||||
op.opPassword = newOpPassword(op)
|
||||
op.cfg.FuncOnWidthChanged(func() {
|
||||
newWidth := cfg.FuncGetWidth()
|
||||
op.opCompleter.OnWidthChange(newWidth)
|
||||
op.opSearch.OnWidthChange(newWidth)
|
||||
op.buf.OnWidthChange(newWidth)
|
||||
})
|
||||
go op.ioloop()
|
||||
return op
|
||||
}
|
||||
|
||||
func (o *Operation) SetPrompt(s string) {
|
||||
o.buf.SetPrompt(s)
|
||||
}
|
||||
|
||||
func (o *Operation) SetMaskRune(r rune) {
|
||||
o.buf.SetMask(r)
|
||||
}
|
||||
|
||||
func (o *Operation) GetConfig() *Config {
|
||||
o.m.Lock()
|
||||
cfg := *o.cfg
|
||||
o.m.Unlock()
|
||||
return &cfg
|
||||
}
|
||||
|
||||
func (o *Operation) ioloop() {
|
||||
for {
|
||||
keepInSearchMode := false
|
||||
keepInCompleteMode := false
|
||||
r := o.t.ReadRune()
|
||||
if o.GetConfig().FuncFilterInputRune != nil {
|
||||
var process bool
|
||||
r, process = o.GetConfig().FuncFilterInputRune(r)
|
||||
if !process {
|
||||
o.buf.Refresh(nil) // to refresh the line
|
||||
continue // ignore this rune
|
||||
}
|
||||
}
|
||||
|
||||
if r == 0 { // io.EOF
|
||||
if o.buf.Len() == 0 {
|
||||
o.buf.Clean()
|
||||
select {
|
||||
case o.errchan <- io.EOF:
|
||||
}
|
||||
break
|
||||
} else {
|
||||
// if stdin got io.EOF and there is something left in buffer,
|
||||
// let's flush them by sending CharEnter.
|
||||
// And we will got io.EOF int next loop.
|
||||
r = CharEnter
|
||||
}
|
||||
}
|
||||
isUpdateHistory := true
|
||||
|
||||
if o.IsInCompleteSelectMode() {
|
||||
keepInCompleteMode = o.HandleCompleteSelect(r)
|
||||
if keepInCompleteMode {
|
||||
continue
|
||||
}
|
||||
|
||||
o.buf.Refresh(nil)
|
||||
switch r {
|
||||
case CharEnter, CharCtrlJ:
|
||||
o.history.Update(o.buf.Runes(), false)
|
||||
fallthrough
|
||||
case CharInterrupt:
|
||||
o.t.KickRead()
|
||||
fallthrough
|
||||
case CharBell:
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if o.IsEnableVimMode() {
|
||||
r = o.HandleVim(r, o.t.ReadRune)
|
||||
if r == 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
switch r {
|
||||
case CharBell:
|
||||
if o.IsSearchMode() {
|
||||
o.ExitSearchMode(true)
|
||||
o.buf.Refresh(nil)
|
||||
}
|
||||
if o.IsInCompleteMode() {
|
||||
o.ExitCompleteMode(true)
|
||||
o.buf.Refresh(nil)
|
||||
}
|
||||
case CharTab:
|
||||
if o.GetConfig().AutoComplete == nil {
|
||||
o.t.Bell()
|
||||
break
|
||||
}
|
||||
if o.OnComplete() {
|
||||
keepInCompleteMode = true
|
||||
} else {
|
||||
o.t.Bell()
|
||||
break
|
||||
}
|
||||
|
||||
case CharBckSearch:
|
||||
if !o.SearchMode(S_DIR_BCK) {
|
||||
o.t.Bell()
|
||||
break
|
||||
}
|
||||
keepInSearchMode = true
|
||||
case CharCtrlU:
|
||||
o.buf.KillFront()
|
||||
case CharFwdSearch:
|
||||
if !o.SearchMode(S_DIR_FWD) {
|
||||
o.t.Bell()
|
||||
break
|
||||
}
|
||||
keepInSearchMode = true
|
||||
case CharKill:
|
||||
o.buf.Kill()
|
||||
keepInCompleteMode = true
|
||||
case MetaForward:
|
||||
o.buf.MoveToNextWord()
|
||||
case CharTranspose:
|
||||
o.buf.Transpose()
|
||||
case MetaBackward:
|
||||
o.buf.MoveToPrevWord()
|
||||
case MetaDelete:
|
||||
o.buf.DeleteWord()
|
||||
case CharLineStart:
|
||||
o.buf.MoveToLineStart()
|
||||
case CharLineEnd:
|
||||
o.buf.MoveToLineEnd()
|
||||
case CharBackspace, CharCtrlH:
|
||||
if o.IsSearchMode() {
|
||||
o.SearchBackspace()
|
||||
keepInSearchMode = true
|
||||
break
|
||||
}
|
||||
|
||||
if o.buf.Len() == 0 {
|
||||
o.t.Bell()
|
||||
break
|
||||
}
|
||||
o.buf.Backspace()
|
||||
if o.IsInCompleteMode() {
|
||||
o.OnComplete()
|
||||
}
|
||||
case CharCtrlZ:
|
||||
o.buf.Clean()
|
||||
o.t.SleepToResume()
|
||||
o.Refresh()
|
||||
case CharCtrlL:
|
||||
ClearScreen(o.w)
|
||||
o.Refresh()
|
||||
case MetaBackspace, CharCtrlW:
|
||||
o.buf.BackEscapeWord()
|
||||
case CharCtrlY:
|
||||
o.buf.Yank()
|
||||
case CharEnter, CharCtrlJ:
|
||||
if o.IsSearchMode() {
|
||||
o.ExitSearchMode(false)
|
||||
}
|
||||
o.buf.MoveToLineEnd()
|
||||
var data []rune
|
||||
if !o.GetConfig().UniqueEditLine {
|
||||
o.buf.WriteRune('\n')
|
||||
data = o.buf.Reset()
|
||||
data = data[:len(data)-1] // trim \n
|
||||
} else {
|
||||
o.buf.Clean()
|
||||
data = o.buf.Reset()
|
||||
}
|
||||
o.outchan <- data
|
||||
if !o.GetConfig().DisableAutoSaveHistory {
|
||||
// ignore IO error
|
||||
_ = o.history.New(data)
|
||||
} else {
|
||||
isUpdateHistory = false
|
||||
}
|
||||
case CharBackward:
|
||||
o.buf.MoveBackward()
|
||||
case CharForward:
|
||||
o.buf.MoveForward()
|
||||
case CharPrev:
|
||||
buf := o.history.Prev()
|
||||
if buf != nil {
|
||||
o.buf.Set(buf)
|
||||
} else {
|
||||
o.t.Bell()
|
||||
}
|
||||
case CharNext:
|
||||
buf, ok := o.history.Next()
|
||||
if ok {
|
||||
o.buf.Set(buf)
|
||||
} else {
|
||||
o.t.Bell()
|
||||
}
|
||||
case CharDelete:
|
||||
if o.buf.Len() > 0 || !o.IsNormalMode() {
|
||||
o.t.KickRead()
|
||||
if !o.buf.Delete() {
|
||||
o.t.Bell()
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// treat as EOF
|
||||
if !o.GetConfig().UniqueEditLine {
|
||||
o.buf.WriteString(o.GetConfig().EOFPrompt + "\n")
|
||||
}
|
||||
o.buf.Reset()
|
||||
isUpdateHistory = false
|
||||
o.history.Revert()
|
||||
o.errchan <- io.EOF
|
||||
if o.GetConfig().UniqueEditLine {
|
||||
o.buf.Clean()
|
||||
}
|
||||
case CharInterrupt:
|
||||
if o.IsSearchMode() {
|
||||
o.t.KickRead()
|
||||
o.ExitSearchMode(true)
|
||||
break
|
||||
}
|
||||
if o.IsInCompleteMode() {
|
||||
o.t.KickRead()
|
||||
o.ExitCompleteMode(true)
|
||||
o.buf.Refresh(nil)
|
||||
break
|
||||
}
|
||||
o.buf.MoveToLineEnd()
|
||||
o.buf.Refresh(nil)
|
||||
hint := o.GetConfig().InterruptPrompt + "\n"
|
||||
if !o.GetConfig().UniqueEditLine {
|
||||
o.buf.WriteString(hint)
|
||||
}
|
||||
remain := o.buf.Reset()
|
||||
if !o.GetConfig().UniqueEditLine {
|
||||
remain = remain[:len(remain)-len([]rune(hint))]
|
||||
}
|
||||
isUpdateHistory = false
|
||||
o.history.Revert()
|
||||
o.errchan <- &InterruptError{remain}
|
||||
default:
|
||||
if o.IsSearchMode() {
|
||||
o.SearchChar(r)
|
||||
keepInSearchMode = true
|
||||
break
|
||||
}
|
||||
o.buf.WriteRune(r)
|
||||
if o.IsInCompleteMode() {
|
||||
o.OnComplete()
|
||||
keepInCompleteMode = true
|
||||
}
|
||||
}
|
||||
|
||||
listener := o.GetConfig().Listener
|
||||
if listener != nil {
|
||||
newLine, newPos, ok := listener.OnChange(o.buf.Runes(), o.buf.Pos(), r)
|
||||
if ok {
|
||||
o.buf.SetWithIdx(newPos, newLine)
|
||||
}
|
||||
}
|
||||
|
||||
o.m.Lock()
|
||||
if !keepInSearchMode && o.IsSearchMode() {
|
||||
o.ExitSearchMode(false)
|
||||
o.buf.Refresh(nil)
|
||||
} else if o.IsInCompleteMode() {
|
||||
if !keepInCompleteMode {
|
||||
o.ExitCompleteMode(false)
|
||||
o.Refresh()
|
||||
} else {
|
||||
o.buf.Refresh(nil)
|
||||
o.CompleteRefresh()
|
||||
}
|
||||
}
|
||||
if isUpdateHistory && !o.IsSearchMode() {
|
||||
// it will cause null history
|
||||
o.history.Update(o.buf.Runes(), false)
|
||||
}
|
||||
o.m.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Operation) Stderr() io.Writer {
|
||||
return &wrapWriter{target: o.GetConfig().Stderr, r: o, t: o.t}
|
||||
}
|
||||
|
||||
func (o *Operation) Stdout() io.Writer {
|
||||
return &wrapWriter{target: o.GetConfig().Stdout, r: o, t: o.t}
|
||||
}
|
||||
|
||||
func (o *Operation) String() (string, error) {
|
||||
r, err := o.Runes()
|
||||
return string(r), err
|
||||
}
|
||||
|
||||
func (o *Operation) Runes() ([]rune, error) {
|
||||
o.t.EnterRawMode()
|
||||
defer o.t.ExitRawMode()
|
||||
|
||||
listener := o.GetConfig().Listener
|
||||
if listener != nil {
|
||||
listener.OnChange(nil, 0, 0)
|
||||
}
|
||||
|
||||
o.buf.Refresh(nil) // print prompt
|
||||
o.t.KickRead()
|
||||
select {
|
||||
case r := <-o.outchan:
|
||||
return r, nil
|
||||
case err := <-o.errchan:
|
||||
if e, ok := err.(*InterruptError); ok {
|
||||
return e.Line, ErrInterrupt
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Operation) PasswordEx(prompt string, l Listener) ([]byte, error) {
|
||||
cfg := o.GenPasswordConfig()
|
||||
cfg.Prompt = prompt
|
||||
cfg.Listener = l
|
||||
return o.PasswordWithConfig(cfg)
|
||||
}
|
||||
|
||||
func (o *Operation) GenPasswordConfig() *Config {
|
||||
return o.opPassword.PasswordConfig()
|
||||
}
|
||||
|
||||
func (o *Operation) PasswordWithConfig(cfg *Config) ([]byte, error) {
|
||||
if err := o.opPassword.EnterPasswordMode(cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer o.opPassword.ExitPasswordMode()
|
||||
return o.Slice()
|
||||
}
|
||||
|
||||
func (o *Operation) Password(prompt string) ([]byte, error) {
|
||||
return o.PasswordEx(prompt, nil)
|
||||
}
|
||||
|
||||
func (o *Operation) SetTitle(t string) {
|
||||
o.w.Write([]byte("\033[2;" + t + "\007"))
|
||||
}
|
||||
|
||||
func (o *Operation) Slice() ([]byte, error) {
|
||||
r, err := o.Runes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []byte(string(r)), nil
|
||||
}
|
||||
|
||||
func (o *Operation) Close() {
|
||||
o.history.Close()
|
||||
}
|
||||
|
||||
func (o *Operation) SetHistoryPath(path string) {
|
||||
if o.history != nil {
|
||||
o.history.Close()
|
||||
}
|
||||
o.cfg.HistoryFile = path
|
||||
o.history = newOpHistory(o.cfg)
|
||||
}
|
||||
|
||||
func (o *Operation) IsNormalMode() bool {
|
||||
return !o.IsInCompleteMode() && !o.IsSearchMode()
|
||||
}
|
||||
|
||||
func (op *Operation) SetConfig(cfg *Config) (*Config, error) {
|
||||
op.m.Lock()
|
||||
defer op.m.Unlock()
|
||||
if op.cfg == cfg {
|
||||
return op.cfg, nil
|
||||
}
|
||||
if err := cfg.Init(); err != nil {
|
||||
return op.cfg, err
|
||||
}
|
||||
old := op.cfg
|
||||
op.cfg = cfg
|
||||
op.SetPrompt(cfg.Prompt)
|
||||
op.SetMaskRune(cfg.MaskRune)
|
||||
op.buf.SetConfig(cfg)
|
||||
width := op.cfg.FuncGetWidth()
|
||||
|
||||
if cfg.opHistory == nil {
|
||||
op.SetHistoryPath(cfg.HistoryFile)
|
||||
cfg.opHistory = op.history
|
||||
cfg.opSearch = newOpSearch(op.buf.w, op.buf, op.history, cfg, width)
|
||||
}
|
||||
op.history = cfg.opHistory
|
||||
|
||||
// SetHistoryPath will close opHistory which already exists
|
||||
// so if we use it next time, we need to reopen it by `InitHistory()`
|
||||
op.history.Init()
|
||||
|
||||
if op.cfg.AutoComplete != nil {
|
||||
op.opCompleter = newOpCompleter(op.buf.w, op, width)
|
||||
}
|
||||
|
||||
op.opSearch = cfg.opSearch
|
||||
return old, nil
|
||||
}
|
||||
|
||||
func (o *Operation) ResetHistory() {
|
||||
o.history.Reset()
|
||||
}
|
||||
|
||||
// if err is not nil, it just mean it fail to write to file
|
||||
// other things goes fine.
|
||||
func (o *Operation) SaveHistory(content string) error {
|
||||
return o.history.New([]rune(content))
|
||||
}
|
||||
|
||||
func (o *Operation) Refresh() {
|
||||
if o.t.IsReading() {
|
||||
o.buf.Refresh(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Operation) Clean() {
|
||||
o.buf.Clean()
|
||||
}
|
||||
|
||||
func FuncListener(f func(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool)) Listener {
|
||||
return &DumpListener{f: f}
|
||||
}
|
||||
|
||||
type DumpListener struct {
|
||||
f func(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool)
|
||||
}
|
||||
|
||||
func (d *DumpListener) OnChange(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool) {
|
||||
return d.f(line, pos, key)
|
||||
}
|
||||
|
||||
type Listener interface {
|
||||
OnChange(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool)
|
||||
}
|
||||
|
||||
type Painter interface {
|
||||
Paint(line []rune, pos int) []rune
|
||||
}
|
||||
|
||||
type defaultPainter struct{}
|
||||
|
||||
func (p *defaultPainter) Paint(line []rune, _ int) []rune {
|
||||
return line
|
||||
}
|
Reference in New Issue
Block a user