Skip to content

Commit a01ce71

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 65fcaa3 commit a01ce71

2 files changed

Lines changed: 52 additions & 1 deletion

File tree

Lib/_pyrepl/historical_reader.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,10 +272,21 @@ def collect_keymap(self) -> tuple[tuple[KeySpec, CommandName], ...]:
272272
def select_item(self, i: int) -> None:
273273
self.transient_history[self.historyi] = self.get_unicode()
274274
buf = self.transient_history.get(i)
275+
self.historyi = i
276+
277+
if self.buffer:
278+
filtered_history = [
279+
(index, item)
280+
for index, item in enumerate(self.history)
281+
if item.startswith(self.get_unicode())
282+
and item.strip() not in self.transient_history.values()
283+
]
284+
if filtered_history:
285+
self.historyi, buf = filtered_history[min(i, len(filtered_history) - 1)]
286+
275287
if buf is None:
276288
buf = self.history[i].rstrip()
277289
self.buffer = list(buf)
278-
self.historyi = i
279290
self.pos = len(self.buffer)
280291
self.dirty = True
281292
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
@@ -601,6 +601,46 @@ def test_history_navigation_with_up_arrow(self):
601601
self.assertEqual(output, "1+1")
602602
self.assertEqual(clean_screen(reader.screen), "1+1")
603603

604+
def test_history_navigation_with_up_arrow_and_partial_text(self):
605+
events = itertools.chain(
606+
code_to_events("spam = 1\nham = 2\neggs = 3\nsp"),
607+
[
608+
Event(evt="key", data="up", raw=bytearray(b"\x1bOA")),
609+
Event(evt="key", data="\n", raw=bytearray(b"\n")),
610+
],
611+
)
612+
613+
reader = self.prepare_reader(events)
614+
615+
output = multiline_input(reader)
616+
self.assertEqual(output, "spam = 1")
617+
618+
def test_history_navigation_with_up_arrow_and_partial_text_with_similar_entries(self):
619+
events = itertools.chain(
620+
code_to_events("a=111\na=11\na=1\na="),
621+
[
622+
Event(evt="key", data="up", raw=bytearray(b"\x1bOA")),
623+
Event(evt="key", data="up", raw=bytearray(b"\x1bOA")),
624+
Event(evt="key", data="up", raw=bytearray(b"\x1bOA")),
625+
Event(evt="key", data="\n", raw=bytearray(b"\n")),
626+
],
627+
)
628+
629+
reader = self.prepare_reader(events)
630+
print()
631+
output = multiline_input(reader)
632+
self.assertEqual(output, "a=111")
633+
self.assertEqual(clean_screen(reader.screen), "a=111")
634+
output = multiline_input(reader)
635+
self.assertEqual(output, "a=11")
636+
self.assertEqual(clean_screen(reader.screen), "a=11")
637+
output = multiline_input(reader)
638+
self.assertEqual(output, "a=1")
639+
self.assertEqual(clean_screen(reader.screen), "a=1")
640+
output = multiline_input(reader)
641+
self.assertEqual(output, "a=111")
642+
self.assertEqual(clean_screen(reader.screen), "a=111")
643+
604644
def test_history_with_multiline_entries(self):
605645
code = "def foo():\nx = 1\ny = 2\nz = 3\n\ndef bar():\nreturn 42\n\n"
606646
events = list(itertools.chain(

0 commit comments

Comments
 (0)