summary refs log blame commit diff stats
path: root/commands/history.go
blob: 77bb15530a43c2cacab0edd5a9ded492da63a522 (plain) (tree)





























































                                                                 
package commands

type cmdHistory struct {
	// rolling buffer of prior commands
	//
	// most recent command is at the end of the list,
	// least recent is index 0
	cmdList []string

	// current placement in list
	current int
}

// number of commands to keep in history
const cmdLimit = 1000

// CmdHistory is the history of executed commands
var CmdHistory = cmdHistory{}

func (h *cmdHistory) Add(cmd string) {
	// if we're at cap, cut off the first element
	if len(h.cmdList) >= cmdLimit {
		h.cmdList = h.cmdList[1:]
	}

	h.cmdList = append(h.cmdList, cmd)

	// whenever we add a new command, reset the current
	// pointer to the "beginning" of the list
	h.Reset()
}

// Prev returns the previous command in history.
// Since the list is reverse-order, this will return elements
// increasingly towards index 0.
func (h *cmdHistory) Prev() string {
	if h.current <= 0 || len(h.cmdList) == 0 {
		h.current = -1
		return "(Already at beginning)"
	}
	h.current--

	return h.cmdList[h.current]
}

// Next returns the next command in history.
// Since the list is reverse-order, this will return elements
// increasingly towards index len(cmdList).
func (h *cmdHistory) Next() string {
	if h.current >= len(h.cmdList)-1 || len(h.cmdList) == 0 {
		h.current = len(h.cmdList)
		return "(Already at end)"
	}
	h.current++

	return h.cmdList[h.current]
}

// Reset the current pointer to the beginning of history.
func (h *cmdHistory) Reset() {
	h.current = len(h.cmdList)
}
span>*Aerc, conf *config.AercConfig, acct *config.AccountConfig, logger *log.Logger, host TabHost) *AccountView { grid := ui.NewGrid().Rows([]ui.GridSpec{ {ui.SIZE_WEIGHT, 1}, }).Columns([]ui.GridSpec{ {ui.SIZE_EXACT, conf.Ui.SidebarWidth}, {ui.SIZE_WEIGHT, 1}, }) worker, err := worker.NewWorker(acct.Source, logger) if err != nil { host.SetStatus(fmt.Sprintf("%s: %s", acct.Name, err)). Color(tcell.ColorDefault, tcell.ColorRed) return &AccountView{ acct: acct, aerc: aerc, grid: grid, host: host, logger: logger, } } dirlist := NewDirectoryList(acct, &conf.Ui, logger, worker) if conf.Ui.SidebarWidth > 0 { grid.AddChild(ui.NewBordered(dirlist, ui.BORDER_RIGHT)) } msglist := NewMessageList(conf, logger, aerc) grid.AddChild(msglist).At(0, 1) view := &AccountView{ acct: acct, aerc: aerc, conf: conf, dirlist: dirlist, grid: grid, host: host, logger: logger, msglist: msglist, worker: worker, } go worker.Backend.Run() worker.PostAction(&types.Configure{Config: acct}, nil) worker.PostAction(&types.Connect{}, view.connected) host.SetStatus("Connecting...") return view } func (acct *AccountView) Tick() bool { if acct.worker == nil { return false } select { case msg := <-acct.worker.Messages: msg = acct.worker.ProcessMessage(msg) acct.onMessage(msg) return true default: return false } } func (acct *AccountView) AccountConfig() *config.AccountConfig { return acct.acct } func (acct *AccountView) Worker() *types.Worker { return acct.worker } func (acct *AccountView) Logger() *log.Logger { return acct.logger } func (acct *AccountView) Name() string { return acct.acct.Name } func (acct *AccountView) Children() []ui.Drawable { return acct.grid.Children() } func (acct *AccountView) OnInvalidate(onInvalidate func(d ui.Drawable)) { acct.grid.OnInvalidate(func(_ ui.Drawable) { onInvalidate(acct) }) } func (acct *AccountView) Invalidate() { acct.grid.Invalidate() } func (acct *AccountView) Draw(ctx *ui.Context) { acct.grid.Draw(ctx) } func (acct *AccountView) MouseEvent(localX int, localY int, event tcell.Event) { acct.grid.MouseEvent(localX, localY, event) } func (acct *AccountView) Focus(focus bool) { // TODO: Unfocus children I guess } func (acct *AccountView) connected(msg types.WorkerMessage) { switch msg.(type) { case *types.Done: acct.host.SetStatus("Listing mailboxes...") acct.logger.Println("Listing mailboxes...") acct.dirlist.UpdateList(func(dirs []string) { var dir string for _, _dir := range dirs { if _dir == acct.acct.Default { dir = _dir break } } if dir == "" && len(dirs) > 0 { dir = dirs[0] } if dir != "" { acct.dirlist.Select(dir) } acct.msglist.SetInitDone() acct.logger.Println("Connected.") acct.host.SetStatus("Connected.") }) } } func (acct *AccountView) Directories() *DirectoryList { return acct.dirlist } func (acct *AccountView) Messages() *MessageList { return acct.msglist } func (acct *AccountView) Store() *lib.MessageStore { if acct.msglist == nil { return nil } return acct.msglist.Store() } func (acct *AccountView) SelectedAccount() *AccountView { return acct } func (acct *AccountView) SelectedMessage() (*models.MessageInfo, error) { if len(acct.msglist.Store().Uids()) == 0 { return nil, errors.New("no message selected") } msg := acct.msglist.Selected() if msg == nil { return nil, errors.New("message not loaded") } return msg, nil } func (acct *AccountView) SelectedMessagePart() *PartInfo { return nil } func (acct *AccountView) onMessage(msg types.WorkerMessage) { switch msg := msg.(type) { case *types.Done: switch msg.InResponseTo().(type) { case *types.OpenDirectory: if store, ok := acct.dirlist.SelectedMsgStore(); ok { // If we've opened this dir before, we can re-render it from // memory while we wait for the update and the UI feels // snappier. If not, we'll unset the store and show the spinner // while we download the UID list. acct.msglist.SetStore(store) } else { acct.msglist.SetStore(nil) } case *types.CreateDirectory: acct.dirlist.UpdateList(nil) } case *types.DirectoryInfo: if store, ok := acct.dirlist.MsgStore(msg.Info.Name); ok { store.Update(msg) } else { store = lib.NewMessageStore(acct.worker, msg.Info, acct.getSortCriteria(), func(msg *models.MessageInfo) { acct.conf.Triggers.ExecNewEmail(acct.acct, acct.conf, msg) }, func() { if acct.conf.Ui.NewMessageBell { acct.host.Beep() } }) acct.dirlist.SetMsgStore(msg.Info.Name, store) store.OnUpdate(func(_ *lib.MessageStore) { store.OnUpdate(nil) acct.msglist.SetStore(store) }) } case *types.DirectoryContents: if store, ok := acct.dirlist.SelectedMsgStore(); ok { store.Update(msg) } case *types.FullMessage: if store, ok := acct.dirlist.SelectedMsgStore(); ok { store.Update(msg) } case *types.MessageInfo: if store, ok := acct.dirlist.SelectedMsgStore(); ok { store.Update(msg) } case *types.MessagesDeleted: if store, ok := acct.dirlist.SelectedMsgStore(); ok { store.Update(msg) } case *types.Error: acct.logger.Printf("%v", msg.Error) acct.host.SetStatus(fmt.Sprintf("%v", msg.Error)). Color(tcell.ColorDefault, tcell.ColorRed) } } func (acct *AccountView) getSortCriteria() []*types.SortCriterion { if len(acct.conf.Ui.Sort) == 0 { return nil } criteria, err := sort.GetSortCriteria(acct.conf.Ui.Sort) if err != nil { acct.aerc.PushError(" ui.sort: " + err.Error()) return nil } return criteria }