forked from mirror/readline
refactor
This commit is contained in:
parent
79d1bf27b4
commit
879224ddc9
|
@ -28,6 +28,12 @@ You can read the source code in [example/main.go](https://github.com/chzyer/read
|
|||
* More funny examples
|
||||
* 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
|
||||
|
||||
* Simplest example
|
||||
|
|
|
@ -20,6 +20,10 @@ bye
|
|||
}
|
||||
|
||||
var completer = readline.NewPrefixCompleter(
|
||||
readline.PcItem("mode",
|
||||
readline.PcItem("vi"),
|
||||
readline.PcItem("emacs"),
|
||||
),
|
||||
readline.PcItem("login"),
|
||||
readline.PcItem("say",
|
||||
readline.PcItem("hello"),
|
||||
|
@ -40,7 +44,6 @@ func main() {
|
|||
Prompt: "\033[31m»\033[0m ",
|
||||
HistoryFile: "/tmp/readline.tmp",
|
||||
AutoComplete: completer,
|
||||
// VimMode: true,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -53,8 +56,23 @@ func main() {
|
|||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
line = strings.TrimSpace(line)
|
||||
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":
|
||||
pswd, err := l.ReadPassword("please enter your password: ")
|
||||
if err != nil {
|
||||
|
|
|
@ -95,9 +95,8 @@ func (o *Operation) ioloop() {
|
|||
}
|
||||
|
||||
if o.IsEnableVimMode() {
|
||||
var ok bool
|
||||
r, ok = o.HandleVim(r, o.t.ReadRune)
|
||||
if ok {
|
||||
r = o.HandleVim(r, o.t.ReadRune)
|
||||
if r == 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,6 +64,10 @@ func (i *Instance) SetVimMode(on bool) {
|
|||
i.o.SetVimMode(on)
|
||||
}
|
||||
|
||||
func (i *Instance) IsVimMode() bool {
|
||||
return i.o.IsEnableVimMode()
|
||||
}
|
||||
|
||||
func (i *Instance) ReadPassword(prompt string) ([]byte, error) {
|
||||
return i.o.Password(prompt)
|
||||
}
|
||||
|
|
28
runebuf.go
28
runebuf.go
|
@ -5,6 +5,11 @@ import (
|
|||
"io"
|
||||
)
|
||||
|
||||
type runeBufferBck struct {
|
||||
buf []rune
|
||||
idx int
|
||||
}
|
||||
|
||||
type RuneBuffer struct {
|
||||
buf []rune
|
||||
idx int
|
||||
|
@ -12,6 +17,22 @@ type RuneBuffer struct {
|
|||
w io.Writer
|
||||
|
||||
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 {
|
||||
|
@ -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) {
|
||||
r.Refresh(func() {
|
||||
if r.idx == len(r.buf) {
|
||||
|
|
153
vim.go
153
vim.go
|
@ -30,92 +30,97 @@ func (o *opVim) SetVimMode(on bool) {
|
|||
}
|
||||
|
||||
func (o *opVim) ExitVimMode() {
|
||||
o.vimMode = VIM_NORMAL
|
||||
o.vimMode = VIM_INSERT
|
||||
}
|
||||
|
||||
func (o *opVim) IsEnableVimMode() bool {
|
||||
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) {
|
||||
rb := o.op.buf
|
||||
handled = true
|
||||
switch r {
|
||||
case CharEnter, CharInterrupt:
|
||||
case 'h':
|
||||
t = CharBackward
|
||||
case 'j':
|
||||
t = CharNext
|
||||
case 'k':
|
||||
t = CharPrev
|
||||
case 'l':
|
||||
t = CharForward
|
||||
case '0', '^':
|
||||
rb.MoveToLineStart()
|
||||
case '$':
|
||||
rb.MoveToLineEnd()
|
||||
case 'b':
|
||||
rb.MoveToPrevWord()
|
||||
case 'w':
|
||||
rb.MoveToNextWord()
|
||||
case 'f', 'F', 't', 'T':
|
||||
next := readNext()
|
||||
prevChar := r == 't' || r == 'T'
|
||||
reverse := r == 'F' || r == 'T'
|
||||
switch next {
|
||||
case CharEsc:
|
||||
default:
|
||||
rb.MoveTo(next, prevChar, reverse)
|
||||
}
|
||||
default:
|
||||
return r, false
|
||||
}
|
||||
return t, true
|
||||
}
|
||||
|
||||
func (o *opVim) handleVimNormalEnterInsert(r rune, readNext func() rune) (t rune, handled bool) {
|
||||
rb := o.op.buf
|
||||
handled := true
|
||||
{
|
||||
switch r {
|
||||
case 'h':
|
||||
t = CharBackward
|
||||
case 'j':
|
||||
t = CharNext
|
||||
case 'k':
|
||||
t = CharPrev
|
||||
case 'l':
|
||||
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', '^':
|
||||
rb.MoveToLineStart()
|
||||
case '$':
|
||||
rb.MoveToLineEnd()
|
||||
case 'b':
|
||||
rb.MoveToPrevWord()
|
||||
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.MoveToNextWord()
|
||||
case 'f', 'F', 't', 'T':
|
||||
next := readNext()
|
||||
prevChar := r == 't' || r == 'T'
|
||||
reverse := r == 'F' || r == 'T'
|
||||
switch next {
|
||||
case CharEsc:
|
||||
default:
|
||||
if rb.MoveTo(next, prevChar, reverse) {
|
||||
return r, true
|
||||
}
|
||||
}
|
||||
default:
|
||||
handled = false
|
||||
}
|
||||
if handled {
|
||||
return r, true
|
||||
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
|
||||
o.op.t.Bell()
|
||||
return r, true
|
||||
return 0
|
||||
}
|
||||
|
||||
func (o *opVim) EnterVimInsertMode() {
|
||||
|
@ -126,19 +131,19 @@ func (o *opVim) ExitVimInsertMode() {
|
|||
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 {
|
||||
return o.HandleVimNormal(r, readNext)
|
||||
}
|
||||
if r == CharEsc {
|
||||
o.ExitVimInsertMode()
|
||||
return r, true
|
||||
return 0
|
||||
}
|
||||
|
||||
switch o.vimMode {
|
||||
case VIM_INSERT:
|
||||
return r, false
|
||||
return r
|
||||
case VIM_VISUAL:
|
||||
}
|
||||
return r, false
|
||||
return r
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue