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))
|
return o.fd.Fd() == ^(uintptr(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *opHistory) InitHistory() {
|
func (o *opHistory) Init() {
|
||||||
if o.IsHistoryClosed() {
|
if o.IsHistoryClosed() {
|
||||||
o.initHistory()
|
o.initHistory()
|
||||||
}
|
}
|
||||||
|
@ -66,24 +66,24 @@ func (o *opHistory) historyUpdatePath(path string) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
o.PushHistory([]rune(strings.TrimSpace(string(line))))
|
o.Push([]rune(strings.TrimSpace(string(line))))
|
||||||
o.CompactHistory()
|
o.Compact()
|
||||||
}
|
}
|
||||||
if total > o.cfg.HistoryLimit {
|
if total > o.cfg.HistoryLimit {
|
||||||
o.HistoryRewrite()
|
o.Rewrite()
|
||||||
}
|
}
|
||||||
o.historyVer++
|
o.historyVer++
|
||||||
o.PushHistory(nil)
|
o.Push(nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *opHistory) CompactHistory() {
|
func (o *opHistory) Compact() {
|
||||||
for o.history.Len() > o.cfg.HistoryLimit && o.history.Len() > 0 {
|
for o.history.Len() > o.cfg.HistoryLimit && o.history.Len() > 0 {
|
||||||
o.history.Remove(o.history.Front())
|
o.history.Remove(o.history.Front())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *opHistory) HistoryRewrite() {
|
func (o *opHistory) Rewrite() {
|
||||||
if o.cfg.HistoryFile == "" {
|
if o.cfg.HistoryFile == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -113,13 +113,13 @@ func (o *opHistory) HistoryRewrite() {
|
||||||
o.fd = fd
|
o.fd = fd
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *opHistory) CloseHistory() {
|
func (o *opHistory) Close() {
|
||||||
if o.fd != nil {
|
if o.fd != nil {
|
||||||
o.fd.Close()
|
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() {
|
for elem := o.current; elem != nil; elem = elem.Prev() {
|
||||||
item := o.showItem(elem.Value)
|
item := o.showItem(elem.Value)
|
||||||
if isNewSearch {
|
if isNewSearch {
|
||||||
|
@ -139,7 +139,7 @@ func (o *opHistory) FindHistoryBck(isNewSearch bool, rs []rune, start int) (int,
|
||||||
return -1, nil
|
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() {
|
for elem := o.current; elem != nil; elem = elem.Next() {
|
||||||
item := o.showItem(elem.Value)
|
item := o.showItem(elem.Value)
|
||||||
if isNewSearch {
|
if isNewSearch {
|
||||||
|
@ -175,7 +175,7 @@ func (o *opHistory) showItem(obj interface{}) []rune {
|
||||||
return item.Source
|
return item.Source
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *opHistory) PrevHistory() []rune {
|
func (o *opHistory) Prev() []rune {
|
||||||
if o.current == nil {
|
if o.current == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -187,7 +187,7 @@ func (o *opHistory) PrevHistory() []rune {
|
||||||
return o.showItem(current.Value)
|
return o.showItem(current.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *opHistory) NextHistory() ([]rune, bool) {
|
func (o *opHistory) Next() ([]rune, bool) {
|
||||||
if o.current == nil {
|
if o.current == nil {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
@ -200,7 +200,8 @@ func (o *opHistory) NextHistory() ([]rune, bool) {
|
||||||
return o.showItem(current.Value), true
|
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
|
// if just use last command without modify
|
||||||
// just clean lastest history
|
// just clean lastest history
|
||||||
if back := o.history.Back(); back != nil {
|
if back := o.history.Back(); back != nil {
|
||||||
|
@ -231,22 +232,22 @@ func (o *opHistory) NewHistory(current []rune) {
|
||||||
current = use.Tmp
|
current = use.Tmp
|
||||||
}
|
}
|
||||||
|
|
||||||
o.UpdateHistory(current, true)
|
o.Update(current, true)
|
||||||
|
|
||||||
// push a new one to commit current command
|
// push a new one to commit current command
|
||||||
o.historyVer++
|
o.historyVer++
|
||||||
o.PushHistory(nil)
|
o.Push(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *opHistory) RevertHistory() {
|
func (o *opHistory) Revert() {
|
||||||
o.historyVer++
|
o.historyVer++
|
||||||
o.current = o.history.Back()
|
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 {
|
if o.current == nil {
|
||||||
o.PushHistory(s)
|
o.Push(s)
|
||||||
o.CompactHistory()
|
o.Compact()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
r := o.current.Value.(*hisItem)
|
r := o.current.Value.(*hisItem)
|
||||||
|
@ -261,10 +262,10 @@ func (o *opHistory) UpdateHistory(s []rune, commit bool) {
|
||||||
r.Tmp = append(r.Tmp[:0], s...)
|
r.Tmp = append(r.Tmp[:0], s...)
|
||||||
}
|
}
|
||||||
o.current.Value = r
|
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))
|
newCopy := make([]rune, len(s))
|
||||||
copy(newCopy, s)
|
copy(newCopy, s)
|
||||||
elem := o.history.PushBack(&hisItem{Source: newCopy})
|
elem := o.history.PushBack(&hisItem{Source: newCopy})
|
||||||
|
|
41
operation.go
41
operation.go
|
@ -20,7 +20,7 @@ type Operation struct {
|
||||||
errchan chan error
|
errchan chan error
|
||||||
w io.Writer
|
w io.Writer
|
||||||
|
|
||||||
*opHistory
|
history *opHistory
|
||||||
*opSearch
|
*opSearch
|
||||||
*opCompleter
|
*opCompleter
|
||||||
*opPassword
|
*opPassword
|
||||||
|
@ -95,7 +95,7 @@ func (o *Operation) ioloop() {
|
||||||
o.buf.Refresh(nil)
|
o.buf.Refresh(nil)
|
||||||
switch r {
|
switch r {
|
||||||
case CharEnter, CharCtrlJ:
|
case CharEnter, CharCtrlJ:
|
||||||
o.UpdateHistory(o.buf.Runes(), false)
|
o.history.Update(o.buf.Runes(), false)
|
||||||
fallthrough
|
fallthrough
|
||||||
case CharInterrupt:
|
case CharInterrupt:
|
||||||
o.t.KickRead()
|
o.t.KickRead()
|
||||||
|
@ -184,20 +184,24 @@ func (o *Operation) ioloop() {
|
||||||
data = o.buf.Reset()
|
data = o.buf.Reset()
|
||||||
}
|
}
|
||||||
o.outchan <- data
|
o.outchan <- data
|
||||||
o.NewHistory(data)
|
if !o.cfg.DisableAutoSaveHistory {
|
||||||
|
o.history.New(data)
|
||||||
|
} else {
|
||||||
|
isUpdateHistory = false
|
||||||
|
}
|
||||||
case CharBackward:
|
case CharBackward:
|
||||||
o.buf.MoveBackward()
|
o.buf.MoveBackward()
|
||||||
case CharForward:
|
case CharForward:
|
||||||
o.buf.MoveForward()
|
o.buf.MoveForward()
|
||||||
case CharPrev:
|
case CharPrev:
|
||||||
buf := o.PrevHistory()
|
buf := o.history.Prev()
|
||||||
if buf != nil {
|
if buf != nil {
|
||||||
o.buf.Set(buf)
|
o.buf.Set(buf)
|
||||||
} else {
|
} else {
|
||||||
o.t.Bell()
|
o.t.Bell()
|
||||||
}
|
}
|
||||||
case CharNext:
|
case CharNext:
|
||||||
buf, ok := o.NextHistory()
|
buf, ok := o.history.Next()
|
||||||
if ok {
|
if ok {
|
||||||
o.buf.Set(buf)
|
o.buf.Set(buf)
|
||||||
} else {
|
} else {
|
||||||
|
@ -216,7 +220,7 @@ func (o *Operation) ioloop() {
|
||||||
o.buf.WriteString(o.cfg.EOFPrompt + "\n")
|
o.buf.WriteString(o.cfg.EOFPrompt + "\n")
|
||||||
o.buf.Reset()
|
o.buf.Reset()
|
||||||
isUpdateHistory = false
|
isUpdateHistory = false
|
||||||
o.RevertHistory()
|
o.history.Revert()
|
||||||
o.errchan <- io.EOF
|
o.errchan <- io.EOF
|
||||||
case CharInterrupt:
|
case CharInterrupt:
|
||||||
if o.IsSearchMode() {
|
if o.IsSearchMode() {
|
||||||
|
@ -235,7 +239,7 @@ func (o *Operation) ioloop() {
|
||||||
o.buf.WriteString(o.cfg.InterruptPrompt + "\n")
|
o.buf.WriteString(o.cfg.InterruptPrompt + "\n")
|
||||||
o.buf.Reset()
|
o.buf.Reset()
|
||||||
isUpdateHistory = false
|
isUpdateHistory = false
|
||||||
o.RevertHistory()
|
o.history.Revert()
|
||||||
o.errchan <- ErrInterrupt
|
o.errchan <- ErrInterrupt
|
||||||
default:
|
default:
|
||||||
if o.IsSearchMode() {
|
if o.IsSearchMode() {
|
||||||
|
@ -270,7 +274,8 @@ func (o *Operation) ioloop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if isUpdateHistory && !o.IsSearchMode() {
|
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() {
|
func (o *Operation) Close() {
|
||||||
o.opHistory.CloseHistory()
|
o.history.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Operation) SetHistoryPath(path string) {
|
func (o *Operation) SetHistoryPath(path string) {
|
||||||
if o.opHistory != nil {
|
if o.history != nil {
|
||||||
o.opHistory.CloseHistory()
|
o.history.Close()
|
||||||
}
|
}
|
||||||
o.cfg.HistoryFile = path
|
o.cfg.HistoryFile = path
|
||||||
o.opHistory = newOpHistory(o.cfg)
|
o.history = newOpHistory(o.cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Operation) IsNormalMode() bool {
|
func (o *Operation) IsNormalMode() bool {
|
||||||
|
@ -382,19 +387,23 @@ func (op *Operation) SetConfig(cfg *Config) (*Config, error) {
|
||||||
|
|
||||||
if cfg.opHistory == nil {
|
if cfg.opHistory == nil {
|
||||||
op.SetHistoryPath(cfg.HistoryFile)
|
op.SetHistoryPath(cfg.HistoryFile)
|
||||||
cfg.opHistory = op.opHistory
|
cfg.opHistory = op.history
|
||||||
cfg.opSearch = newOpSearch(op.buf.w, op.buf, op.opHistory)
|
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
|
// SetHistoryPath will close opHistory which already exists
|
||||||
// so if we use it next time, we need to reopen it by `InitHistory()`
|
// so if we use it next time, we need to reopen it by `InitHistory()`
|
||||||
op.opHistory.InitHistory()
|
op.history.Init()
|
||||||
|
|
||||||
op.opSearch = cfg.opSearch
|
op.opSearch = cfg.opSearch
|
||||||
return old, nil
|
return old, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *Operation) SaveHistory(content string) {
|
||||||
|
o.history.New([]rune(content))
|
||||||
|
}
|
||||||
|
|
||||||
func (o *Operation) Refresh() {
|
func (o *Operation) Refresh() {
|
||||||
if o.t.IsReading() {
|
if o.t.IsReading() {
|
||||||
o.buf.Refresh(nil)
|
o.buf.Refresh(nil)
|
||||||
|
|
|
@ -24,6 +24,7 @@ type Config struct {
|
||||||
HistoryFile string
|
HistoryFile string
|
||||||
// specify the max length of historys, it's 500 by default, set it to -1 to disable history
|
// 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
|
// AutoCompleter will called once user press TAB
|
||||||
AutoComplete AutoCompleter
|
AutoComplete AutoCompleter
|
||||||
|
@ -154,10 +155,15 @@ func (i *Instance) ReadPassword(prompt string) ([]byte, error) {
|
||||||
return i.Operation.Password(prompt)
|
return i.Operation.Password(prompt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// err is one of (nil, io.EOF, readline.ErrInterrupt)
|
||||||
func (i *Instance) Readline() (string, error) {
|
func (i *Instance) Readline() (string, error) {
|
||||||
return i.Operation.String()
|
return i.Operation.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Instance) SaveHistory(content string) {
|
||||||
|
i.Operation.SaveHistory(content)
|
||||||
|
}
|
||||||
|
|
||||||
// same as readline
|
// same as readline
|
||||||
func (i *Instance) ReadSlice() ([]byte, error) {
|
func (i *Instance) ReadSlice() ([]byte, error) {
|
||||||
return i.Operation.Slice()
|
return i.Operation.Slice()
|
||||||
|
|
|
@ -51,9 +51,9 @@ func (o *opSearch) SearchBackspace() {
|
||||||
|
|
||||||
func (o *opSearch) findHistoryBy(isNewSearch bool) (int, *list.Element) {
|
func (o *opSearch) findHistoryBy(isNewSearch bool) (int, *list.Element) {
|
||||||
if o.dir == S_DIR_BCK {
|
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 {
|
func (o *opSearch) search(isChange bool) bool {
|
||||||
|
|
Loading…
Reference in New Issue