vendor: go get github.com/chzyer/readline@master
This new version includes Solaris support, the lack of which previously caused us to disable readline-using features ("terraform console") on Solaris builds.
This commit is contained in:
parent
a038f8c43b
commit
aa6dca4912
2
go.mod
2
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
|
||||
|
|
4
go.sum
4
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=
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
.vscode/*
|
|
@ -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/...
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<img src="https://raw.githubusercontent.com/chzyer/readline/assets/logo_f.png" />
|
||||
</p>
|
||||
|
||||
A powerful readline library in `Linux` `macOS` `Windows`
|
||||
A powerful readline library in `Linux` `macOS` `Windows` `Solaris`
|
||||
|
||||
## Guide
|
||||
|
||||
|
@ -21,14 +21,15 @@ A powerful readline library in `Linux` `macOS` `Windows`
|
|||
## 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
|
||||
|
|
|
@ -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]
|
||||
}
|
||||
|
|
|
@ -203,7 +203,9 @@ func (o *opCompleter) CompleteRefresh() {
|
|||
// -1 to avoid reach the end of line
|
||||
width := o.width - 1
|
||||
colNum := width / colWidth
|
||||
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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,7 +54,8 @@ type Config struct {
|
|||
|
||||
FuncGetWidth func() int
|
||||
|
||||
Stdin io.Reader
|
||||
Stdin io.ReadCloser
|
||||
StdinWriter io.Writer
|
||||
Stdout io.Writer
|
||||
Stderr io.Writer
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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':
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue