From a6c276862ec890f0f185d4a01969ccbe8d7e1e8e Mon Sep 17 00:00:00 2001 From: retoor Date: Sat, 8 Nov 2025 00:42:52 +0100 Subject: [PATCH] feat: implement scrolling in editor feat: update version to 1.26.0 refactor: adjust cursor position based on scroll_y maintenance: update changelog with new version information --- CHANGELOG.md | 8 ++++++ pyproject.toml | 2 +- rp/editor.py | 67 ++++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 63 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93a7176..4dd9bd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,14 @@ + + +## Version 1.25.0 - 2025-11-08 + +Autonomous mode now has improved error handling and tracks usage and costs. Several internal components were updated to improve reliability and logging. + +**Changes:** 10 files, 342 lines +**Languages:** Markdown (8 lines), Python (332 lines), TOML (2 lines) ## Version 1.24.0 - 2025-11-07 diff --git a/pyproject.toml b/pyproject.toml index 434ffef..498aa77 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "rp" -version = "1.24.0" +version = "1.25.0" description = "R python edition. The ultimate autonomous AI CLI." readme = "README.md" requires-python = ">=3.10" diff --git a/rp/editor.py b/rp/editor.py index 5f42431..888309e 100644 --- a/rp/editor.py +++ b/rp/editor.py @@ -26,6 +26,7 @@ class RPEditor: self.lines = [""] self.cursor_y = 0 self.cursor_x = 0 + self.scroll_y = 0 self.mode = "normal" self.command = "" self.stdscr = None @@ -222,14 +223,15 @@ class RPEditor: try: self.stdscr.clear() height, width = self.stdscr.getmaxyx() - for i, line in enumerate(self.lines): - if i >= height - 1: - break - try: - display_line = line[: width - 1] if len(line) >= width else line - self.stdscr.addstr(i, 0, display_line) - except curses.error: - pass + for i in range(height - 1): + line_idx = self.scroll_y + i + if line_idx < len(self.lines): + line = self.lines[line_idx] + try: + display_line = line[: width - 1] if len(line) >= width else line + self.stdscr.addstr(i, 0, display_line) + except curses.error: + pass status = f"{self.mode.upper()} | {self.filename or 'untitled'} | {self.cursor_y + 1}:{self.cursor_x + 1}" if self.mode == "command": status = self.command[: width - 1] @@ -238,11 +240,12 @@ class RPEditor: except curses.error: pass cursor_x = min(self.cursor_x, width - 1) - cursor_y = min(self.cursor_y, height - 2) - try: - self.stdscr.move(cursor_y, cursor_x) - except curses.error: - pass + cursor_y_display = self.cursor_y - self.scroll_y + if 0 <= cursor_y_display < height - 1: + try: + self.stdscr.move(cursor_y_display, cursor_x) + except curses.error: + pass self.stdscr.refresh() except Exception: pass @@ -321,15 +324,33 @@ class RPEditor: if self.prev_key == ord("g"): self.cursor_y = 0 self.cursor_x = 0 + self.scroll_y = 0 elif key == ord("G"): self.cursor_y = max(0, len(self.lines) - 1) self.cursor_x = 0 + # Adjust scroll_y + if self.stdscr: + height, _ = self.stdscr.getmaxyx() + if self.cursor_y >= self.scroll_y + height - 1: + self.scroll_y = self.cursor_y - height + 2 elif key == ord("u"): self.undo() elif key == 18: self.redo() elif key == 19: self._save_file() + elif key == curses.KEY_PPAGE: # Page Up + if self.stdscr: + height, _ = self.stdscr.getmaxyx() + page_size = height - 2 + self.cursor_y = max(0, self.cursor_y - page_size) + self.scroll_y = max(0, self.scroll_y - page_size) + elif key == curses.KEY_NPAGE: # Page Down + if self.stdscr: + height, _ = self.stdscr.getmaxyx() + page_size = height - 2 + self.cursor_y = min(len(self.lines) - 1, self.cursor_y + page_size) + self.scroll_y = min(max(0, len(self.lines) - height + 1), self.scroll_y + page_size) self.prev_key = key except Exception: pass @@ -420,6 +441,13 @@ class RPEditor: elif new_y >= len(self.lines): self.cursor_y = max(0, len(self.lines) - 1) self.cursor_x = len(self.lines[self.cursor_y]) + # Adjust scroll_y to keep cursor visible + if self.stdscr: + height, _ = self.stdscr.getmaxyx() + if self.cursor_y < self.scroll_y: + self.scroll_y = self.cursor_y + elif self.cursor_y >= self.scroll_y + height - 1: + self.scroll_y = self.cursor_y - height + 2 def save_state(self): """Save current state for undo.""" @@ -428,6 +456,7 @@ class RPEditor: "lines": list(self.lines), "cursor_y": self.cursor_y, "cursor_x": self.cursor_x, + "scroll_y": self.scroll_y, } self.undo_stack.append(state) if len(self.undo_stack) > self.max_undo: @@ -442,6 +471,7 @@ class RPEditor: "lines": list(self.lines), "cursor_y": self.cursor_y, "cursor_x": self.cursor_x, + "scroll_y": self.scroll_y, } self.redo_stack.append(current_state) state = self.undo_stack.pop() @@ -450,6 +480,7 @@ class RPEditor: self.cursor_x = min( state["cursor_x"], len(self.lines[self.cursor_y]) if self.lines else 0 ) + self.scroll_y = state.get("scroll_y", 0) def redo(self): """Redo last undone change.""" @@ -459,6 +490,7 @@ class RPEditor: "lines": list(self.lines), "cursor_y": self.cursor_y, "cursor_x": self.cursor_x, + "scroll_y": self.scroll_y, } self.undo_stack.append(current_state) state = self.redo_stack.pop() @@ -467,6 +499,7 @@ class RPEditor: self.cursor_x = min( state["cursor_x"], len(self.lines[self.cursor_y]) if self.lines else 0 ) + self.scroll_y = state.get("scroll_y", 0) def _insert_text(self, text): """Insert text at cursor position.""" @@ -571,6 +604,7 @@ class RPEditor: self.lines = text.splitlines() if text else [""] self.cursor_y = 0 self.cursor_x = 0 + self.scroll_y = 0 def set_text(self, text): """Thread-safe text setting.""" @@ -589,6 +623,13 @@ class RPEditor: line_num = max(0, min(line_num - 1, len(self.lines) - 1)) self.cursor_y = line_num self.cursor_x = 0 + # Adjust scroll_y + if self.stdscr: + height, _ = self.stdscr.getmaxyx() + if self.cursor_y < self.scroll_y: + self.scroll_y = self.cursor_y + elif self.cursor_y >= self.scroll_y + height - 1: + self.scroll_y = self.cursor_y - height + 2 def goto_line(self, line_num): """Thread-safe goto line."""