Skip to content

Commit cdc9458

Browse files
committed
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
1 parent cffad5c commit cdc9458

File tree

2 files changed

+52
-1
lines changed

2 files changed

+52
-1
lines changed

Lib/_pyrepl/historical_reader.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,10 +258,21 @@ def collect_keymap(self) -> tuple[tuple[KeySpec, CommandName], ...]:
258258
def select_item(self, i: int) -> None:
259259
self.transient_history[self.historyi] = self.get_unicode()
260260
buf = self.transient_history.get(i)
261+
self.historyi = i
262+
263+
if self.buffer:
264+
filtered_history = [
265+
(index, item)
266+
for index, item in enumerate(self.history)
267+
if item.startswith(self.get_unicode())
268+
and item.strip() not in self.transient_history.values()
269+
]
270+
if filtered_history:
271+
self.historyi, buf = filtered_history[min(i, len(filtered_history) - 1)]
272+
261273
if buf is None:
262274
buf = self.history[i].rstrip()
263275
self.buffer = list(buf)
264-
self.historyi = i
265276
self.pos = len(self.buffer)
266277
self.dirty = True
267278
self.last_refresh_cache.invalidated = True

Lib/test/test_pyrepl/test_pyrepl.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,46 @@ def test_history_navigation_with_up_arrow(self):
555555
self.assertEqual(output, "1+1")
556556
self.assertEqual(clean_screen(reader.screen), "1+1")
557557

558+
def test_history_navigation_with_up_arrow_and_partial_text(self):
559+
events = itertools.chain(
560+
code_to_events("spam = 1\nham = 2\neggs = 3\nsp"),
561+
[
562+
Event(evt="key", data="up", raw=bytearray(b"\x1bOA")),
563+
Event(evt="key", data="\n", raw=bytearray(b"\n")),
564+
],
565+
)
566+
567+
reader = self.prepare_reader(events)
568+
569+
output = multiline_input(reader)
570+
self.assertEqual(output, "spam = 1")
571+
572+
def test_history_navigation_with_up_arrow_and_partial_text_with_similar_entries(self):
573+
events = itertools.chain(
574+
code_to_events("a=111\na=11\na=1\na="),
575+
[
576+
Event(evt="key", data="up", raw=bytearray(b"\x1bOA")),
577+
Event(evt="key", data="up", raw=bytearray(b"\x1bOA")),
578+
Event(evt="key", data="up", raw=bytearray(b"\x1bOA")),
579+
Event(evt="key", data="\n", raw=bytearray(b"\n")),
580+
],
581+
)
582+
583+
reader = self.prepare_reader(events)
584+
print()
585+
output = multiline_input(reader)
586+
self.assertEqual(output, "a=111")
587+
self.assertEqual(clean_screen(reader.screen), "a=111")
588+
output = multiline_input(reader)
589+
self.assertEqual(output, "a=11")
590+
self.assertEqual(clean_screen(reader.screen), "a=11")
591+
output = multiline_input(reader)
592+
self.assertEqual(output, "a=1")
593+
self.assertEqual(clean_screen(reader.screen), "a=1")
594+
output = multiline_input(reader)
595+
self.assertEqual(output, "a=111")
596+
self.assertEqual(clean_screen(reader.screen), "a=111")
597+
558598
def test_history_with_multiline_entries(self):
559599
code = "def foo():\nx = 1\ny = 2\nz = 3\n\ndef bar():\nreturn 42\n\n"
560600
events = list(itertools.chain(

0 commit comments

Comments
 (0)