From 42fdcc0e8f736316870339a1b1083baff06c5447 Mon Sep 17 00:00:00 2001 From: Mecaneer23 <74385377+Mecaneer23@users.noreply.github.com> Date: Sat, 22 Jun 2024 19:25:27 -0500 Subject: [PATCH] feat: migrate Positions to internally just store start and stop and generate list using range --- src/class_cursor.py | 128 +++++++++++++++++++++++++++---------------- src/class_history.py | 2 +- todo.txt | 1 - 3 files changed, 82 insertions(+), 49 deletions(-) diff --git a/src/class_cursor.py b/src/class_cursor.py index 79a6389..42d8ede 100644 --- a/src/class_cursor.py +++ b/src/class_cursor.py @@ -9,7 +9,7 @@ # from functools import wraps # from typing import Any, Callable, Iterable, TypeVar -from typing import Iterable, TypeVar +from typing import TypeVar from src.class_todo import Todos from src.get_args import UI_TYPE, UiType @@ -33,25 +33,55 @@ class _Direction(Enum): NONE = 2 -class Positions(list[int]): +class Positions: """ - Wrapper for a list of indices of positions + Store a group of consecutive ints """ - def __init__(self, iterable: Iterable[int]): - super().__init__(iterable) + @classmethod + def from_start(cls, start: int) -> "Positions": + """Construct a Positions object from a single int""" + return cls(start, start + 1) + + def __init__(self, start: int, stop: int) -> None: + self._start = start + self._stop = stop + + def get_start(self) -> int: + """Getter for start""" + return self._start + + def get_stop(self) -> int: + """Getter for stop""" + return self._stop + + def __len__(self) -> int: + return self._stop - self._start + + def __contains__(self, child: int) -> bool: + return self._start <= child < self._stop + + def as_range(self) -> range: + """Getter for Positions represented as an iterator""" + return range(self._start, self._stop) + + def raise_start(self, amount: int) -> None: + """Raise start by `amount`""" + self._start += amount + + def raise_stop(self, amount: int) -> None: + """Raise stop by `amount`""" + self._stop += amount class Cursor: """ - Store position(s) in a list using a `Positions` object - - Especially helpful for storing a cursor which might span - multiple consecutive positions + Store potentially multiple consectutive position(s) using a + `Positions` object """ def __init__(self, position: int, todos: Todos) -> None: - self._positions: Positions = Positions([position]) + self._positions: Positions = Positions.from_start(position) self._direction: _Direction = _Direction.NONE # self._todos: Todos = todos _ = todos @@ -60,28 +90,31 @@ def __len__(self) -> int: return len(self._positions) def __str__(self) -> str: - return str(self._positions[0]) + return str(self.get_first()) def __repr__(self) -> str: - return " ".join(map(str, self._positions)) + return " ".join(map(str, self._positions.as_range())) def __int__(self) -> int: - return self._positions[0] + return self.get_first() def __contains__(self, child: int) -> bool: return child in self._positions - def get(self) -> Positions: - """Return a `Positions` object holding the current cursor""" - return self._positions + def __iter__(self) -> range: + return self.get() + + def get(self) -> range: + """Return a iterable object holding the current cursor""" + return self._positions.as_range() def get_first(self) -> int: """Return the top-most selected position""" - return self._positions[0] + return self._positions.get_start() def get_last(self) -> int: """Return the bottom-most selected position""" - return self._positions[-1] + return self._positions.get_stop() - 1 # @staticmethod # def _updates_cursor( @@ -121,7 +154,7 @@ def get_last(self) -> int: def set_to(self, position: int) -> None: """Replace the entire cursor with a new single position""" - self._positions = Positions([position]) + self._positions = Positions.from_start(position) def override_passthrough(self, passthrough: T, positions: Positions) -> T: """ @@ -136,33 +169,33 @@ def single_up(self, max_len: int) -> None: if len(self._positions) == max_len: self.set_to(0) return - if min(self._positions) == 0: + if self.get_first() == 0: return - self.set_to(min(self._positions) - 1) + self.set_to(self.get_first() - 1) # while self.todos[self.get_first()].is_folded(): # self.multiselect_up() def slide_up(self) -> None: """Shift each value in the cursor up by 1""" - if min(self._positions) == 0: + if self.get_first() == 0: return - self._positions.insert(0, min(self._positions) - 1) - self._positions.pop() + self._positions.raise_start(-1) + self._positions.raise_stop(-1) def single_down(self, max_len: int) -> None: """Move a cursor with length 1 down by 1""" if len(self._positions) == max_len: - self.set_to(min(self._positions)) - if max(self._positions) >= max_len - 1: + self.set_to(self.get_first()) + if self.get_last() >= max_len - 1: return - self.set_to(max(self._positions) + 1) + self.set_to(self.get_last() + 1) def slide_down(self, max_len: int) -> None: """Shift each value in the cursor down by 1""" - if max(self._positions) >= max_len - 1: + if self.get_last() >= max_len - 1: return - self._positions.append(max(self._positions) + 1) - self._positions.pop(0) + self._positions.raise_start(1) + self._positions.raise_stop(1) def to_top(self) -> None: """Move the cursor to the top""" @@ -174,33 +207,34 @@ def to_bottom(self, len_list: int) -> None: def _select_next(self) -> None: """Extend the cursor down by 1""" - self._positions.append(max(self._positions) + 1) + self._positions.raise_stop(1) def _deselect_next(self) -> None: """Retract the cursor by 1""" if len(self._positions) > 1: - self._positions.remove(max(self._positions)) + self._positions.raise_stop(-1) def _deselect_prev(self) -> None: """Remove the first position of the cursor""" if len(self._positions) > 1: - self._positions.remove(min(self._positions)) + self._positions.raise_start(1) def _select_prev(self) -> None: """Extend the cursor up by 1""" - self._positions.insert(0, min(self._positions) - 1) + self._positions.raise_start(-1) - def get_deletable(self) -> Positions: + def get_deletable(self) -> list[int]: """ - Return a Positions object with each value + Return a list with the same length as the + current internal `positions` with each value set to the minimum position of the current Cursor """ - return Positions([min(self._positions) for _ in self._positions]) + return [self.get_first() for _ in self._positions.as_range()] def multiselect_down(self, max_len: int) -> None: """Extend the cursor down by 1""" - if max(self._positions) >= max_len - 1: + if self.get_last() >= max_len - 1: return if len(self._positions) == 1 or self._direction == _Direction.DOWN: self._select_next() @@ -210,7 +244,7 @@ def multiselect_down(self, max_len: int) -> None: def multiselect_up(self) -> None: """Extend the cursor up by 1""" - if min(self._positions) == 0 and self._direction == _Direction.UP: + if self.get_first() == 0 and self._direction == _Direction.UP: return if len(self._positions) == 1 or self._direction == _Direction.UP: self._select_prev() @@ -223,22 +257,22 @@ def multiselect_top(self) -> None: Select every position between 0 and the current top of the selection """ - for _ in range(self._positions[0], 0, -1): + for _ in range(self.get_first(), 0, -1): self.multiselect_up() def multiselect_bottom(self, max_len: int) -> None: """ Select every position between the - current top of the selection and + current bottom of the selection and the `max_len` of the list """ - for _ in range(self._positions[0], max_len): + for _ in range(self.get_last() - 1, max_len): self.multiselect_down(max_len) def _multiselect_to(self, position: int, max_len: int) -> None: """Select from current position up or down `position`""" - direction = -1 if position < self._positions[0] else 1 - for _ in range(self._positions[0], position, direction): + direction = -1 if position < self.get_first() else 1 + for _ in range(self.get_first(), position, direction): if direction == 1: self.multiselect_down(max_len) continue @@ -277,9 +311,9 @@ def relative_to( key = stdscr.getch() # alt + ... stdscr.nodelay(False) if key == Key.k: - operation(self._positions[0] - int(total), max_len) + operation(self.get_first() - int(total), max_len) elif key == Key.j: - operation(self._positions[0] + int(total), max_len) + operation(self.get_first() + int(total), max_len) elif key in (Key.g, Key.G): operation(int(total) - 1, max_len) elif key in Key.digits(): @@ -289,4 +323,4 @@ def relative_to( def multiselect_all(self, max_len: int) -> None: """Set internal positions to entirity of list""" - self._positions = Positions(range(0, max_len)) + self._positions = Positions(0, max_len) diff --git a/src/class_history.py b/src/class_history.py index 4b2db29..9521b57 100644 --- a/src/class_history.py +++ b/src/class_history.py @@ -39,7 +39,7 @@ def get(self) -> TodoList: return TodoList( Todos([Todo(line) for line in self.stored.split(self.SEPARATOR)]), - Positions(range(self.first, self.last + 1)), + Positions(self.first, self.last + 1), ) def __repr__(self) -> str: diff --git a/todo.txt b/todo.txt index e5878fd..614c403 100644 --- a/todo.txt +++ b/todo.txt @@ -14,5 +14,4 @@ - fix: acurses preserve terminal from prior to app instantiation - feat: migrate copied_todo to list of todos and add multiple line paste operation from in app copy - fix: visual glitch when len(todos) == 1 -- feat: migrate Positions to internally just store start and stop and generate list using range - feat: remove Positions and integrate functionality into Cursor class directly \ No newline at end of file