diff --git a/go.mod b/go.mod index 232b52697..7fc130592 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( github.com/blang/semver v3.5.1+incompatible github.com/boltdb/bolt v1.3.1 // indirect github.com/chzyer/logex v1.1.10 // indirect - github.com/chzyer/readline v0.0.0-20161106042343-c914be64f07d + github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 // indirect github.com/coreos/bbolt v1.3.0 // indirect github.com/coreos/etcd v3.3.10+incompatible diff --git a/go.sum b/go.sum index 99873711a..b02b3bfc8 100644 --- a/go.sum +++ b/go.sum @@ -81,8 +81,8 @@ github.com/bsm/go-vlq v0.0.0-20150828105119-ec6e8d4f5f4e/go.mod h1:N+BjUcTjSxc2m github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20161106042343-c914be64f07d h1:aG5FcWiZTOhPQzYIxwxSR1zEOxzL32fwr1CsaCfhO6w= -github.com/chzyer/readline v0.0.0-20161106042343-c914be64f07d/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= diff --git a/vendor/github.com/chzyer/readline/.gitignore b/vendor/github.com/chzyer/readline/.gitignore new file mode 100644 index 000000000..a3062beae --- /dev/null +++ b/vendor/github.com/chzyer/readline/.gitignore @@ -0,0 +1 @@ +.vscode/* diff --git a/vendor/github.com/chzyer/readline/.travis.yml b/vendor/github.com/chzyer/readline/.travis.yml index 014a722ac..9c3595543 100644 --- a/vendor/github.com/chzyer/readline/.travis.yml +++ b/vendor/github.com/chzyer/readline/.travis.yml @@ -1,7 +1,6 @@ language: go go: - - 1.5 - - 1.7 + - 1.x script: - GOOS=windows go install github.com/chzyer/readline/example/... - GOOS=linux go install github.com/chzyer/readline/example/... diff --git a/vendor/github.com/chzyer/readline/CHANGELOG.md b/vendor/github.com/chzyer/readline/CHANGELOG.md index 5c1811a7f..14ff5be13 100644 --- a/vendor/github.com/chzyer/readline/CHANGELOG.md +++ b/vendor/github.com/chzyer/readline/CHANGELOG.md @@ -11,7 +11,7 @@ * [#38][38] add SetChildren for prefix completer interface * [#42][42] improve multiple lines compatibility -* [#43][43] remove sub-package(runes) for gopkg compatiblity +* [#43][43] remove sub-package(runes) for gopkg compatibility * [#46][46] Auto complete with space prefixed line * [#48][48] support suspend process (ctrl+Z) * [#49][49] fix bug that check equals with previous command diff --git a/vendor/github.com/chzyer/readline/README.md b/vendor/github.com/chzyer/readline/README.md index 2eabc20a1..fab974b7f 100644 --- a/vendor/github.com/chzyer/readline/README.md +++ b/vendor/github.com/chzyer/readline/README.md @@ -11,7 +11,7 @@

-A powerful readline library in `Linux` `macOS` `Windows` +A powerful readline library in `Linux` `macOS` `Windows` `Solaris` ## Guide @@ -19,24 +19,25 @@ A powerful readline library in `Linux` `macOS` `Windows` * [Shortcut](doc/shortcut.md) ## Repos using readline - + [![cockroachdb](https://img.shields.io/github/stars/cockroachdb/cockroach.svg?label=cockroachdb/cockroach)](https://github.com/cockroachdb/cockroach) +[![robertkrimen/otto](https://img.shields.io/github/stars/robertkrimen/otto.svg?label=robertkrimen/otto)](https://github.com/robertkrimen/otto) [![empire](https://img.shields.io/github/stars/remind101/empire.svg?label=remind101/empire)](https://github.com/remind101/empire) +[![mehrdadrad/mylg](https://img.shields.io/github/stars/mehrdadrad/mylg.svg?label=mehrdadrad/mylg)](https://github.com/mehrdadrad/mylg) +[![knq/usql](https://img.shields.io/github/stars/knq/usql.svg?label=knq/usql)](https://github.com/knq/usql) [![youtube/doorman](https://img.shields.io/github/stars/youtube/doorman.svg?label=youtube/doorman)](https://github.com/youtube/doorman) [![bom-d-van/harp](https://img.shields.io/github/stars/bom-d-van/harp.svg?label=bom-d-van/harp)](https://github.com/bom-d-van/harp) [![abiosoft/ishell](https://img.shields.io/github/stars/abiosoft/ishell.svg?label=abiosoft/ishell)](https://github.com/abiosoft/ishell) -[![robertkrimen/otto](https://img.shields.io/github/stars/robertkrimen/otto.svg?label=robertkrimen/otto)](https://github.com/robertkrimen/otto) [![Netflix/hal-9001](https://img.shields.io/github/stars/Netflix/hal-9001.svg?label=Netflix/hal-9001)](https://github.com/Netflix/hal-9001) [![docker/go-p9p](https://img.shields.io/github/stars/docker/go-p9p.svg?label=docker/go-p9p)](https://github.com/docker/go-p9p) -[![mehrdadrad/mylg](https://img.shields.io/github/stars/mehrdadrad/mylg.svg?label=mehrdadrad/mylg)](https://github.com/mehrdadrad/mylg) ## Feedback If you have any questions, please submit a github issue and any pull requests is welcomed :) -* [https://twitter.com/chzyer](https://twitter.com/chzyer) -* [http://weibo.com/2145262190](http://weibo.com/2145262190) +* [https://twitter.com/chzyer](https://twitter.com/chzyer) +* [http://weibo.com/2145262190](http://weibo.com/2145262190) ## Backers diff --git a/vendor/github.com/chzyer/readline/ansi_windows.go b/vendor/github.com/chzyer/readline/ansi_windows.go index da106b576..63b908c18 100644 --- a/vendor/github.com/chzyer/readline/ansi_windows.go +++ b/vendor/github.com/chzyer/readline/ansi_windows.go @@ -25,6 +25,7 @@ const ( COLOR_BINTENSITY = 0x0080 COMMON_LVB_UNDERSCORE = 0x8000 + COMMON_LVB_BOLD = 0x0007 ) var ColorTableFg = []word{ @@ -163,6 +164,8 @@ func (a *ANSIWriterCtx) ioloopEscSeq(w *bufio.Writer, r rune, argptr *[]string) color |= ColorTableBg[c-40] } else if c == 4 { color |= COMMON_LVB_UNDERSCORE | ColorTableFg[7] + } else if c == 1 { + color |= COMMON_LVB_BOLD | COLOR_FINTENSITY } else { // unknown code treat as reset color = ColorTableFg[7] } diff --git a/vendor/github.com/chzyer/readline/complete.go b/vendor/github.com/chzyer/readline/complete.go index 349fc3d24..c08c99414 100644 --- a/vendor/github.com/chzyer/readline/complete.go +++ b/vendor/github.com/chzyer/readline/complete.go @@ -203,7 +203,9 @@ func (o *opCompleter) CompleteRefresh() { // -1 to avoid reach the end of line width := o.width - 1 colNum := width / colWidth - colWidth += (width - (colWidth * colNum)) / colNum + if colNum != 0 { + colWidth += (width - (colWidth * colNum)) / colNum + } o.candidateColNum = colNum buf := bufio.NewWriter(o.w) @@ -219,7 +221,7 @@ func (o *opCompleter) CompleteRefresh() { } buf.WriteString(string(same)) buf.WriteString(string(c)) - buf.Write(bytes.Repeat([]byte(" "), colWidth-len(c)-len(same))) + buf.Write(bytes.Repeat([]byte(" "), colWidth-runes.WidthAll(c)-runes.WidthAll(same))) if inSelect { buf.WriteString("\033[0m") diff --git a/vendor/github.com/chzyer/readline/history.go b/vendor/github.com/chzyer/readline/history.go index b154aedd5..6b17c464b 100644 --- a/vendor/github.com/chzyer/readline/history.go +++ b/vendor/github.com/chzyer/readline/history.go @@ -27,12 +27,14 @@ type opHistory struct { current *list.Element fd *os.File fdLock sync.Mutex + enable bool } func newOpHistory(cfg *Config) (o *opHistory) { o = &opHistory{ cfg: cfg, history: list.New(), + enable: true, } return o } @@ -117,7 +119,7 @@ func (o *opHistory) rewriteLocked() { buf := bufio.NewWriter(fd) for elem := o.history.Front(); elem != nil; elem = elem.Next() { - buf.WriteString(string(elem.Value.(*hisItem).Source)) + buf.WriteString(string(elem.Value.(*hisItem).Source) + "\n") } buf.Flush() @@ -223,6 +225,16 @@ func (o *opHistory) Next() ([]rune, bool) { return runes.Copy(o.showItem(current.Value)), true } +// Disable the current history +func (o *opHistory) Disable() { + o.enable = false +} + +// Enable the current history +func (o *opHistory) Enable() { + o.enable = true +} + func (o *opHistory) debug() { Debug("-------") for item := o.history.Front(); item != nil; item = item.Next() { @@ -232,6 +244,12 @@ func (o *opHistory) debug() { // save history func (o *opHistory) New(current []rune) (err error) { + + // history deactivated + if !o.enable { + return nil + } + current = runes.Copy(current) // if just use last command without modify diff --git a/vendor/github.com/chzyer/readline/operation.go b/vendor/github.com/chzyer/readline/operation.go index 8377cce7c..4c31624f8 100644 --- a/vendor/github.com/chzyer/readline/operation.go +++ b/vendor/github.com/chzyer/readline/operation.go @@ -3,6 +3,7 @@ package readline import ( "errors" "io" + "sync" ) var ( @@ -18,6 +19,7 @@ func (*InterruptError) Error() string { } type Operation struct { + m sync.Mutex cfg *Config t *Terminal buf *RuneBuffer @@ -32,6 +34,10 @@ type Operation struct { *opVim } +func (o *Operation) SetBuffer(what string) { + o.buf.Set([]rune(what)) +} + type wrapWriter struct { r *Operation t *Terminal @@ -66,7 +72,7 @@ func NewOperation(t *Terminal, cfg *Config) *Operation { t: t, buf: NewRuneBuffer(t, cfg.Prompt, cfg, width), outchan: make(chan []rune), - errchan: make(chan error), + errchan: make(chan error, 1), } op.w = op.buf.w op.SetConfig(cfg) @@ -91,11 +97,27 @@ 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() @@ -149,7 +171,7 @@ func (o *Operation) ioloop() { o.buf.Refresh(nil) } case CharTab: - if o.cfg.AutoComplete == nil { + if o.GetConfig().AutoComplete == nil { o.t.Bell() break } @@ -213,13 +235,15 @@ func (o *Operation) ioloop() { 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.cfg.UniqueEditLine { + if !o.GetConfig().UniqueEditLine { o.buf.WriteRune('\n') data = o.buf.Reset() data = data[:len(data)-1] // trim \n @@ -228,7 +252,7 @@ func (o *Operation) ioloop() { data = o.buf.Reset() } o.outchan <- data - if !o.cfg.DisableAutoSaveHistory { + if !o.GetConfig().DisableAutoSaveHistory { // ignore IO error _ = o.history.New(data) } else { @@ -262,14 +286,14 @@ func (o *Operation) ioloop() { } // treat as EOF - if !o.cfg.UniqueEditLine { - o.buf.WriteString(o.cfg.EOFPrompt + "\n") + 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.cfg.UniqueEditLine { + if o.GetConfig().UniqueEditLine { o.buf.Clean() } case CharInterrupt: @@ -286,12 +310,12 @@ func (o *Operation) ioloop() { } o.buf.MoveToLineEnd() o.buf.Refresh(nil) - hint := o.cfg.InterruptPrompt + "\n" - if !o.cfg.UniqueEditLine { + hint := o.GetConfig().InterruptPrompt + "\n" + if !o.GetConfig().UniqueEditLine { o.buf.WriteString(hint) } remain := o.buf.Reset() - if !o.cfg.UniqueEditLine { + if !o.GetConfig().UniqueEditLine { remain = remain[:len(remain)-len([]rune(hint))] } isUpdateHistory = false @@ -310,13 +334,15 @@ func (o *Operation) ioloop() { } } - if o.cfg.Listener != nil { - newLine, newPos, ok := o.cfg.Listener.OnChange(o.buf.Runes(), o.buf.Pos(), r) + 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) @@ -333,15 +359,16 @@ func (o *Operation) ioloop() { // 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.cfg.Stderr, r: o, t: o.t} + return &wrapWriter{target: o.GetConfig().Stderr, r: o, t: o.t} } 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) { @@ -353,8 +380,9 @@ func (o *Operation) Runes() ([]rune, error) { o.t.EnterRawMode() defer o.t.ExitRawMode() - if o.cfg.Listener != nil { - o.cfg.Listener.OnChange(nil, 0, 0) + listener := o.GetConfig().Listener + if listener != nil { + listener.OnChange(nil, 0, 0) } o.buf.Refresh(nil) // print prompt @@ -422,6 +450,8 @@ func (o *Operation) IsNormalMode() bool { } func (op *Operation) SetConfig(cfg *Config) (*Config, error) { + op.m.Lock() + defer op.m.Unlock() if op.cfg == cfg { return op.cfg, nil } @@ -489,3 +519,13 @@ func (d *DumpListener) OnChange(line []rune, pos int, key rune) (newLine []rune, 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 +} diff --git a/vendor/github.com/chzyer/readline/password.go b/vendor/github.com/chzyer/readline/password.go index 4b0737954..414288c2a 100644 --- a/vendor/github.com/chzyer/readline/password.go +++ b/vendor/github.com/chzyer/readline/password.go @@ -25,6 +25,7 @@ func (o *opPassword) PasswordConfig() *Config { InterruptPrompt: "\n", EOFPrompt: "\n", HistoryLimit: -1, + Painter: &defaultPainter{}, Stdout: o.o.cfg.Stdout, Stderr: o.o.cfg.Stderr, diff --git a/vendor/github.com/chzyer/readline/readline.go b/vendor/github.com/chzyer/readline/readline.go index 1e232fbb8..0e7aca06d 100644 --- a/vendor/github.com/chzyer/readline/readline.go +++ b/vendor/github.com/chzyer/readline/readline.go @@ -44,6 +44,8 @@ type Config struct { // NOTE: Listener will be triggered by (nil, 0, 0) immediately Listener Listener + Painter Painter + // If VimMode is true, readline will in vim.insert mode by default VimMode bool @@ -52,9 +54,10 @@ type Config struct { FuncGetWidth func() int - Stdin io.Reader - Stdout io.Writer - Stderr io.Writer + Stdin io.ReadCloser + StdinWriter io.Writer + Stdout io.Writer + Stderr io.Writer EnableMask bool MaskRune rune @@ -63,6 +66,10 @@ type Config struct { // it use in IM usually. UniqueEditLine bool + // filter input runes (may be used to disable CtrlZ or for translating some keys to different actions) + // -> output = new (translated) rune and true/false if continue with processing this one + FuncFilterInputRune func(rune) (rune, bool) + // force use interactive even stdout is not a tty FuncIsTerminal func() bool FuncMakeRaw func() error @@ -91,6 +98,9 @@ func (c *Config) Init() error { if c.Stdin == nil { c.Stdin = NewCancelableStdin(Stdin) } + + c.Stdin, c.StdinWriter = NewFillableStdin(c.Stdin) + if c.Stdout == nil { c.Stdout = Stdout } @@ -145,12 +155,19 @@ func (c *Config) SetListener(f func(line []rune, pos int, key rune) (newLine []r c.Listener = FuncListener(f) } +func (c *Config) SetPainter(p Painter) { + c.Painter = p +} + func NewEx(cfg *Config) (*Instance, error) { t, err := NewTerminal(cfg) if err != nil { return nil, err } rl := t.Readline() + if cfg.Painter == nil { + cfg.Painter = &defaultPainter{} + } return &Instance{ Config: cfg, Terminal: t, @@ -238,6 +255,11 @@ func (i *Instance) Readline() (string, error) { return i.Operation.String() } +func (i *Instance) ReadlineWithDefault(what string) (string, error) { + i.Operation.SetBuffer(what) + return i.Operation.String() +} + func (i *Instance) SaveHistory(content string) error { return i.Operation.SaveHistory(content) } @@ -252,6 +274,7 @@ func (i *Instance) Close() error { if err := i.Terminal.Close(); err != nil { return err } + i.Config.Stdin.Close() i.Operation.Close() return nil } @@ -263,6 +286,20 @@ func (i *Instance) Write(b []byte) (int, error) { return i.Stdout().Write(b) } +// WriteStdin prefill the next Stdin fetch +// Next time you call ReadLine() this value will be writen before the user input +// ie : +// i := readline.New() +// i.WriteStdin([]byte("test")) +// _, _= i.Readline() +// +// gives +// +// > test[cursor] +func (i *Instance) WriteStdin(val []byte) (int, error) { + return i.Terminal.WriteStdin(val) +} + func (i *Instance) SetConfig(cfg *Config) *Config { if i.Config == cfg { return cfg @@ -277,3 +314,13 @@ func (i *Instance) SetConfig(cfg *Config) *Config { func (i *Instance) Refresh() { i.Operation.Refresh() } + +// HistoryDisable the save of the commands into the history +func (i *Instance) HistoryDisable() { + i.Operation.history.Disable() +} + +// HistoryEnable the save of the commands into the history (default on) +func (i *Instance) HistoryEnable() { + i.Operation.history.Enable() +} diff --git a/vendor/github.com/chzyer/readline/remote.go b/vendor/github.com/chzyer/readline/remote.go index db77ae8cf..74dbf5690 100644 --- a/vendor/github.com/chzyer/readline/remote.go +++ b/vendor/github.com/chzyer/readline/remote.go @@ -189,11 +189,12 @@ loop: } } -func (r *RemoteSvr) Close() { +func (r *RemoteSvr) Close() error { if atomic.CompareAndSwapInt32(&r.closed, 0, 1) { close(r.stopChan) r.conn.Close() } + return nil } func (r *RemoteSvr) readLoop(buf *bufio.Reader) { diff --git a/vendor/github.com/chzyer/readline/runebuf.go b/vendor/github.com/chzyer/readline/runebuf.go index 1b2a5d04d..81d2da50c 100644 --- a/vendor/github.com/chzyer/readline/runebuf.go +++ b/vendor/github.com/chzyer/readline/runebuf.go @@ -4,6 +4,7 @@ import ( "bufio" "bytes" "io" + "strconv" "strings" "sync" ) @@ -29,9 +30,15 @@ type RuneBuffer struct { offset string + lastKill []rune + sync.Mutex } +func (r* RuneBuffer) pushKill(text []rune) { + r.lastKill = append([]rune{}, text...) +} + func (r *RuneBuffer) OnWidthChange(newWidth int) { r.Lock() r.width = newWidth @@ -187,6 +194,7 @@ func (r *RuneBuffer) Replace(ch rune) { func (r *RuneBuffer) Erase() { r.Refresh(func() { r.idx = 0 + r.pushKill(r.buf[:]) r.buf = r.buf[:0] }) } @@ -196,6 +204,7 @@ func (r *RuneBuffer) Delete() (success bool) { if r.idx == len(r.buf) { return } + r.pushKill(r.buf[r.idx : r.idx+1]) r.buf = append(r.buf[:r.idx], r.buf[r.idx+1:]...) success = true }) @@ -212,6 +221,7 @@ func (r *RuneBuffer) DeleteWord() { } for i := init + 1; i < len(r.buf); i++ { if !IsWordBreak(r.buf[i]) && IsWordBreak(r.buf[i-1]) { + r.pushKill(r.buf[r.idx:i-1]) r.Refresh(func() { r.buf = append(r.buf[:r.idx], r.buf[i-1:]...) }) @@ -247,6 +257,7 @@ func (r *RuneBuffer) KillFront() { } length := len(r.buf) - r.idx + r.pushKill(r.buf[:r.idx]) copy(r.buf[:length], r.buf[r.idx:]) r.idx = 0 r.buf = r.buf[:length] @@ -255,6 +266,7 @@ func (r *RuneBuffer) KillFront() { func (r *RuneBuffer) Kill() { r.Refresh(func() { + r.pushKill(r.buf[r.idx:]) r.buf = r.buf[:r.idx] }) } @@ -321,6 +333,7 @@ func (r *RuneBuffer) BackEscapeWord() { } for i := r.idx - 1; i > 0; i-- { if !IsWordBreak(r.buf[i]) && IsWordBreak(r.buf[i-1]) { + r.pushKill(r.buf[i:r.idx]) r.buf = append(r.buf[:i], r.buf[r.idx:]...) r.idx = i return @@ -332,6 +345,20 @@ func (r *RuneBuffer) BackEscapeWord() { }) } +func (r *RuneBuffer) Yank() { + if len(r.lastKill) == 0 { + return + } + r.Refresh(func() { + buf := make([]rune, 0, len(r.buf) + len(r.lastKill)) + buf = append(buf, r.buf[:r.idx]...) + buf = append(buf, r.lastKill...) + buf = append(buf, r.buf[r.idx:]...) + r.buf = buf + r.idx += len(r.lastKill) + }) +} + func (r *RuneBuffer) Backspace() { r.Refresh(func() { if r.idx == 0 { @@ -460,28 +487,58 @@ func (r *RuneBuffer) output() []byte { buf.Write([]byte(string(r.cfg.MaskRune))) } if len(r.buf) > r.idx { - buf.Write(runes.Backspace(r.buf[r.idx:])) + buf.Write(r.getBackspaceSequence()) } } else { - for idx := range r.buf { - if r.buf[idx] == '\t' { + for _, e := range r.cfg.Painter.Paint(r.buf, r.idx) { + if e == '\t' { buf.WriteString(strings.Repeat(" ", TabWidth)) } else { - buf.WriteRune(r.buf[idx]) + buf.WriteRune(e) } } if r.isInLineEdge() { buf.Write([]byte(" \b")) } } - + // cursor position if len(r.buf) > r.idx { - buf.Write(runes.Backspace(r.buf[r.idx:])) + buf.Write(r.getBackspaceSequence()) } return buf.Bytes() } +func (r *RuneBuffer) getBackspaceSequence() []byte { + var sep = map[int]bool{} + + var i int + for { + if i >= runes.WidthAll(r.buf) { + break + } + + if i == 0 { + i -= r.promptLen() + } + i += r.width + + sep[i] = true + } + var buf []byte + for i := len(r.buf); i > r.idx; i-- { + // move input to the left of one + buf = append(buf, '\b') + if sep[i] { + // up one line, go to the start of the line and move cursor right to the end (r.width) + buf = append(buf, "\033[A\r"+"\033["+strconv.Itoa(r.width)+"C"...) + } + } + + return buf + +} + func (r *RuneBuffer) Reset() []rune { ret := runes.Copy(r.buf) r.buf = r.buf[:0] diff --git a/vendor/github.com/chzyer/readline/std.go b/vendor/github.com/chzyer/readline/std.go index e0c55ee98..61d44b759 100644 --- a/vendor/github.com/chzyer/readline/std.go +++ b/vendor/github.com/chzyer/readline/std.go @@ -131,3 +131,67 @@ func (c *CancelableStdin) Close() error { } return nil } + +// FillableStdin is a stdin reader which can prepend some data before +// reading into the real stdin +type FillableStdin struct { + sync.Mutex + stdin io.Reader + stdinBuffer io.ReadCloser + buf []byte + bufErr error +} + +// NewFillableStdin gives you FillableStdin +func NewFillableStdin(stdin io.Reader) (io.ReadCloser, io.Writer) { + r, w := io.Pipe() + s := &FillableStdin{ + stdinBuffer: r, + stdin: stdin, + } + s.ioloop() + return s, w +} + +func (s *FillableStdin) ioloop() { + go func() { + for { + bufR := make([]byte, 100) + var n int + n, s.bufErr = s.stdinBuffer.Read(bufR) + if s.bufErr != nil { + if s.bufErr == io.ErrClosedPipe { + break + } + } + s.Lock() + s.buf = append(s.buf, bufR[:n]...) + s.Unlock() + } + }() +} + +// Read will read from the local buffer and if no data, read from stdin +func (s *FillableStdin) Read(p []byte) (n int, err error) { + s.Lock() + i := len(s.buf) + if len(p) < i { + i = len(p) + } + if i > 0 { + n := copy(p, s.buf) + s.buf = s.buf[:0] + cerr := s.bufErr + s.bufErr = nil + s.Unlock() + return n, cerr + } + s.Unlock() + n, err = s.stdin.Read(p) + return n, err +} + +func (s *FillableStdin) Close() error { + s.stdinBuffer.Close() + return nil +} diff --git a/vendor/github.com/chzyer/readline/term.go b/vendor/github.com/chzyer/readline/term.go index 87ef8f7d7..133993ca8 100644 --- a/vendor/github.com/chzyer/readline/term.go +++ b/vendor/github.com/chzyer/readline/term.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd linux,!appengine netbsd openbsd +// +build darwin dragonfly freebsd linux,!appengine netbsd openbsd solaris // Package terminal provides support functions for dealing with terminals, as // commonly found on UNIX systems. @@ -19,19 +19,17 @@ package readline import ( "io" "syscall" - "unsafe" ) // State contains the state of a terminal. type State struct { - termios syscall.Termios + termios Termios } // IsTerminal returns true if the given file descriptor is a terminal. func IsTerminal(fd int) bool { - var termios syscall.Termios - _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) - return err == 0 + _, err := getTermios(fd) + return err == nil } // MakeRaw put the terminal connected to the given file descriptor into raw @@ -39,8 +37,11 @@ func IsTerminal(fd int) bool { // restored. func MakeRaw(fd int) (*State, error) { var oldState State - if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 { + + if termios, err := getTermios(fd); err != nil { return nil, err + } else { + oldState.termios = *termios } newState := oldState.termios @@ -52,47 +53,35 @@ func MakeRaw(fd int) (*State, error) { newState.Cflag &^= syscall.CSIZE | syscall.PARENB newState.Cflag |= syscall.CS8 - if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 { - return nil, err - } + newState.Cc[syscall.VMIN] = 1 + newState.Cc[syscall.VTIME] = 0 - return &oldState, nil + return &oldState, setTermios(fd, &newState) } // GetState returns the current state of a terminal which may be useful to // restore the terminal after a signal. func GetState(fd int) (*State, error) { - var oldState State - if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 { + termios, err := getTermios(fd) + if err != nil { return nil, err } - return &oldState, nil + return &State{termios: *termios}, nil } // Restore restores the terminal connected to the given file descriptor to a // previous state. func restoreTerm(fd int, state *State) error { - _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&state.termios)), 0, 0, 0) - return err -} - -// GetSize returns the dimensions of the given terminal. -func GetSize(fd int) (width, height int, err error) { - var dimensions [4]uint16 - - if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&dimensions)), 0, 0, 0); err != 0 { - return -1, -1, err - } - return int(dimensions[1]), int(dimensions[0]), nil + return setTermios(fd, &state.termios) } // ReadPassword reads a line of input from a terminal without local echo. This // is commonly used for inputting passwords and other sensitive data. The slice // returned does not include the \n. func ReadPassword(fd int) ([]byte, error) { - var oldState syscall.Termios - if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0); err != 0 { + oldState, err := getTermios(fd) + if err != nil { return nil, err } @@ -100,12 +89,12 @@ func ReadPassword(fd int) ([]byte, error) { newState.Lflag &^= syscall.ECHO newState.Lflag |= syscall.ICANON | syscall.ISIG newState.Iflag |= syscall.ICRNL - if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 { + if err := setTermios(fd, newState); err != nil { return nil, err } defer func() { - syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0) + setTermios(fd, oldState) }() var buf [16]byte diff --git a/vendor/github.com/chzyer/readline/term_bsd.go b/vendor/github.com/chzyer/readline/term_bsd.go index 69682cda0..68b56ea6b 100644 --- a/vendor/github.com/chzyer/readline/term_bsd.go +++ b/vendor/github.com/chzyer/readline/term_bsd.go @@ -6,7 +6,24 @@ package readline -import "syscall" +import ( + "syscall" + "unsafe" +) -const ioctlReadTermios = syscall.TIOCGETA -const ioctlWriteTermios = syscall.TIOCSETA +func getTermios(fd int) (*Termios, error) { + termios := new(Termios) + _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), syscall.TIOCGETA, uintptr(unsafe.Pointer(termios)), 0, 0, 0) + if err != 0 { + return nil, err + } + return termios, nil +} + +func setTermios(fd int, termios *Termios) error { + _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), syscall.TIOCSETA, uintptr(unsafe.Pointer(termios)), 0, 0, 0) + if err != 0 { + return err + } + return nil +} diff --git a/vendor/github.com/chzyer/readline/term_linux.go b/vendor/github.com/chzyer/readline/term_linux.go index 891800867..e3392b4ac 100644 --- a/vendor/github.com/chzyer/readline/term_linux.go +++ b/vendor/github.com/chzyer/readline/term_linux.go @@ -4,8 +4,30 @@ package readline +import ( + "syscall" + "unsafe" +) + // These constants are declared here, rather than importing // them from the syscall package as some syscall packages, even // on linux, for example gccgo, do not declare them. const ioctlReadTermios = 0x5401 // syscall.TCGETS const ioctlWriteTermios = 0x5402 // syscall.TCSETS + +func getTermios(fd int) (*Termios, error) { + termios := new(Termios) + _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(termios)), 0, 0, 0) + if err != 0 { + return nil, err + } + return termios, nil +} + +func setTermios(fd int, termios *Termios) error { + _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(termios)), 0, 0, 0) + if err != 0 { + return err + } + return nil +} diff --git a/vendor/github.com/chzyer/readline/term_solaris.go b/vendor/github.com/chzyer/readline/term_solaris.go new file mode 100644 index 000000000..4c27273c7 --- /dev/null +++ b/vendor/github.com/chzyer/readline/term_solaris.go @@ -0,0 +1,32 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build solaris + +package readline + +import "golang.org/x/sys/unix" + +// GetSize returns the dimensions of the given terminal. +func GetSize(fd int) (int, int, error) { + ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ) + if err != nil { + return 0, 0, err + } + return int(ws.Col), int(ws.Row), nil +} + +type Termios unix.Termios + +func getTermios(fd int) (*Termios, error) { + termios, err := unix.IoctlGetTermios(fd, unix.TCGETS) + if err != nil { + return nil, err + } + return (*Termios)(termios), nil +} + +func setTermios(fd int, termios *Termios) error { + return unix.IoctlSetTermios(fd, unix.TCSETSF, (*unix.Termios)(termios)) +} diff --git a/vendor/github.com/chzyer/readline/term_unix.go b/vendor/github.com/chzyer/readline/term_unix.go new file mode 100644 index 000000000..d3ea24244 --- /dev/null +++ b/vendor/github.com/chzyer/readline/term_unix.go @@ -0,0 +1,24 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd linux,!appengine netbsd openbsd + +package readline + +import ( + "syscall" + "unsafe" +) + +type Termios syscall.Termios + +// GetSize returns the dimensions of the given terminal. +func GetSize(fd int) (int, int, error) { + var dimensions [4]uint16 + _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&dimensions)), 0, 0, 0) + if err != 0 { + return 0, 0, err + } + return int(dimensions[1]), int(dimensions[0]), nil +} diff --git a/vendor/github.com/chzyer/readline/terminal.go b/vendor/github.com/chzyer/readline/terminal.go index b3d1cafe4..1078631c1 100644 --- a/vendor/github.com/chzyer/readline/terminal.go +++ b/vendor/github.com/chzyer/readline/terminal.go @@ -10,6 +10,7 @@ import ( ) type Terminal struct { + m sync.Mutex cfg *Config outchan chan rune closed int32 @@ -64,6 +65,12 @@ func (t *Terminal) Write(b []byte) (int, error) { return t.cfg.Stdout.Write(b) } +// WriteStdin prefill the next Stdin fetch +// Next time you call ReadLine() this value will be writen before the user input +func (t *Terminal) WriteStdin(b []byte) (int, error) { + return t.cfg.StdinWriter.Write(b) +} + type termSize struct { left int top int @@ -121,7 +128,7 @@ func (t *Terminal) ioloop() { expectNextChar bool ) - buf := bufio.NewReader(t.cfg.Stdin) + buf := bufio.NewReader(t.getStdin()) for { if !expectNextChar { atomic.StoreInt32(&t.isReading, 0) @@ -206,10 +213,26 @@ func (t *Terminal) Close() error { return t.ExitRawMode() } +func (t *Terminal) GetConfig() *Config { + t.m.Lock() + cfg := *t.cfg + t.m.Unlock() + return &cfg +} + +func (t *Terminal) getStdin() io.Reader { + t.m.Lock() + r := t.cfg.Stdin + t.m.Unlock() + return r +} + func (t *Terminal) SetConfig(c *Config) error { if err := c.Init(); err != nil { return err } + t.m.Lock() t.cfg = c + t.m.Unlock() return nil } diff --git a/vendor/github.com/chzyer/readline/utils.go b/vendor/github.com/chzyer/readline/utils.go index 96518f1dc..af4e00521 100644 --- a/vendor/github.com/chzyer/readline/utils.go +++ b/vendor/github.com/chzyer/readline/utils.go @@ -38,6 +38,7 @@ const ( CharTranspose = 20 CharCtrlU = 21 CharCtrlW = 23 + CharCtrlY = 25 CharCtrlZ = 26 CharEsc = 27 CharEscapeEx = 91 @@ -82,7 +83,9 @@ func Restore(fd int, state *State) error { if err != nil { // errno 0 means everything is ok :) if err.Error() == "errno 0" { - err = nil + return nil + } else { + return err } } return nil diff --git a/vendor/github.com/chzyer/readline/utils_unix.go b/vendor/github.com/chzyer/readline/utils_unix.go index 39c32a12a..f88dac97b 100644 --- a/vendor/github.com/chzyer/readline/utils_unix.go +++ b/vendor/github.com/chzyer/readline/utils_unix.go @@ -1,4 +1,4 @@ -// +build darwin dragonfly freebsd linux,!appengine netbsd openbsd +// +build darwin dragonfly freebsd linux,!appengine netbsd openbsd solaris package readline @@ -8,7 +8,6 @@ import ( "os/signal" "sync" "syscall" - "unsafe" ) type winsize struct { @@ -30,17 +29,11 @@ func SuspendMe() { // get width of the terminal func getWidth(stdoutFd int) int { - ws := &winsize{} - retCode, _, errno := syscall.Syscall(syscall.SYS_IOCTL, - uintptr(stdoutFd), - uintptr(syscall.TIOCGWINSZ), - uintptr(unsafe.Pointer(ws))) - - if int(retCode) == -1 { - _ = errno + cols, _, err := GetSize(stdoutFd) + if err != nil { return -1 } - return int(ws.Col) + return cols } func GetScreenWidth() int { diff --git a/vendor/github.com/chzyer/readline/vim.go b/vendor/github.com/chzyer/readline/vim.go index 641b22b7a..bedf2c1a6 100644 --- a/vendor/github.com/chzyer/readline/vim.go +++ b/vendor/github.com/chzyer/readline/vim.go @@ -72,6 +72,8 @@ func (o *opVim) handleVimNormalMovement(r rune, readNext func() rune) (t rune, h case 'l': rb.Delete() } + case 'p': + rb.Yank() case 'b', 'B': rb.MoveToPrevWord() case 'w', 'W': diff --git a/vendor/modules.txt b/vendor/modules.txt index 76b566282..4e021027d 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -128,7 +128,7 @@ github.com/bgentry/go-netrc/netrc github.com/bgentry/speakeasy # github.com/blang/semver v3.5.1+incompatible github.com/blang/semver -# github.com/chzyer/readline v0.0.0-20161106042343-c914be64f07d +# github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e github.com/chzyer/readline # github.com/coreos/etcd v3.3.10+incompatible github.com/coreos/etcd/client