From 6642cc6506a6f36c5a46dee350adab103adb8033 Mon Sep 17 00:00:00 2001 From: Cheney Date: Mon, 21 Sep 2015 13:13:30 +0800 Subject: [PATCH] support history --- .gitignore | 2 ++ history.go | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++ readline.go | 34 +++++++++++++++++++++++++------- runebuf.go | 11 +++++++++-- terminal.go | 5 ++--- utils.go | 2 ++ 6 files changed, 98 insertions(+), 12 deletions(-) create mode 100644 .gitignore create mode 100644 history.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d4dc10f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ + +*.tmp diff --git a/history.go b/history.go new file mode 100644 index 0000000..a58c89a --- /dev/null +++ b/history.go @@ -0,0 +1,56 @@ +package readline + +func (l *Readline) PrevHistory() []rune { + if l.current == nil { + return nil + } + current := l.current.Prev() + if current == nil { + return nil + } + l.current = current + return current.Value.([]rune) +} + +func (l *Readline) NextHistory() []rune { + if l.current == nil { + return nil + } + current := l.current.Next() + if current == nil { + return nil + } + l.current = current + return current.Value.([]rune) +} + +func (l *Readline) NewHistory(current []rune) { + l.UpdateHistory(current) + if l.current != l.history.Back() { + // move history item to current command + l.history.Remove(l.current) + use := l.current.Value.([]rune) + l.current = l.history.Back() + l.UpdateHistory(use) + } + + // push a new one to commit current command + l.PushHistory(nil) +} + +func (l *Readline) UpdateHistory(s []rune) { + if l.current == nil { + l.PushHistory(s) + return + } + r := l.current.Value.([]rune) + l.current.Value = append(r[:0], s...) +} + +func (l *Readline) PushHistory(s []rune) { + // copy + newCopy := make([]rune, len(s)) + copy(newCopy, s) + elem := l.history.PushBack(newCopy) + l.current = elem +} diff --git a/readline.go b/readline.go index 342643e..63c0521 100644 --- a/readline.go +++ b/readline.go @@ -1,6 +1,7 @@ package readline import ( + "container/list" "io" "os" ) @@ -10,15 +11,21 @@ type Readline struct { t *Terminal buf *RuneBuffer outchan chan []rune + + history *list.List + current *list.Element } const ( CharLineStart = 0x1 CharLineEnd = 0x5 - CharPrev = 0x2 - CharNext = 0x6 + CharNext = 0xe + CharPrev = 0x10 + CharBackward = 0x2 + CharForward = 0x6 CharEscape = 0x7f CharEnter = 0xd + CharEnter2 = 0xa ) type wrapWriter struct { @@ -40,6 +47,7 @@ func newReadline(r *os.File, t *Terminal, prompt string) *Readline { t: t, buf: NewRuneBuffer(t, prompt), outchan: make(chan []rune), + history: list.New(), } go rl.ioloop() return rl @@ -63,21 +71,33 @@ func (l *Readline) ioloop() { l.buf.Delete() case CharEscape: l.buf.BackEscape() - case CharEnter: + case CharEnter, CharEnter2: l.buf.WriteRune('\n') data := l.buf.Reset() - l.outchan <- data[:len(data)-1] + data = data[:len(data)-1] // trim \n + l.outchan <- data + l.NewHistory(data) + case CharBackward: + l.buf.MoveBackward() + case CharForward: + l.buf.MoveForward() case CharPrev: - l.buf.MovePrev() + buf := l.PrevHistory() + if buf != nil { + l.buf.Set(buf) + } case CharNext: - l.buf.MoveNext() + buf := l.NextHistory() + if buf != nil { + l.buf.Set(buf) + } case KeyInterrupt: l.buf.WriteString("^C\n") l.outchan <- nil - break default: l.buf.WriteRune(r) } + l.UpdateHistory(l.buf.Runes()) } } diff --git a/runebuf.go b/runebuf.go index 33348d4..a1d82be 100644 --- a/runebuf.go +++ b/runebuf.go @@ -42,7 +42,7 @@ func (r *RuneBuffer) MoveToLineStart() { r.Refresh(-1, r.SetIdx(0)) } -func (r *RuneBuffer) MovePrev() { +func (r *RuneBuffer) MoveBackward() { if r.idx == 0 { return } @@ -65,7 +65,7 @@ func (rb *RuneBuffer) WriteRunes(r []rune) { rb.Refresh(1, 1) } -func (r *RuneBuffer) MoveNext() { +func (r *RuneBuffer) MoveForward() { if r.idx == len(r.buf) { return } @@ -187,3 +187,10 @@ func (r *RuneBuffer) Reset() []rune { r.Refresh(-len(ret), r.SetIdx(0)) return ret } + +func (r *RuneBuffer) Set(buf []rune) { + length, idx := len(r.buf), r.idx + r.buf = buf + r.idx = len(r.buf) + r.RefreshSet(length, idx) +} diff --git a/terminal.go b/terminal.go index de78782..ef5bc6e 100644 --- a/terminal.go +++ b/terminal.go @@ -21,7 +21,6 @@ const ( KeyInterrupt = 0x3 KeyNextChar = 0x6 KeyDelete = 0x4 - KeyEnter = 0xd KeyEsc = 0x1b ) @@ -89,9 +88,9 @@ func (t *Terminal) ioloop() { goto exit case KeyEsc: prefix = true - case KeyEnter, KeyPrevChar, KeyNextChar, KeyDelete: + case CharEnter, CharEnter2, KeyPrevChar, KeyNextChar, KeyDelete: fallthrough - case CharLineEnd, CharLineStart: + case CharLineEnd, CharLineStart, CharNext, CharPrev: t.outchan <- r default: println("np:", r) diff --git a/utils.go b/utils.go index cfbc5b0..cc4752a 100644 --- a/utils.go +++ b/utils.go @@ -33,6 +33,8 @@ func prefixKey(r rune) rune { r = MetaNext case 'd': r = MetaDelete + case KeyEsc: + } return r }