history.go 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. package readline
  2. import (
  3. "bufio"
  4. "fmt"
  5. "os"
  6. "path/filepath"
  7. "strings"
  8. "github.com/emirpasic/gods/v2/lists/arraylist"
  9. )
  10. type History struct {
  11. Enabled bool
  12. lines *arraylist.List[string]
  13. limit int
  14. pos int
  15. filename string
  16. }
  17. func NewHistory() (*History, error) {
  18. h := &History{
  19. Enabled: true,
  20. lines: arraylist.New[string](),
  21. limit: 100, // resizeme
  22. }
  23. home, err := os.UserHomeDir()
  24. if err != nil {
  25. return nil, err
  26. }
  27. path := filepath.Join(home, ".ollama", "history")
  28. if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
  29. return nil, err
  30. }
  31. h.filename = path
  32. f, err := os.OpenFile(path, os.O_CREATE|os.O_RDONLY, 0o600)
  33. if err != nil {
  34. return nil, err
  35. }
  36. defer f.Close()
  37. scanner := bufio.NewScanner(f)
  38. for scanner.Scan() {
  39. if line := strings.TrimSpace(scanner.Text()); len(line) > 0 {
  40. h.Add(line)
  41. }
  42. }
  43. return h, nil
  44. }
  45. func (h *History) Add(s string) {
  46. if latest, _ := h.lines.Get(h.Size() - 1); latest != s {
  47. h.lines.Add(s)
  48. h.Compact()
  49. _ = h.Save()
  50. }
  51. // always set position to the end
  52. h.pos = h.Size()
  53. }
  54. func (h *History) Compact() {
  55. if s := h.lines.Size(); s > h.limit {
  56. for range s - h.limit {
  57. h.lines.Remove(0)
  58. }
  59. }
  60. }
  61. func (h *History) Clear() {
  62. h.lines.Clear()
  63. }
  64. func (h *History) Prev() (line string) {
  65. if h.pos > 0 {
  66. h.pos -= 1
  67. }
  68. // return first line if at the beginning
  69. line, _ = h.lines.Get(h.pos)
  70. return line
  71. }
  72. func (h *History) Next() (line string) {
  73. if h.pos < h.lines.Size() {
  74. h.pos += 1
  75. line, _ = h.lines.Get(h.pos)
  76. }
  77. // return empty string if at the end
  78. return line
  79. }
  80. func (h *History) Size() int {
  81. return h.lines.Size()
  82. }
  83. func (h *History) Save() error {
  84. if !h.Enabled {
  85. return nil
  86. }
  87. f, err := os.CreateTemp(filepath.Dir(h.filename), "")
  88. if err != nil {
  89. return err
  90. }
  91. func() {
  92. defer f.Close()
  93. w := bufio.NewWriter(f)
  94. defer w.Flush()
  95. h.lines.Each(func(i int, line string) {
  96. fmt.Fprintln(w, line)
  97. })
  98. }()
  99. return os.Rename(f.Name(), h.filename)
  100. }