home · contact · privacy
Explicate which indices are used negatively and which positively.
authorChristian Heller <c.heller@plomlompom.de>
Mon, 29 Sep 2025 04:36:01 +0000 (06:36 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Mon, 29 Sep 2025 04:36:01 +0000 (06:36 +0200)
src/ircplom/tui_base.py

index 2ef81c777992716744ebdd1aa586d4f8840a561f..3414a7d8d4479f174952f9fb8dc52420901821a2 100644 (file)
@@ -84,7 +84,7 @@ class _Widget(ABC):
 
 
 class _ScrollableWidget(_Widget):
-    _history_idx: int
+    _history_idx_neg: int
 
     def __init__(self, write: Callable[..., None], **kwargs) -> None:
         super().__init__(**kwargs)
@@ -112,12 +112,12 @@ class _HistoryWidget(_ScrollableWidget):
     def __init__(self, wrap: Callable[[str], list[str]], **kwargs) -> None:
         super().__init__(**kwargs)
         self._wrap = wrap
-        self._wrapped_idx = self._history_idx = -1
+        self._wrapped_idx_neg = self._history_idx_neg = -1
         self._wrapped: list[tuple[Optional[int], str]] = []
 
-    def _add_wrapped(self, idx_original, line) -> int:
+    def _add_wrapped(self, idx_pos_original, line) -> int:
         wrapped_lines = self._wrap(line)
-        self._wrapped += [(idx_original, line) for line in wrapped_lines]
+        self._wrapped += [(idx_pos_original, line) for line in wrapped_lines]
         return len(wrapped_lines)
 
     def set_geometry(self, sizes: _YX) -> None:
@@ -127,45 +127,47 @@ class _HistoryWidget(_ScrollableWidget):
             self._wrapped.clear()
             self._wrapped += [(None, '')] * self._sizes.y
             if self._history:
-                for idx_history, line in enumerate(self._history):
-                    self._add_wrapped(idx_history, line)
+                for idx_pos_history, line in enumerate(self._history):
+                    self._add_wrapped(idx_pos_history, line)
                 wrapped_lines_for_history_idx = [
                         t for t in self._wrapped
-                        if t[0] == len(self._history) + self._history_idx]
-                idx_their_last = self._wrapped.index(
+                        if t[0] == len(self._history) + self._history_idx_neg]
+                # ensure that of the full line identified by ._history_idx_neg,
+                # ._wrapped_idx_neg point to the lowest of its wrap parts
+                idx_pos_their_last = self._wrapped.index(
                         wrapped_lines_for_history_idx[-1])
-                self._wrapped_idx = idx_their_last - len(self._wrapped)
+                self._wrapped_idx_neg = idx_pos_their_last - len(self._wrapped)
 
     def append(self, to_append: str) -> None:
         super().append(to_append)
         self.taint()
-        if self._history_idx < -1:
-            self._history_idx -= 1
+        if self._history_idx_neg < -1:
+            self._history_idx_neg -= 1
         if self._drawable:
             n_wrapped_lines = self._add_wrapped(len(self._history)
                                                 - 1, to_append)
-            if self._wrapped_idx < -1:
-                self._wrapped_idx -= n_wrapped_lines
+            if self._wrapped_idx_neg < -1:
+                self._wrapped_idx_neg -= n_wrapped_lines
 
     def _draw(self) -> None:
-        start_idx = self._wrapped_idx - self._sizes.y + 1
-        end_idx = self._wrapped_idx
-        idx_bookmark = ([idx for idx, t in enumerate(self._wrapped)
-                         if t[0] == self._bookmark]+[0])[0]
-        wrapped = (self._wrapped[:idx_bookmark+1]
+        start_idx_neg = self._wrapped_idx_neg - self._sizes.y + 1
+        end_idx_neg = self._wrapped_idx_neg
+        idx_pos_bookmark = ([idx_pos for idx_pos, t in enumerate(self._wrapped)
+                             if t[0] == self._bookmark]+[0])[0]
+        wrapped = (self._wrapped[:idx_pos_bookmark+1]
                    + [(0, '-'*self._sizes.x)]
-                   + self._wrapped[idx_bookmark+1:])
-        to_write = [t[1] for t in wrapped[start_idx:end_idx]]
-        if self._wrapped_idx < -1:
-            scroll_info = f'vvv [{(-1) * self._wrapped_idx}] '
+                   + self._wrapped[idx_pos_bookmark+1:])
+        to_write = [t[1] for t in wrapped[start_idx_neg:end_idx_neg]]
+        if self._wrapped_idx_neg < -1:
+            scroll_info = f'vvv [{(-1) * self._wrapped_idx_neg}] '
             scroll_info += 'v' * (self._sizes.x - len(scroll_info))
             to_write += [scroll_info]
         else:
-            to_write += [wrapped[self._wrapped_idx][1]]
+            to_write += [wrapped[self._wrapped_idx_neg][1]]
         for i, line in enumerate(to_write):
             self._write(line, i)
-        hist_idx = self._wrapped[end_idx][0]
-        self._last_read = max(self._last_read, hist_idx or 0)
+        hist_idx_pos = self._wrapped[end_idx_neg][0]
+        self._last_read = max(self._last_read, hist_idx_pos or 0)
 
     def bookmark(self) -> None:
         'Store to what most recent line we have (been) scrolled.'
@@ -180,21 +182,21 @@ class _HistoryWidget(_ScrollableWidget):
         super()._scroll(up)
         if self._drawable:
             if up:
-                self._wrapped_idx = max(
+                self._wrapped_idx_neg = max(
                         self._sizes.y + 1 - len(self._wrapped),
-                        self._wrapped_idx - self._y_pgscroll)
+                        self._wrapped_idx_neg - self._y_pgscroll)
             else:
-                self._wrapped_idx = min(
-                        -1, self._wrapped_idx + self._y_pgscroll)
-            history_idx_to_wrapped_idx = self._wrapped[self._wrapped_idx][0]
-            if history_idx_to_wrapped_idx is not None:
-                self._history_idx = history_idx_to_wrapped_idx\
+                self._wrapped_idx_neg = min(
+                        -1, self._wrapped_idx_neg + self._y_pgscroll)
+            hist_idx_to_wrapped_idx = self._wrapped[self._wrapped_idx_neg][0]
+            if hist_idx_to_wrapped_idx is not None:
+                self._history_idx_neg = hist_idx_to_wrapped_idx\
                         - len(self._history)
 
 
 class PromptWidget(_ScrollableWidget):
     'Manages/displays keyboard input field.'
-    _history_idx: int = 0
+    _history_idx_neg: int = 0
     _input_buffer_unsafe: str
     _cursor_x: int
 
@@ -245,19 +247,19 @@ class PromptWidget(_ScrollableWidget):
 
     def _scroll(self, up: bool = True) -> None:
         super()._scroll(up)
-        if up and -(self._history_idx) < len(self._history):
-            if self._history_idx == 0 and self._input_buffer:
+        if up and -(self._history_idx_neg) < len(self._history):
+            if self._history_idx_neg == 0 and self._input_buffer:
                 self._archive_prompt()
-                self._history_idx -= 1
-            self._history_idx -= 1
-            self._reset_buffer(self._history[self._history_idx])
+                self._history_idx_neg -= 1
+            self._history_idx_neg -= 1
+            self._reset_buffer(self._history[self._history_idx_neg])
         elif not up:
-            if self._history_idx < 0:
-                self._history_idx += 1
-                if self._history_idx == 0:
+            if self._history_idx_neg < 0:
+                self._history_idx_neg += 1
+                if self._history_idx_neg == 0:
                     self._reset_buffer('')
                 else:
-                    self._reset_buffer(self._history[self._history_idx])
+                    self._reset_buffer(self._history[self._history_idx_neg])
             elif self._input_buffer:
                 self._archive_prompt()
 
@@ -267,7 +269,7 @@ class PromptWidget(_ScrollableWidget):
         self._input_buffer = (self._input_buffer[:self._cursor_x - 1]
                               + to_insert
                               + self._input_buffer[self._cursor_x - 1:])
-        self._history_idx = 0
+        self._history_idx_neg = 0
 
     def cmd__backspace(self) -> None:
         'Truncate current content by one character, if possible.'
@@ -275,7 +277,7 @@ class PromptWidget(_ScrollableWidget):
             self._cursor_x -= 1
             self._input_buffer = (self._input_buffer[:self._cursor_x]
                                   + self._input_buffer[self._cursor_x + 1:])
-            self._history_idx = 0
+            self._history_idx_neg = 0
 
     def cmd__move_cursor(self, direction: str) -> None:
         'Move cursor one space into direction ("left" or "right") if possible.'