mirror of https://github.com/chzyer/readline.git
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
|
* 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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
28
runebuf.go
28
runebuf.go
|
@ -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) {
|
||||||
|
|
153
vim.go
153
vim.go
|
@ -30,92 +30,97 @@ 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) {
|
||||||
|
rb := o.op.buf
|
||||||
|
handled = true
|
||||||
switch r {
|
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 r, false
|
||||||
}
|
}
|
||||||
|
return t, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *opVim) handleVimNormalEnterInsert(r rune, readNext func() rune) (t rune, handled bool) {
|
||||||
rb := o.op.buf
|
rb := o.op.buf
|
||||||
handled := true
|
handled = true
|
||||||
{
|
switch r {
|
||||||
switch r {
|
case 'i':
|
||||||
case 'h':
|
case 'I':
|
||||||
t = CharBackward
|
rb.MoveToLineStart()
|
||||||
case 'j':
|
case 'a':
|
||||||
t = CharNext
|
rb.MoveForward()
|
||||||
case 'k':
|
case 'A':
|
||||||
t = CharPrev
|
rb.MoveToLineEnd()
|
||||||
case 'l':
|
case 's':
|
||||||
t = CharForward
|
rb.Delete()
|
||||||
default:
|
case 'S':
|
||||||
handled = false
|
rb.Erase()
|
||||||
}
|
case 'c':
|
||||||
if handled {
|
next := readNext()
|
||||||
return t, false
|
switch next {
|
||||||
}
|
case 'c':
|
||||||
}
|
rb.Erase()
|
||||||
|
|
||||||
{ // 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()
|
|
||||||
case 'w':
|
case 'w':
|
||||||
rb.MoveToNextWord()
|
rb.DeleteWord()
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue