Use persistent history in the editor

This resolves issue #45.
This commit is contained in:
Cheer Xiao 2015-02-26 00:40:35 +01:00
parent d804f82350
commit f407b94d09
3 changed files with 96 additions and 15 deletions

View File

@ -10,6 +10,7 @@ import (
"github.com/elves/elvish/edit/tty"
"github.com/elves/elvish/parse"
"github.com/elves/elvish/store"
)
const lackEOL = "\033[7m\u23ce\033[m\n"
@ -51,6 +52,8 @@ type Editor struct {
reader *Reader
sigs <-chan os.Signal
histories []string
store *store.Store
cmdSeq int
editorState
}
@ -62,39 +65,102 @@ type LineRead struct {
Err error
}
func (h *history) jump(i int, line string) {
h.current = i
h.line = line
}
func (ed *Editor) appendHistory(line string) {
ed.histories = append(ed.histories, line)
if ed.store != nil {
ed.store.AddCmd(line)
// TODO(xiaq): Report possible error
}
}
func lastHistory(histories []string, upto int, prefix string) (int, string) {
for i := upto - 1; i >= 0; i-- {
if strings.HasPrefix(histories[i], prefix) {
return i, histories[i]
}
}
return -1, ""
}
func firstHistory(histories []string, from int, prefix string) (int, string) {
for i := from; i < len(histories); i++ {
if strings.HasPrefix(histories[i], prefix) {
return i, histories[i]
}
}
return -1, ""
}
func (ed *Editor) prevHistory() bool {
for i := ed.history.current - 1; i >= 0; i-- {
if strings.HasPrefix(ed.histories[i], ed.history.prefix) {
ed.history.current = i
ed.history.line = ed.histories[i]
if ed.history.current > 0 {
// Session history
i, line := lastHistory(ed.histories, ed.history.current, ed.history.prefix)
if i >= 0 {
ed.history.jump(i, line)
return true
}
}
if ed.store != nil {
// Persistent history
upto := ed.cmdSeq + min(0, ed.history.current)
i, line, err := ed.store.LastCmd(upto, ed.history.prefix)
if err == nil {
ed.history.jump(i-ed.cmdSeq, line)
return true
}
}
// TODO(xiaq): Errors other than ErrNoMatchingCmd should be reported
return false
}
func (ed *Editor) nextHistory() bool {
for i := ed.history.current + 1; i < len(ed.histories); i++ {
if strings.HasPrefix(ed.histories[i], ed.history.prefix) {
ed.history.current = i
ed.history.line = ed.histories[i]
if ed.store != nil {
// Persistent history
if ed.history.current < -1 {
from := ed.cmdSeq + ed.history.current + 1
i, line, err := ed.store.FirstCmd(from, ed.history.prefix)
if err == nil {
ed.history.jump(i-ed.cmdSeq, line)
return true
}
// TODO(xiaq): Errors other than ErrNoMatchingCmd should be reported
}
}
from := max(0, ed.history.current+1)
i, line := firstHistory(ed.histories, from, ed.history.prefix)
if i >= 0 {
ed.history.jump(i, line)
return true
}
return false
}
// NewEditor creates an Editor.
func NewEditor(file *os.File, sigs <-chan os.Signal) *Editor {
func NewEditor(file *os.File, sigs <-chan os.Signal, st *store.Store) *Editor {
seq := -1
if st != nil {
var err error
seq, err = st.NextCmdSeq()
if err != nil {
// TODO(xiaq): Also report the error
seq = -1
}
}
return &Editor{
file: file,
writer: newWriter(file),
reader: NewReader(file),
sigs: sigs,
store: st,
cmdSeq: seq,
}
}

15
edit/minmax.go Normal file
View File

@ -0,0 +1,15 @@
package edit
func min(a, b int) int {
if a <= b {
return a
}
return b
}
func max(a, b int) int {
if a >= b {
return a
}
return b
}

10
main.go
View File

@ -22,7 +22,7 @@ const (
outChanLeader = "▶ "
)
func newEvaler() *eval.Evaler {
func newEvalerAndStore() (*eval.Evaler, *store.Store) {
dataDir, err := store.EnsureDataDir()
if err != nil {
fmt.Fprintln(os.Stderr, "Warning: cannot create data dir ~/.elvish")
@ -36,7 +36,7 @@ func newEvaler() *eval.Evaler {
}
}
return eval.NewEvaler(st, dataDir)
return eval.NewEvaler(st, dataDir), st
}
func printError(err error) {
@ -51,7 +51,7 @@ func printError(err error) {
// TODO(xiaq): Currently only the editor deals with signals.
func interact() {
ev := newEvaler()
ev, st := newEvalerAndStore()
datadir, err := store.EnsureDataDir()
printError(err)
if err == nil {
@ -76,7 +76,7 @@ func interact() {
sigch := make(chan os.Signal, sigchSize)
ed := edit.NewEditor(os.Stdin, sigch)
ed := edit.NewEditor(os.Stdin, sigch, st)
for {
cmdNum++
@ -111,7 +111,7 @@ func interact() {
}
func script(fname string) {
ev := newEvaler()
ev, _ := newEvalerAndStore()
err := ev.Source(fname)
printError(err)
if err != nil {