treat `Ctrl+D` + EmptyLine = EOF, `Ctrl+C` = ErrInterrupt

This commit is contained in:
Cheney 2015-11-19 11:55:07 +08:00
parent 9364259fb1
commit 7a18498f5b
2 changed files with 30 additions and 10 deletions

View File

@ -1,6 +1,7 @@
package readline package readline
import ( import (
"errors"
"fmt" "fmt"
"io" "io"
"os" "os"
@ -8,11 +9,16 @@ import (
"golang.org/x/crypto/ssh/terminal" "golang.org/x/crypto/ssh/terminal"
) )
var (
ErrInterrupt = errors.New("Interrupt")
)
type Operation struct { type Operation struct {
cfg *Config cfg *Config
t *Terminal t *Terminal
buf *RuneBuffer buf *RuneBuffer
outchan chan []rune outchan chan []rune
errchan chan error
w io.Writer w io.Writer
*opHistory *opHistory
@ -55,6 +61,7 @@ func NewOperation(t *Terminal, cfg *Config) *Operation {
t: t, t: t,
buf: NewRuneBuffer(t, cfg.Prompt), buf: NewRuneBuffer(t, cfg.Prompt),
outchan: make(chan []rune), outchan: make(chan []rune),
errchan: make(chan error),
} }
op.SetHistoryPath(cfg.HistoryFile) op.SetHistoryPath(cfg.HistoryFile)
op.opVim = newVimMode(op) op.opVim = newVimMode(op)
@ -141,10 +148,6 @@ func (o *Operation) ioloop() {
o.buf.MoveToLineStart() o.buf.MoveToLineStart()
case CharLineEnd: case CharLineEnd:
o.buf.MoveToLineEnd() o.buf.MoveToLineEnd()
case CharDelete:
if !o.buf.Delete() {
o.t.Bell()
}
case CharBackspace, CharCtrlH: case CharBackspace, CharCtrlH:
if o.IsSearchMode() { if o.IsSearchMode() {
o.SearchBackspace() o.SearchBackspace()
@ -190,6 +193,18 @@ func (o *Operation) ioloop() {
} else { } else {
o.t.Bell() o.t.Bell()
} }
case CharDelete:
if o.buf.Len() > 0 || !o.IsNormalMode() {
o.t.KickRead()
if !o.buf.Delete() {
o.t.Bell()
}
break
}
// treat as EOF
o.buf.WriteString("^D\n")
o.errchan <- io.EOF
case CharInterrupt: case CharInterrupt:
if o.IsSearchMode() { if o.IsSearchMode() {
o.t.KickRead() o.t.KickRead()
@ -205,7 +220,7 @@ func (o *Operation) ioloop() {
o.buf.MoveToLineEnd() o.buf.MoveToLineEnd()
o.buf.Refresh(nil) o.buf.Refresh(nil)
o.buf.WriteString("^C\n") o.buf.WriteString("^C\n")
o.outchan <- nil o.errchan <- ErrInterrupt
default: default:
if o.IsSearchMode() { if o.IsSearchMode() {
o.SearchChar(r) o.SearchChar(r)
@ -259,11 +274,12 @@ func (o *Operation) Runes() ([]rune, error) {
o.buf.Refresh(nil) // print prompt o.buf.Refresh(nil) // print prompt
o.t.KickRead() o.t.KickRead()
r := <-o.outchan select {
if r == nil { case r := <-o.outchan:
return nil, io.EOF
}
return r, nil return r, nil
case err := <-o.errchan:
return nil, err
}
} }
func (o *Operation) Password(prompt string) ([]byte, error) { func (o *Operation) Password(prompt string) ([]byte, error) {
@ -302,3 +318,7 @@ func (o *Operation) SetHistoryPath(path string) {
o.cfg.HistoryFile = path o.cfg.HistoryFile = path
o.opHistory = newOpHistory(o.cfg) o.opHistory = newOpHistory(o.cfg)
} }
func (o *Operation) IsNormalMode() bool {
return !o.IsInCompleteMode() && !o.IsSearchMode()
}

View File

@ -129,7 +129,7 @@ func (t *Terminal) ioloop() {
break break
} }
isEscape = true isEscape = true
case CharInterrupt, CharEnter, CharCtrlJ: case CharInterrupt, CharEnter, CharCtrlJ, CharDelete:
expectNextChar = false expectNextChar = false
fallthrough fallthrough
default: default: