diff --git a/internal/action/infopane.go b/internal/action/infopane.go index 7fad9ad6..e1342ad9 100644 --- a/internal/action/infopane.go +++ b/internal/action/infopane.go @@ -108,7 +108,11 @@ func (h *InfoPane) HandleEvent(event tcell.Event) { if done && h.HasPrompt && !hasYN { resp := string(h.LineBytes(0)) hist := h.History[h.PromptType] - hist[h.HistoryNum] = resp + if resp != hist[h.HistoryNum] { + h.HistoryNum = len(hist) - 1 + hist[h.HistoryNum] = resp + h.HistorySearch = false + } if h.EventCallback != nil { h.EventCallback(resp) } @@ -155,6 +159,18 @@ func (h *InfoPane) HistoryDown() { h.DownHistory(h.History[h.PromptType]) } +// HistorySearchUp fetches the previous history item beginning with the text +// in the infobuffer before cursor +func (h *InfoPane) HistorySearchUp() { + h.SearchUpHistory(h.History[h.PromptType]) +} + +// HistorySearchDown fetches the next history item beginning with the text +// in the infobuffer before cursor +func (h *InfoPane) HistorySearchDown() { + h.SearchDownHistory(h.History[h.PromptType]) +} + // Autocomplete begins autocompletion func (h *InfoPane) CommandComplete() { b := h.Buf @@ -198,9 +214,11 @@ func (h *InfoPane) AbortCommand() { // InfoKeyActions contains the list of all possible key actions the infopane could execute var InfoKeyActions = map[string]InfoKeyAction{ - "HistoryUp": (*InfoPane).HistoryUp, - "HistoryDown": (*InfoPane).HistoryDown, - "CommandComplete": (*InfoPane).CommandComplete, - "ExecuteCommand": (*InfoPane).ExecuteCommand, - "AbortCommand": (*InfoPane).AbortCommand, + "HistoryUp": (*InfoPane).HistoryUp, + "HistoryDown": (*InfoPane).HistoryDown, + "HistorySearchUp": (*InfoPane).HistorySearchUp, + "HistorySearchDown": (*InfoPane).HistorySearchDown, + "CommandComplete": (*InfoPane).CommandComplete, + "ExecuteCommand": (*InfoPane).ExecuteCommand, + "AbortCommand": (*InfoPane).AbortCommand, } diff --git a/internal/info/history.go b/internal/info/history.go index 722a22f7..a09a58cf 100644 --- a/internal/info/history.go +++ b/internal/info/history.go @@ -4,8 +4,10 @@ import ( "encoding/gob" "os" "path/filepath" + "strings" "github.com/zyedidia/micro/v2/internal/config" + "github.com/zyedidia/micro/v2/internal/util" ) // LoadHistory attempts to load user history from configDir/buffers/history @@ -102,3 +104,51 @@ func (i *InfoBuf) DownHistory(history []string) { i.Buffer.GetActiveCursor().GotoLoc(i.End()) } } + +// SearchUpHistory fetches the previous item in the history +// beginning with the text in the infobuffer before cursor +func (i *InfoBuf) SearchUpHistory(history []string) { + if i.HistoryNum > 0 && i.HasPrompt && !i.HasYN { + i.searchHistory(history, false) + } +} + +// SearchDownHistory fetches the next item in the history +// beginning with the text in the infobuffer before cursor +func (i *InfoBuf) SearchDownHistory(history []string) { + if i.HistoryNum < len(history)-1 && i.HasPrompt && !i.HasYN { + i.searchHistory(history, true) + } +} + +func (i *InfoBuf) searchHistory(history []string, down bool) { + line := string(i.LineBytes(0)) + c := i.Buffer.GetActiveCursor() + + if !i.HistorySearch || !strings.HasPrefix(line, i.HistorySearchPrefix) { + i.HistorySearch = true + i.HistorySearchPrefix = util.SliceStartStr(line, c.X) + } + + found := -1 + if down { + for j := i.HistoryNum + 1; j < len(history); j++ { + if strings.HasPrefix(history[j], i.HistorySearchPrefix) { + found = j + break + } + } + } else { + for j := i.HistoryNum - 1; j >= 0; j-- { + if strings.HasPrefix(history[j], i.HistorySearchPrefix) { + found = j + break + } + } + } + if found != -1 { + i.HistoryNum = found + i.Replace(i.Start(), i.End(), history[found]) + c.GotoLoc(i.End()) + } +} diff --git a/internal/info/infobuffer.go b/internal/info/infobuffer.go index e88701a4..8f8d7251 100644 --- a/internal/info/infobuffer.go +++ b/internal/info/infobuffer.go @@ -25,6 +25,10 @@ type InfoBuf struct { // It's a map of history type -> history array History map[string][]string HistoryNum int + // HistorySearch indicates whether we are searching for history items + // beginning with HistorySearchPrefix + HistorySearch bool + HistorySearchPrefix string // Is the current message a message from the gutter HasGutter bool @@ -102,6 +106,7 @@ func (i *InfoBuf) Prompt(prompt string, msg string, ptype string, eventcb func(s i.History[ptype] = append(i.History[ptype], "") } i.HistoryNum = len(i.History[ptype]) - 1 + i.HistorySearch = false i.PromptType = ptype i.Msg = prompt