Persist undo/redo history across runs with a 20-entry cap.

Serialize create/update/delete commands to DataDir/history.json and reload them on startup via OpenHistory.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Skruecken
2026-10-19 10:25:07 +02:00
co-authored by Cursor
parent 24c1e146ec
commit 73216ec4ae
4 changed files with 298 additions and 9 deletions
+33 -8
View File
@@ -4,6 +4,8 @@ import (
"context"
"fmt"
"sync"
"task/internal/ticket"
)
// Executor runs commands and supports undo/redo of successful ones.
@@ -17,16 +19,36 @@ type Executor interface {
LastRedoName() string
}
// History is an in-memory undo/redo stack for Command values.
// History is an undo/redo stack for Command values, optionally persisted to disk.
type History struct {
mu sync.Mutex
undoS []Command
redoS []Command
path string
store ticket.Store
max int
}
// NewHistory creates an empty command history.
// NewHistory creates an empty in-memory command history (cap MaxHistory).
func NewHistory() *History {
return &History{}
return &History{max: MaxHistory}
}
// OpenHistory loads a persisted history from path (DataDir/history.json) or
// starts empty if the file is missing. Subsequent Do/Undo/Redo write back.
func OpenHistory(path string, store ticket.Store) (*History, error) {
h := &History{
path: path,
store: store,
max: MaxHistory,
}
undo, redo, err := loadHistoryFile(path, store)
if err != nil {
return nil, err
}
h.undoS = undo
h.redoS = redo
return h, nil
}
// Do executes cmd and pushes it onto the undo stack. The redo stack is cleared.
@@ -37,8 +59,9 @@ func (h *History) Do(cmd Command) error {
h.mu.Lock()
defer h.mu.Unlock()
h.undoS = append(h.undoS, cmd)
h.undoS = trimStack(h.undoS, h.max)
h.redoS = nil
return nil
return h.persistLocked()
}
// Undo reverses the last successful command.
@@ -60,9 +83,10 @@ func (h *History) Undo() error {
}
h.mu.Lock()
defer h.mu.Unlock()
h.redoS = append(h.redoS, cmd)
h.mu.Unlock()
return nil
h.redoS = trimStack(h.redoS, h.max)
return h.persistLocked()
}
// Redo re-applies the last undone command.
@@ -84,9 +108,10 @@ func (h *History) Redo() error {
}
h.mu.Lock()
defer h.mu.Unlock()
h.undoS = append(h.undoS, cmd)
h.mu.Unlock()
return nil
h.undoS = trimStack(h.undoS, h.max)
return h.persistLocked()
}
// CanUndo reports whether Undo would succeed.