update history interfaces. and support DisableAutoSaveHistory

This commit is contained in:
Cheney 2016-02-16 16:55:19 +08:00
parent d435bdb1ae
commit 07485bbd8f
5 changed files with 97 additions and 40 deletions

View File

@ -0,0 +1,41 @@
package main
import (
"strings"
"github.com/chzyer/readline"
)
func main() {
rl, err := readline.NewEx(&readline.Config{
Prompt: "> ",
HistoryFile: "/tmp/readline-multiline",
DisableAutoSaveHistory: true,
})
if err != nil {
panic(err)
}
defer rl.Close()
var cmds []string
for {
line, err := rl.Readline()
if err != nil {
break
}
line = strings.TrimSpace(line)
if len(line) == 0 {
continue
}
cmds = append(cmds, line)
if !strings.Contains(line, ";") {
rl.SetPrompt(">>> ")
continue
}
cmd := strings.Join(cmds, " ")
cmds = cmds[:0]
rl.SetPrompt("> ")
rl.SaveHistory(cmd)
println(cmd)
}
}

View File

@ -40,7 +40,7 @@ func (o *opHistory) IsHistoryClosed() bool {
return o.fd.Fd() == ^(uintptr(0))
}
func (o *opHistory) InitHistory() {
func (o *opHistory) Init() {
if o.IsHistoryClosed() {
o.initHistory()
}
@ -66,24 +66,24 @@ func (o *opHistory) historyUpdatePath(path string) {
if err != nil {
break
}
o.PushHistory([]rune(strings.TrimSpace(string(line))))
o.CompactHistory()
o.Push([]rune(strings.TrimSpace(string(line))))
o.Compact()
}
if total > o.cfg.HistoryLimit {
o.HistoryRewrite()
o.Rewrite()
}
o.historyVer++
o.PushHistory(nil)
o.Push(nil)
return
}
func (o *opHistory) CompactHistory() {
func (o *opHistory) Compact() {
for o.history.Len() > o.cfg.HistoryLimit && o.history.Len() > 0 {
o.history.Remove(o.history.Front())
}
}
func (o *opHistory) HistoryRewrite() {
func (o *opHistory) Rewrite() {
if o.cfg.HistoryFile == "" {
return
}
@ -113,13 +113,13 @@ func (o *opHistory) HistoryRewrite() {
o.fd = fd
}
func (o *opHistory) CloseHistory() {
func (o *opHistory) Close() {
if o.fd != nil {
o.fd.Close()
}
}
func (o *opHistory) FindHistoryBck(isNewSearch bool, rs []rune, start int) (int, *list.Element) {
func (o *opHistory) FindBck(isNewSearch bool, rs []rune, start int) (int, *list.Element) {
for elem := o.current; elem != nil; elem = elem.Prev() {
item := o.showItem(elem.Value)
if isNewSearch {
@ -139,7 +139,7 @@ func (o *opHistory) FindHistoryBck(isNewSearch bool, rs []rune, start int) (int,
return -1, nil
}
func (o *opHistory) FindHistoryFwd(isNewSearch bool, rs []rune, start int) (int, *list.Element) {
func (o *opHistory) FindFwd(isNewSearch bool, rs []rune, start int) (int, *list.Element) {
for elem := o.current; elem != nil; elem = elem.Next() {
item := o.showItem(elem.Value)
if isNewSearch {
@ -175,7 +175,7 @@ func (o *opHistory) showItem(obj interface{}) []rune {
return item.Source
}
func (o *opHistory) PrevHistory() []rune {
func (o *opHistory) Prev() []rune {
if o.current == nil {
return nil
}
@ -187,7 +187,7 @@ func (o *opHistory) PrevHistory() []rune {
return o.showItem(current.Value)
}
func (o *opHistory) NextHistory() ([]rune, bool) {
func (o *opHistory) Next() ([]rune, bool) {
if o.current == nil {
return nil, false
}
@ -200,7 +200,8 @@ func (o *opHistory) NextHistory() ([]rune, bool) {
return o.showItem(current.Value), true
}
func (o *opHistory) NewHistory(current []rune) {
// save history
func (o *opHistory) New(current []rune) {
// if just use last command without modify
// just clean lastest history
if back := o.history.Back(); back != nil {
@ -231,22 +232,22 @@ func (o *opHistory) NewHistory(current []rune) {
current = use.Tmp
}
o.UpdateHistory(current, true)
o.Update(current, true)
// push a new one to commit current command
o.historyVer++
o.PushHistory(nil)
o.Push(nil)
}
func (o *opHistory) RevertHistory() {
func (o *opHistory) Revert() {
o.historyVer++
o.current = o.history.Back()
}
func (o *opHistory) UpdateHistory(s []rune, commit bool) {
func (o *opHistory) Update(s []rune, commit bool) {
if o.current == nil {
o.PushHistory(s)
o.CompactHistory()
o.Push(s)
o.Compact()
return
}
r := o.current.Value.(*hisItem)
@ -261,10 +262,10 @@ func (o *opHistory) UpdateHistory(s []rune, commit bool) {
r.Tmp = append(r.Tmp[:0], s...)
}
o.current.Value = r
o.CompactHistory()
o.Compact()
}
func (o *opHistory) PushHistory(s []rune) {
func (o *opHistory) Push(s []rune) {
newCopy := make([]rune, len(s))
copy(newCopy, s)
elem := o.history.PushBack(&hisItem{Source: newCopy})

View File

@ -20,7 +20,7 @@ type Operation struct {
errchan chan error
w io.Writer
*opHistory
history *opHistory
*opSearch
*opCompleter
*opPassword
@ -95,7 +95,7 @@ func (o *Operation) ioloop() {
o.buf.Refresh(nil)
switch r {
case CharEnter, CharCtrlJ:
o.UpdateHistory(o.buf.Runes(), false)
o.history.Update(o.buf.Runes(), false)
fallthrough
case CharInterrupt:
o.t.KickRead()
@ -184,20 +184,24 @@ func (o *Operation) ioloop() {
data = o.buf.Reset()
}
o.outchan <- data
o.NewHistory(data)
if !o.cfg.DisableAutoSaveHistory {
o.history.New(data)
} else {
isUpdateHistory = false
}
case CharBackward:
o.buf.MoveBackward()
case CharForward:
o.buf.MoveForward()
case CharPrev:
buf := o.PrevHistory()
buf := o.history.Prev()
if buf != nil {
o.buf.Set(buf)
} else {
o.t.Bell()
}
case CharNext:
buf, ok := o.NextHistory()
buf, ok := o.history.Next()
if ok {
o.buf.Set(buf)
} else {
@ -216,7 +220,7 @@ func (o *Operation) ioloop() {
o.buf.WriteString(o.cfg.EOFPrompt + "\n")
o.buf.Reset()
isUpdateHistory = false
o.RevertHistory()
o.history.Revert()
o.errchan <- io.EOF
case CharInterrupt:
if o.IsSearchMode() {
@ -235,7 +239,7 @@ func (o *Operation) ioloop() {
o.buf.WriteString(o.cfg.InterruptPrompt + "\n")
o.buf.Reset()
isUpdateHistory = false
o.RevertHistory()
o.history.Revert()
o.errchan <- ErrInterrupt
default:
if o.IsSearchMode() {
@ -270,7 +274,8 @@ func (o *Operation) ioloop() {
}
}
if isUpdateHistory && !o.IsSearchMode() {
o.UpdateHistory(o.buf.Runes(), false)
// it will cause null history
o.history.Update(o.buf.Runes(), false)
}
}
}
@ -353,15 +358,15 @@ func (o *Operation) Slice() ([]byte, error) {
}
func (o *Operation) Close() {
o.opHistory.CloseHistory()
o.history.Close()
}
func (o *Operation) SetHistoryPath(path string) {
if o.opHistory != nil {
o.opHistory.CloseHistory()
if o.history != nil {
o.history.Close()
}
o.cfg.HistoryFile = path
o.opHistory = newOpHistory(o.cfg)
o.history = newOpHistory(o.cfg)
}
func (o *Operation) IsNormalMode() bool {
@ -382,19 +387,23 @@ func (op *Operation) SetConfig(cfg *Config) (*Config, error) {
if cfg.opHistory == nil {
op.SetHistoryPath(cfg.HistoryFile)
cfg.opHistory = op.opHistory
cfg.opSearch = newOpSearch(op.buf.w, op.buf, op.opHistory)
cfg.opHistory = op.history
cfg.opSearch = newOpSearch(op.buf.w, op.buf, op.history)
}
op.opHistory = cfg.opHistory
op.history = cfg.opHistory
// SetHistoryPath will close opHistory which already exists
// so if we use it next time, we need to reopen it by `InitHistory()`
op.opHistory.InitHistory()
op.history.Init()
op.opSearch = cfg.opSearch
return old, nil
}
func (o *Operation) SaveHistory(content string) {
o.history.New([]rune(content))
}
func (o *Operation) Refresh() {
if o.t.IsReading() {
o.buf.Refresh(nil)

View File

@ -24,6 +24,7 @@ type Config struct {
HistoryFile string
// specify the max length of historys, it's 500 by default, set it to -1 to disable history
HistoryLimit int
DisableAutoSaveHistory bool
// AutoCompleter will called once user press TAB
AutoComplete AutoCompleter
@ -154,10 +155,15 @@ func (i *Instance) ReadPassword(prompt string) ([]byte, error) {
return i.Operation.Password(prompt)
}
// err is one of (nil, io.EOF, readline.ErrInterrupt)
func (i *Instance) Readline() (string, error) {
return i.Operation.String()
}
func (i *Instance) SaveHistory(content string) {
i.Operation.SaveHistory(content)
}
// same as readline
func (i *Instance) ReadSlice() ([]byte, error) {
return i.Operation.Slice()

View File

@ -51,9 +51,9 @@ func (o *opSearch) SearchBackspace() {
func (o *opSearch) findHistoryBy(isNewSearch bool) (int, *list.Element) {
if o.dir == S_DIR_BCK {
return o.history.FindHistoryBck(isNewSearch, o.data, o.buf.idx)
return o.history.FindBck(isNewSearch, o.data, o.buf.idx)
}
return o.history.FindHistoryFwd(isNewSearch, o.data, o.buf.idx)
return o.history.FindFwd(isNewSearch, o.data, o.buf.idx)
}
func (o *opSearch) search(isChange bool) bool {