package fzf import ( "errors" "os" "strings" ) // History struct represents input history type History struct { path string lines []string modified map[int]string maxSize int cursor int } // NewHistory returns the pointer to a new History struct func NewHistory(path string, maxSize int) (*History, error) { fmtError := func(e error) error { if os.IsPermission(e) { return errors.New("permission denied: " + path) } return errors.New("invalid history file: " + e.Error()) } // Read history file data, err := os.ReadFile(path) if err != nil { // If it doesn't exist, check if we can create a file with the name if os.IsNotExist(err) { data = []byte{} if err := os.WriteFile(path, data, 0600); err != nil { return nil, fmtError(err) } } else { return nil, fmtError(err) } } // Split lines and limit the maximum number of lines lines := strings.Split(strings.Trim(string(data), "\n"), "\n") if len(lines[len(lines)-1]) > 0 { lines = append(lines, "") } return &History{ path: path, maxSize: maxSize, lines: lines, modified: make(map[int]string), cursor: len(lines) - 1}, nil } func (h *History) append(line string) error { // We don't append empty lines if len(line) == 0 { return nil } lines := append(h.lines[:len(h.lines)-1], line) if len(lines) > h.maxSize { lines = lines[len(lines)-h.maxSize:] } h.lines = append(lines, "") return os.WriteFile(h.path, []byte(strings.Join(h.lines, "\n")), 0600) } func (h *History) override(str string) { // You can update the history, but they're not written to the file if h.cursor == len(h.lines)-1 { h.lines[h.cursor] = str } else if h.cursor < len(h.lines)-1 { h.modified[h.cursor] = str } } func (h *History) current() string { if str, prs := h.modified[h.cursor]; prs { return str } return h.lines[h.cursor] } func (h *History) previous() string { if h.cursor > 0 { h.cursor-- } return h.current() } func (h *History) next() string { if h.cursor < len(h.lines)-1 { h.cursor++ } return h.current() }