add history limitation

This commit is contained in:
Cheney 2015-10-04 15:23:00 +08:00
parent 4dc2ce7141
commit bfa8c1dfdb
3 changed files with 93 additions and 20 deletions

View File

@ -19,40 +19,81 @@ func (h *hisItem) Clean() {
}
type opHistory struct {
path string
cfg *Config
history *list.List
historyVer int64
current *list.Element
fd *os.File
}
func newOpHistory(path string) (o *opHistory) {
func newOpHistory(cfg *Config) (o *opHistory) {
o = &opHistory{
path: path,
cfg: cfg,
history: list.New(),
}
if o.path == "" {
return
if o.cfg.HistoryFile != "" {
o.historyUpdatePath(o.cfg.HistoryFile)
}
f, err := os.OpenFile(o.path, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666)
return
}
// only called by newOpHistory
func (o *opHistory) historyUpdatePath(path string) {
f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666)
if err != nil {
return
}
o.fd = f
r := bufio.NewReader(o.fd)
for {
total := 0
for ; ; total++ {
line, err := r.ReadSlice('\n')
if err != nil {
break
}
o.PushHistory([]rune(strings.TrimSpace(string(line))))
o.CompactHistory()
}
if total > o.cfg.HistoryLimit {
o.HistoryRewrite()
}
o.historyVer++
o.PushHistory(nil)
return
}
func (o *opHistory) Close() {
func (o *opHistory) CompactHistory() {
for o.history.Len() > o.cfg.HistoryLimit {
o.history.Remove(o.history.Front())
}
}
func (o *opHistory) HistoryRewrite() {
if o.cfg.HistoryFile == "" {
return
}
tmpFile := o.cfg.HistoryFile + ".tmp"
fd, err := os.OpenFile(tmpFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC|os.O_APPEND, 0666)
if err != nil {
return
}
defer fd.Close()
buf := bufio.NewWriter(fd)
for elem := o.history.Front(); elem != nil; elem = elem.Next() {
buf.WriteString(string(elem.Value.(*hisItem).Source))
}
buf.Flush()
if o.fd != nil {
o.fd.Close()
}
// fd is write only, just satisfy what we need.
o.fd = fd
}
func (o *opHistory) CloseHistory() {
if o.fd != nil {
o.fd.Close()
}
@ -197,7 +238,6 @@ func (o *opHistory) UpdateHistory(s []rune, commit bool) {
}
func (o *opHistory) PushHistory(s []rune) {
// copy
newCopy := make([]rune, len(s))
copy(newCopy, s)
elem := o.history.PushBack(&hisItem{Source: newCopy})

View File

@ -51,12 +51,12 @@ func (w *wrapWriter) Write(b []byte) (int, error) {
func NewOperation(t *Terminal, cfg *Config) *Operation {
op := &Operation{
cfg: cfg,
t: t,
buf: NewRuneBuffer(t, cfg.Prompt),
outchan: make(chan []rune),
opHistory: newOpHistory(cfg.HistoryFile),
cfg: cfg,
t: t,
buf: NewRuneBuffer(t, cfg.Prompt),
outchan: make(chan []rune),
}
op.SetHistoryPath(cfg.HistoryFile)
op.opVim = newVimMode(op)
op.w = op.buf.w
op.opSearch = newOpSearch(op.buf.w, op.buf, op.opHistory)
@ -286,5 +286,13 @@ func (o *Operation) Slice() ([]byte, error) {
}
func (o *Operation) Close() {
o.opHistory.Close()
o.opHistory.CloseHistory()
}
func (o *Operation) SetHistoryPath(path string) {
if o.opHistory != nil {
o.opHistory.CloseHistory()
}
o.cfg.HistoryFile = path
o.opHistory = newOpHistory(o.cfg)
}

View File

@ -3,17 +3,28 @@ package readline
import "io"
type Instance struct {
Config *Config
Terminal *Terminal
Operation *Operation
}
type Config struct {
Prompt string
HistoryFile string
// prompt supports ANSI escape sequence, so we can color some characters even in windows
Prompt string
// readline will persist historys to file where HistoryFile specified
HistoryFile string
// specify the max length of historys, it's 500 by default
HistoryLimit int
// AutoCompleter will called once user press TAB
AutoComplete AutoCompleter
VimMode bool
Stdout io.Writer
Stderr io.Writer
// If VimMode is true, readline will in vim.insert mode by default
VimMode bool
Stdout io.Writer
Stderr io.Writer
inited bool
}
@ -29,6 +40,9 @@ func (c *Config) Init() error {
if c.Stderr == nil {
c.Stderr = Stderr
}
if c.HistoryLimit < 0 {
c.HistoryLimit = 500
}
return nil
}
@ -39,6 +53,7 @@ func NewEx(cfg *Config) (*Instance, error) {
}
rl := t.Readline()
return &Instance{
Config: cfg,
Terminal: t,
Operation: rl,
}, nil
@ -52,14 +67,22 @@ func (i *Instance) SetPrompt(s string) {
i.Operation.SetPrompt(s)
}
// change hisotry persistence in runtime
func (i *Instance) SetHistoryPath(p string) {
i.Operation.SetHistoryPath(p)
}
// readline will refresh automatic when write through Stdout()
func (i *Instance) Stdout() io.Writer {
return i.Operation.Stdout()
}
// readline will refresh automatic when write through Stdout()
func (i *Instance) Stderr() io.Writer {
return i.Operation.Stderr()
}
// switch VimMode in runtime
func (i *Instance) SetVimMode(on bool) {
i.Operation.SetVimMode(on)
}
@ -76,10 +99,12 @@ func (i *Instance) Readline() (string, error) {
return i.Operation.String()
}
// same as readline
func (i *Instance) ReadSlice() ([]byte, error) {
return i.Operation.Slice()
}
// we must make sure that call Close() before process exit.
func (i *Instance) Close() error {
if err := i.Terminal.Close(); err != nil {
return err