feat(history): Implement generic HistoryManager and unit tests
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
import typing
|
||||
import time
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
@dataclass
|
||||
class HistoryEntry:
|
||||
state: typing.Any
|
||||
description: str
|
||||
timestamp: float = field(default_factory=lambda: time.time())
|
||||
|
||||
class HistoryManager:
|
||||
def __init__(self, max_capacity: int = 100):
|
||||
self.max_capacity = max_capacity
|
||||
self._undo_stack: typing.List[HistoryEntry] = []
|
||||
self._redo_stack: typing.List[HistoryEntry] = []
|
||||
|
||||
def push(self, state: typing.Any, description: str) -> None:
|
||||
"""
|
||||
Pushes a new state to the undo stack and clears the redo stack.
|
||||
If the undo stack exceeds max_capacity, the oldest state is removed.
|
||||
"""
|
||||
entry = HistoryEntry(state=state, description=description)
|
||||
self._undo_stack.append(entry)
|
||||
self._redo_stack.clear()
|
||||
if len(self._undo_stack) > self.max_capacity:
|
||||
self._undo_stack.pop(0)
|
||||
|
||||
def undo(self, current_state: typing.Any, current_description: str = "Current State") -> typing.Optional[HistoryEntry]:
|
||||
"""
|
||||
Undoes the last action by moving the current_state to the redo stack
|
||||
and returning the top of the undo stack.
|
||||
"""
|
||||
if not self._undo_stack:
|
||||
return None
|
||||
|
||||
redo_entry = HistoryEntry(state=current_state, description=current_description)
|
||||
self._redo_stack.append(redo_entry)
|
||||
return self._undo_stack.pop()
|
||||
|
||||
def redo(self, current_state: typing.Any, current_description: str = "Current State") -> typing.Optional[HistoryEntry]:
|
||||
"""
|
||||
Redoes the last undone action by moving the current_state to the undo stack
|
||||
and returning the top of the redo stack.
|
||||
"""
|
||||
if not self._redo_stack:
|
||||
return None
|
||||
|
||||
undo_entry = HistoryEntry(state=current_state, description=current_description)
|
||||
self._undo_stack.append(undo_entry)
|
||||
return self._redo_stack.pop()
|
||||
|
||||
@property
|
||||
def can_undo(self) -> bool:
|
||||
return len(self._undo_stack) > 0
|
||||
|
||||
@property
|
||||
def can_redo(self) -> bool:
|
||||
return len(self._redo_stack) > 0
|
||||
|
||||
def get_history(self) -> typing.List[typing.Dict[str, typing.Any]]:
|
||||
"""Returns a list of descriptions and timestamps for the undo stack."""
|
||||
return [
|
||||
{"description": e.description, "timestamp": e.timestamp}
|
||||
for e in self._undo_stack
|
||||
]
|
||||
Reference in New Issue
Block a user