Source code for collections_undo._containers._list

from __future__ import annotations
from abc import abstractmethod

from typing import Iterable, Iterator, MutableSequence, TypeVar, SupportsIndex
from collections_undo._stack import UndoManager

_T = TypeVar("_T")


[docs]class AbstractUndoableList(MutableSequence[_T]): """ An undoable mutable sequence. Abstract Methods ---------------- - ``__getitem__(self, key) -> _T`` ... Get item at ``key``. You must return a copy of the item if the item is mutable, such as a numpy array. - ``_raw_setitem(self, key, val) -> None`` ... Set item at ``key``. - ``_raw_delitem(self, key) -> None`` ... Delete item at ``key``. - ``_raw_insert(self, index, val)`` ... Insert ``val`` at ``index``. - ``__len__(self) -> int`` ... Get length of list. - ``__iter__(self) -> Iterator`` ... Iterate over list. """ _mgr = UndoManager() def __repr__(self) -> str: clsname = type(self).__name__ return f"{clsname}({list(self)!r})" @abstractmethod def __getitem__(self, key: SupportsIndex) -> _T: ... @abstractmethod def __len__(self) -> int: ... @abstractmethod def __iter__(self) -> Iterator[_T]: ... @abstractmethod def _raw_setitem(self, key: SupportsIndex, val: _T) -> None: ... @abstractmethod def _raw_delitem(self, key: SupportsIndex) -> None: ... @abstractmethod def _raw_insert(self, index: int, val: _T) -> None: ... def __setitem__(self, key: SupportsIndex, val: _T): if isinstance(key, slice): key = slice(*key.indices(len(self))) elif key < 0: key += len(self) return self._setitem(key, val) @_mgr.interface def _setitem(self, key, val): self._raw_setitem(key, val) @_setitem.server def _setitem(self, key, val): _val = self[key] if isinstance(key, slice): _val = list(_val) return (key, _val), {} def __delitem__(self, key) -> None: if isinstance(key, slice): key = slice(*key.indices(len(self))) elif key < 0: key += len(self) return self._delitem_command(key, self[key]) @_mgr.undoable def _delitem_command(self, key, val): self._raw_delitem(key) @_delitem_command.undo_def def _delitem_command(self, key, val): if isinstance(key, slice): for i, idx in enumerate(range(key.start, key.stop, key.step)): self._raw_insert(idx, val[i]) else: self._raw_insert(key, val) @_mgr.undoable def insert(self, index: int, val: _T): self._raw_insert(index, val)
[docs] @insert.undo_def def insert(self, index: int, val: _T): self._raw_delitem(index)
# reimplemented methods
[docs] def extend(self, values: Iterable[_T]) -> None: """Extend the list with given values.""" return self._extend(values)
@_mgr.undoable def _extend(self, values): for val in values: self._raw_insert(len(self), val) @_extend.undo_def def _extend(self, values): [self._raw_delitem(-1) for i in reversed(range(len(values)))]
[docs] def clear(self) -> None: """Clear the list.""" return self._clear(list(self))
@_mgr.undoable def _clear(self, data: list[_T]): [self._raw_delitem(i) for i in reversed(range(len(self)))] @_clear.undo_def def _clear(self, data: list[_T]): self._extend._call_raw(data)
[docs] def reverse(self) -> None: n = len(self) self[:] = [self[i] for i in range(n - 1, -1, -1)]
[docs] def undo(self): """Undo the last operation.""" return self._mgr.undo()
[docs] def redo(self): """Redo the last undo operation.""" return self._mgr.redo()
[docs]class UndoableList(AbstractUndoableList[_T]): def __init__(self, iterable=(), /): self._list: list[_T] = list(iterable) def __len__(self) -> int: """Length of list.""" return len(self._list) def __getitem__(self, i): return self._list[i] def _raw_setitem(self, key, val: _T) -> None: self._list[key] = val def _raw_delitem(self, key) -> None: del self._list[key] def __iter__(self) -> Iterator[_T]: return iter(self._list) def _raw_insert(self, index: int, val: _T): self._list.insert(index, val)
[docs] def sort(self, *, key=None, reverse=False): self[:] = sorted(self._list, key=key, reverse=reverse)