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:
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user