2019-01-01 03:07:01 +00:00
|
|
|
package info
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"encoding/gob"
|
|
|
|
|
"os"
|
2020-02-11 18:09:17 +00:00
|
|
|
"path/filepath"
|
2023-07-08 21:00:22 +00:00
|
|
|
"strings"
|
2019-01-01 03:07:01 +00:00
|
|
|
|
2020-05-04 14:16:15 +00:00
|
|
|
"github.com/zyedidia/micro/v2/internal/config"
|
2023-07-08 21:00:22 +00:00
|
|
|
"github.com/zyedidia/micro/v2/internal/util"
|
2019-01-01 03:07:01 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// LoadHistory attempts to load user history from configDir/buffers/history
|
|
|
|
|
// into the history map
|
|
|
|
|
// The savehistory option must be on
|
2019-01-02 03:36:12 +00:00
|
|
|
func (i *InfoBuf) LoadHistory() {
|
2019-01-01 03:07:01 +00:00
|
|
|
if config.GetGlobalOption("savehistory").(bool) {
|
2020-02-11 18:09:17 +00:00
|
|
|
file, err := os.Open(filepath.Join(config.ConfigDir, "buffers", "history"))
|
2019-01-01 03:07:01 +00:00
|
|
|
var decodedMap map[string][]string
|
|
|
|
|
if err == nil {
|
2020-09-16 04:08:01 +00:00
|
|
|
defer file.Close()
|
2019-01-01 03:07:01 +00:00
|
|
|
decoder := gob.NewDecoder(file)
|
|
|
|
|
err = decoder.Decode(&decodedMap)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
i.Error("Error loading history:", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if decodedMap != nil {
|
|
|
|
|
i.History = decodedMap
|
|
|
|
|
} else {
|
|
|
|
|
i.History = make(map[string][]string)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
i.History = make(map[string][]string)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SaveHistory saves the user's command history to configDir/buffers/history
|
|
|
|
|
// only if the savehistory option is on
|
2019-01-02 03:36:12 +00:00
|
|
|
func (i *InfoBuf) SaveHistory() {
|
2019-01-01 03:07:01 +00:00
|
|
|
if config.GetGlobalOption("savehistory").(bool) {
|
|
|
|
|
// Don't save history past 100
|
|
|
|
|
for k, v := range i.History {
|
|
|
|
|
if len(v) > 100 {
|
|
|
|
|
i.History[k] = v[len(i.History[k])-100:]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-11 18:09:17 +00:00
|
|
|
file, err := os.Create(filepath.Join(config.ConfigDir, "buffers", "history"))
|
2019-01-01 03:07:01 +00:00
|
|
|
if err == nil {
|
2020-09-16 04:08:01 +00:00
|
|
|
defer file.Close()
|
2019-01-01 03:07:01 +00:00
|
|
|
encoder := gob.NewEncoder(file)
|
|
|
|
|
|
|
|
|
|
err = encoder.Encode(i.History)
|
|
|
|
|
if err != nil {
|
|
|
|
|
i.Error("Error saving history:", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-01-03 22:07:28 +00:00
|
|
|
|
2020-08-23 19:47:14 +00:00
|
|
|
// AddToHistory adds a new item to the history for the prompt type `ptype`.
|
|
|
|
|
// This function is not used by micro itself. It is useful for plugins
|
|
|
|
|
// which add their own items to the history, bypassing the infobar command line.
|
|
|
|
|
func (i *InfoBuf) AddToHistory(ptype string, item string) {
|
|
|
|
|
if i.HasPrompt && i.PromptType == ptype {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if _, ok := i.History[ptype]; !ok {
|
|
|
|
|
i.History[ptype] = []string{item}
|
|
|
|
|
} else {
|
|
|
|
|
i.History[ptype] = append(i.History[ptype], item)
|
|
|
|
|
|
|
|
|
|
// avoid duplicates
|
|
|
|
|
h := i.History[ptype]
|
|
|
|
|
for j := len(h) - 2; j >= 0; j-- {
|
|
|
|
|
if h[j] == h[len(h)-1] {
|
|
|
|
|
i.History[ptype] = append(h[:j], h[j+1:]...)
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-03 22:07:28 +00:00
|
|
|
// UpHistory fetches the previous item in the history
|
|
|
|
|
func (i *InfoBuf) UpHistory(history []string) {
|
2019-06-17 15:58:39 +00:00
|
|
|
if i.HistoryNum > 0 && i.HasPrompt && !i.HasYN {
|
2019-01-03 22:07:28 +00:00
|
|
|
i.HistoryNum--
|
2019-01-17 03:32:33 +00:00
|
|
|
i.Replace(i.Start(), i.End(), history[i.HistoryNum])
|
2019-01-03 22:07:28 +00:00
|
|
|
i.Buffer.GetActiveCursor().GotoLoc(i.End())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// DownHistory fetches the next item in the history
|
|
|
|
|
func (i *InfoBuf) DownHistory(history []string) {
|
2019-06-17 15:58:39 +00:00
|
|
|
if i.HistoryNum < len(history)-1 && i.HasPrompt && !i.HasYN {
|
2019-01-03 22:07:28 +00:00
|
|
|
i.HistoryNum++
|
2019-01-17 03:32:33 +00:00
|
|
|
i.Replace(i.Start(), i.End(), history[i.HistoryNum])
|
2019-01-03 22:07:28 +00:00
|
|
|
i.Buffer.GetActiveCursor().GotoLoc(i.End())
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-07-08 21:00:22 +00:00
|
|
|
|
|
|
|
|
// 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())
|
|
|
|
|
}
|
|
|
|
|
}
|