mirror of https://github.com/chzyer/readline.git
update history interfaces. and support DisableAutoSaveHistory
This commit is contained in:
parent
d435bdb1ae
commit
07485bbd8f
|
@ -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)
|
||||
}
|
||||
}
|
43
history.go
43
history.go
|
@ -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})
|
||||
|
|
41
operation.go
41
operation.go
|
@ -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)
|
||||
|
|
|
@ -23,7 +23,8 @@ type Config struct {
|
|||
// readline will persist historys to file where HistoryFile specified
|
||||
HistoryFile string
|
||||
// specify the max length of historys, it's 500 by default, set it to -1 to disable history
|
||||
HistoryLimit int
|
||||
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()
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue