ollama/readline/history.go
Michael Yang cb40d60469 chore: upgrade to gods v2
gods v2 uses go generics rather than interfaces which simplifies the
code considerably
2024-12-21 00:05:16 -08:00

152 lines
2.2 KiB
Go

package readline
import (
"bufio"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"github.com/emirpasic/gods/v2/lists/arraylist"
)
type History struct {
Buf *arraylist.List[string]
Autosave bool
Pos int
Limit int
Filename string
Enabled bool
}
func NewHistory() (*History, error) {
h := &History{
Buf: arraylist.New[string](),
Limit: 100, // resizeme
Autosave: true,
Enabled: true,
}
err := h.Init()
if err != nil {
return nil, err
}
return h, nil
}
func (h *History) Init() error {
home, err := os.UserHomeDir()
if err != nil {
return err
}
path := filepath.Join(home, ".ollama", "history")
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
return err
}
h.Filename = path
f, err := os.OpenFile(path, os.O_CREATE|os.O_RDONLY, 0o600)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return nil
}
return err
}
defer f.Close()
r := bufio.NewReader(f)
for {
line, err := r.ReadString('\n')
if err != nil {
if errors.Is(err, io.EOF) {
break
}
return err
}
line = strings.TrimSpace(line)
if len(line) == 0 {
continue
}
h.Add(line)
}
return nil
}
func (h *History) Add(s string) {
h.Buf.Add(s)
h.Compact()
h.Pos = h.Size()
if h.Autosave {
_ = h.Save()
}
}
func (h *History) Compact() {
s := h.Buf.Size()
if s > h.Limit {
for range s - h.Limit {
h.Buf.Remove(0)
}
}
}
func (h *History) Clear() {
h.Buf.Clear()
}
func (h *History) Prev() (line string) {
if h.Pos > 0 {
h.Pos -= 1
}
line, _ = h.Buf.Get(h.Pos)
return line
}
func (h *History) Next() (line string) {
if h.Pos < h.Buf.Size() {
h.Pos += 1
line, _ = h.Buf.Get(h.Pos)
}
return line
}
func (h *History) Size() int {
return h.Buf.Size()
}
func (h *History) Save() error {
if !h.Enabled {
return nil
}
tmpFile := h.Filename + ".tmp"
f, err := os.OpenFile(tmpFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC|os.O_APPEND, 0o600)
if err != nil {
return err
}
defer f.Close()
buf := bufio.NewWriter(f)
for cnt := range h.Size() {
line, _ := h.Buf.Get(cnt)
fmt.Fprintln(buf, line)
}
buf.Flush()
f.Close()
if err = os.Rename(tmpFile, h.Filename); err != nil {
return err
}
return nil
}