From b356d5ad14e9498d8b93716684b7a628f67504be Mon Sep 17 00:00:00 2001 From: Etty Date: Wed, 17 Jul 2024 12:57:12 +0100 Subject: [PATCH 1/4] Add REPL history navigation with partial text This changes add supports to navigate the history with arrow up based on partial text in the buffer --- Lib/_pyrepl/historical_reader.py | 13 +++++++++- Lib/test/test_pyrepl/test_pyrepl.py | 40 +++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/Lib/_pyrepl/historical_reader.py b/Lib/_pyrepl/historical_reader.py index 5d416f336ad5d2..bb2e1043567cc5 100644 --- a/Lib/_pyrepl/historical_reader.py +++ b/Lib/_pyrepl/historical_reader.py @@ -274,10 +274,21 @@ def collect_keymap(self) -> tuple[tuple[KeySpec, CommandName], ...]: def select_item(self, i: int) -> None: self.transient_history[self.historyi] = self.get_unicode() buf = self.transient_history.get(i) + self.historyi = i + + if self.buffer: + filtered_history = [ + (index, item) + for index, item in enumerate(self.history) + if item.startswith(self.get_unicode()) + and item.strip() not in self.transient_history.values() + ] + if filtered_history: + self.historyi, buf = filtered_history[min(i, len(filtered_history) - 1)] + if buf is None: buf = self.history[i].rstrip() self.buffer = list(buf) - self.historyi = i self.pos = len(self.buffer) self.dirty = True self.last_refresh_cache.invalidated = True diff --git a/Lib/test/test_pyrepl/test_pyrepl.py b/Lib/test/test_pyrepl/test_pyrepl.py index 36f940eaea4eac..30fb1a18a9b660 100644 --- a/Lib/test/test_pyrepl/test_pyrepl.py +++ b/Lib/test/test_pyrepl/test_pyrepl.py @@ -689,6 +689,46 @@ def test_history_navigation_with_up_arrow(self): self.assertEqual(output, "1+1") self.assertEqual(clean_screen(reader.screen), "1+1") + def test_history_navigation_with_up_arrow_and_partial_text(self): + events = itertools.chain( + code_to_events("spam = 1\nham = 2\neggs = 3\nsp"), + [ + Event(evt="key", data="up", raw=bytearray(b"\x1bOA")), + Event(evt="key", data="\n", raw=bytearray(b"\n")), + ], + ) + + reader = self.prepare_reader(events) + + output = multiline_input(reader) + self.assertEqual(output, "spam = 1") + + def test_history_navigation_with_up_arrow_and_partial_text_with_similar_entries(self): + events = itertools.chain( + code_to_events("a=111\na=11\na=1\na="), + [ + Event(evt="key", data="up", raw=bytearray(b"\x1bOA")), + Event(evt="key", data="up", raw=bytearray(b"\x1bOA")), + Event(evt="key", data="up", raw=bytearray(b"\x1bOA")), + Event(evt="key", data="\n", raw=bytearray(b"\n")), + ], + ) + + reader = self.prepare_reader(events) + print() + output = multiline_input(reader) + self.assertEqual(output, "a=111") + self.assertEqual(clean_screen(reader.screen), "a=111") + output = multiline_input(reader) + self.assertEqual(output, "a=11") + self.assertEqual(clean_screen(reader.screen), "a=11") + output = multiline_input(reader) + self.assertEqual(output, "a=1") + self.assertEqual(clean_screen(reader.screen), "a=1") + output = multiline_input(reader) + self.assertEqual(output, "a=111") + self.assertEqual(clean_screen(reader.screen), "a=111") + def test_history_with_multiline_entries(self): code = "def foo():\nx = 1\ny = 2\nz = 3\n\ndef bar():\nreturn 42\n\n" events = list(itertools.chain( From d6e027fe61765dc261b3b4536479dbaacc9bc472 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Wed, 17 Jul 2024 12:13:31 +0000 Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Library/2024-07-17-12-13-30.gh-issue-120767.BKyogz.rst | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2024-07-17-12-13-30.gh-issue-120767.BKyogz.rst diff --git a/Misc/NEWS.d/next/Library/2024-07-17-12-13-30.gh-issue-120767.BKyogz.rst b/Misc/NEWS.d/next/Library/2024-07-17-12-13-30.gh-issue-120767.BKyogz.rst new file mode 100644 index 00000000000000..27c2de893cf25c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-07-17-12-13-30.gh-issue-120767.BKyogz.rst @@ -0,0 +1,4 @@ +Enhance REPL history navigation with partial text support + +The REPL now correctly navigates history based on partial text +in the buffer when using the up arrow. From ca6e3ca28ee2bebce23e38fd55f3a1b31dfde374 Mon Sep 17 00:00:00 2001 From: Etty Date: Tue, 10 Sep 2024 15:01:05 +0100 Subject: [PATCH 3/4] Revert select_item --- Lib/_pyrepl/historical_reader.py | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/Lib/_pyrepl/historical_reader.py b/Lib/_pyrepl/historical_reader.py index bb2e1043567cc5..5d416f336ad5d2 100644 --- a/Lib/_pyrepl/historical_reader.py +++ b/Lib/_pyrepl/historical_reader.py @@ -274,21 +274,10 @@ def collect_keymap(self) -> tuple[tuple[KeySpec, CommandName], ...]: def select_item(self, i: int) -> None: self.transient_history[self.historyi] = self.get_unicode() buf = self.transient_history.get(i) - self.historyi = i - - if self.buffer: - filtered_history = [ - (index, item) - for index, item in enumerate(self.history) - if item.startswith(self.get_unicode()) - and item.strip() not in self.transient_history.values() - ] - if filtered_history: - self.historyi, buf = filtered_history[min(i, len(filtered_history) - 1)] - if buf is None: buf = self.history[i].rstrip() self.buffer = list(buf) + self.historyi = i self.pos = len(self.buffer) self.dirty = True self.last_refresh_cache.invalidated = True From 3adac4090f91bb99af5d89946e215d5d2752baec Mon Sep 17 00:00:00 2001 From: Etty Date: Tue, 10 Sep 2024 15:02:11 +0100 Subject: [PATCH 4/4] Bind up and down arrow key to history-search --- Lib/_pyrepl/reader.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/_pyrepl/reader.py b/Lib/_pyrepl/reader.py index 4b0700d069c621..00ad40250081e0 100644 --- a/Lib/_pyrepl/reader.py +++ b/Lib/_pyrepl/reader.py @@ -143,8 +143,8 @@ def make_default_commands() -> dict[CommandName, type[Command]]: + [(c, "self-insert") for c in map(chr, range(32, 127)) if c != "\\"] + [(c, "self-insert") for c in map(chr, range(128, 256)) if c.isalpha()] + [ - (r"\", "up"), - (r"\", "down"), + (r"\", "history-search-backward"), + (r"\", "history-search-forward"), (r"\", "left"), (r"\C-\", "backward-word"), (r"\", "right"),