forked from mirror/readline
add history limitation
This commit is contained in:
parent
4dc2ce7141
commit
bfa8c1dfdb
58
history.go
58
history.go
|
@ -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})
|
||||
|
|
12
operation.go
12
operation.go
|
@ -55,8 +55,8 @@ func NewOperation(t *Terminal, cfg *Config) *Operation {
|
|||
t: t,
|
||||
buf: NewRuneBuffer(t, cfg.Prompt),
|
||||
outchan: make(chan []rune),
|
||||
opHistory: newOpHistory(cfg.HistoryFile),
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
|
25
readline.go
25
readline.go
|
@ -3,15 +3,26 @@ package readline
|
|||
import "io"
|
||||
|
||||
type Instance struct {
|
||||
Config *Config
|
||||
Terminal *Terminal
|
||||
Operation *Operation
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
// 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
|
||||
|
||||
// If VimMode is true, readline will in vim.insert mode by default
|
||||
VimMode bool
|
||||
|
||||
Stdout io.Writer
|
||||
Stderr io.Writer
|
||||
|
||||
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue