This commit is contained in:
Cheney 2015-10-02 10:37:21 +08:00
parent 79d1bf27b4
commit 879224ddc9
6 changed files with 139 additions and 79 deletions

View File

@ -28,6 +28,12 @@ You can read the source code in [example/main.go](https://github.com/chzyer/read
* More funny examples * More funny examples
* Support dumb/eterm-color terminal in emacs * Support dumb/eterm-color terminal in emacs
# Features
* Support emacs/vi mode, almost all basic features GNU-Readline support
* zsh-style backward/forward history search
* zsh-style completion
* Readline auto refresh when others write to Stdout while editing(it needs specify the Stdout/Stderr provided by *readline.Instance to others).
# Usage # Usage
* Simplest example * Simplest example

View File

@ -20,6 +20,10 @@ bye
} }
var completer = readline.NewPrefixCompleter( var completer = readline.NewPrefixCompleter(
readline.PcItem("mode",
readline.PcItem("vi"),
readline.PcItem("emacs"),
),
readline.PcItem("login"), readline.PcItem("login"),
readline.PcItem("say", readline.PcItem("say",
readline.PcItem("hello"), readline.PcItem("hello"),
@ -40,7 +44,6 @@ func main() {
Prompt: "\033[31m»\033[0m ", Prompt: "\033[31m»\033[0m ",
HistoryFile: "/tmp/readline.tmp", HistoryFile: "/tmp/readline.tmp",
AutoComplete: completer, AutoComplete: completer,
// VimMode: true,
}) })
if err != nil { if err != nil {
panic(err) panic(err)
@ -53,8 +56,23 @@ func main() {
if err != nil { if err != nil {
break break
} }
line = strings.TrimSpace(line)
switch { switch {
case strings.HasPrefix(line, "mode "):
switch line[5:] {
case "vi":
l.SetVimMode(true)
case "emacs":
l.SetVimMode(false)
default:
println("invalid mode:", line[5:])
}
case line == "mode":
if l.IsVimMode() {
println("current mode: vim")
} else {
println("current mode: emacs")
}
case line == "login": case line == "login":
pswd, err := l.ReadPassword("please enter your password: ") pswd, err := l.ReadPassword("please enter your password: ")
if err != nil { if err != nil {

View File

@ -95,9 +95,8 @@ func (o *Operation) ioloop() {
} }
if o.IsEnableVimMode() { if o.IsEnableVimMode() {
var ok bool r = o.HandleVim(r, o.t.ReadRune)
r, ok = o.HandleVim(r, o.t.ReadRune) if r == 0 {
if ok {
continue continue
} }
} }

View File

@ -64,6 +64,10 @@ func (i *Instance) SetVimMode(on bool) {
i.o.SetVimMode(on) i.o.SetVimMode(on)
} }
func (i *Instance) IsVimMode() bool {
return i.o.IsEnableVimMode()
}
func (i *Instance) ReadPassword(prompt string) ([]byte, error) { func (i *Instance) ReadPassword(prompt string) ([]byte, error) {
return i.o.Password(prompt) return i.o.Password(prompt)
} }

View File

@ -5,6 +5,11 @@ import (
"io" "io"
) )
type runeBufferBck struct {
buf []rune
idx int
}
type RuneBuffer struct { type RuneBuffer struct {
buf []rune buf []rune
idx int idx int
@ -12,6 +17,22 @@ type RuneBuffer struct {
w io.Writer w io.Writer
cleanInScreen bool cleanInScreen bool
bck *runeBufferBck
}
func (r *RuneBuffer) Backup() {
r.bck = &runeBufferBck{r.buf, r.idx}
}
func (r *RuneBuffer) Restore() {
r.Refresh(func() {
if r.bck == nil {
return
}
r.buf = r.bck.buf
r.idx = r.bck.idx
})
} }
func NewRuneBuffer(w io.Writer, prompt string) *RuneBuffer { func NewRuneBuffer(w io.Writer, prompt string) *RuneBuffer {
@ -98,6 +119,13 @@ func (r *RuneBuffer) MoveForward() {
}) })
} }
func (r *RuneBuffer) Erase() {
r.Refresh(func() {
r.idx = 0
r.buf = r.buf[:0]
})
}
func (r *RuneBuffer) Delete() (success bool) { func (r *RuneBuffer) Delete() (success bool) {
r.Refresh(func() { r.Refresh(func() {
if r.idx == len(r.buf) { if r.idx == len(r.buf) {

107
vim.go
View File

@ -30,21 +30,16 @@ func (o *opVim) SetVimMode(on bool) {
} }
func (o *opVim) ExitVimMode() { func (o *opVim) ExitVimMode() {
o.vimMode = VIM_NORMAL o.vimMode = VIM_INSERT
} }
func (o *opVim) IsEnableVimMode() bool { func (o *opVim) IsEnableVimMode() bool {
return o.cfg.VimMode return o.cfg.VimMode
} }
func (o *opVim) HandleVimNormal(r rune, readNext func() rune) (t rune, handle bool) { func (o *opVim) handleVimNormalMovement(r rune, readNext func() rune) (t rune, handled bool) {
switch r {
case CharEnter, CharInterrupt:
return r, false
}
rb := o.op.buf rb := o.op.buf
handled := true handled = true
{
switch r { switch r {
case 'h': case 'h':
t = CharBackward t = CharBackward
@ -54,38 +49,6 @@ func (o *opVim) HandleVimNormal(r rune, readNext func() rune) (t rune, handle bo
t = CharPrev t = CharPrev
case 'l': case 'l':
t = CharForward t = CharForward
default:
handled = false
}
if handled {
return t, false
}
}
{ // to insert
handled = true
switch r {
case 'i':
case 'I':
rb.MoveToLineStart()
case 'a':
rb.MoveForward()
case 'A':
rb.MoveToLineEnd()
case 's':
rb.Delete()
default:
handled = false
}
if handled {
o.EnterVimInsertMode()
return r, true
}
}
{ // movement
handled = true
switch r {
case '0', '^': case '0', '^':
rb.MoveToLineStart() rb.MoveToLineStart()
case '$': case '$':
@ -101,21 +64,63 @@ func (o *opVim) HandleVimNormal(r rune, readNext func() rune) (t rune, handle bo
switch next { switch next {
case CharEsc: case CharEsc:
default: default:
if rb.MoveTo(next, prevChar, reverse) { rb.MoveTo(next, prevChar, reverse)
return r, true
}
} }
default: default:
handled = false return r, false
} }
if handled { return t, true
return r, true }
func (o *opVim) handleVimNormalEnterInsert(r rune, readNext func() rune) (t rune, handled bool) {
rb := o.op.buf
handled = true
switch r {
case 'i':
case 'I':
rb.MoveToLineStart()
case 'a':
rb.MoveForward()
case 'A':
rb.MoveToLineEnd()
case 's':
rb.Delete()
case 'S':
rb.Erase()
case 'c':
next := readNext()
switch next {
case 'c':
rb.Erase()
case 'w':
rb.DeleteWord()
} }
default:
return r, false
}
o.EnterVimInsertMode()
return
}
func (o *opVim) HandleVimNormal(r rune, readNext func() rune) (t rune) {
switch r {
case CharEnter, CharInterrupt:
o.ExitVimMode()
return r
}
if r, handled := o.handleVimNormalMovement(r, readNext); handled {
return r
}
if r, handled := o.handleVimNormalEnterInsert(r, readNext); handled {
return r
} }
// invalid operation // invalid operation
o.op.t.Bell() o.op.t.Bell()
return r, true return 0
} }
func (o *opVim) EnterVimInsertMode() { func (o *opVim) EnterVimInsertMode() {
@ -126,19 +131,19 @@ func (o *opVim) ExitVimInsertMode() {
o.vimMode = VIM_NORMAL o.vimMode = VIM_NORMAL
} }
func (o *opVim) HandleVim(r rune, readNext func() rune) (rune, bool) { func (o *opVim) HandleVim(r rune, readNext func() rune) rune {
if o.vimMode == VIM_NORMAL { if o.vimMode == VIM_NORMAL {
return o.HandleVimNormal(r, readNext) return o.HandleVimNormal(r, readNext)
} }
if r == CharEsc { if r == CharEsc {
o.ExitVimInsertMode() o.ExitVimInsertMode()
return r, true return 0
} }
switch o.vimMode { switch o.vimMode {
case VIM_INSERT: case VIM_INSERT:
return r, false return r
case VIM_VISUAL: case VIM_VISUAL:
} }
return r, false return r
} }