Skip to content

Commit

Permalink
Add REPL history navigation with partial text
Browse files Browse the repository at this point in the history
This changes add supports to navigate the history with arrow up
based on partial text in the buffer
  • Loading branch information
estyxx committed Sep 10, 2024
1 parent 65fcaa3 commit a01ce71
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 1 deletion.
13 changes: 12 additions & 1 deletion Lib/_pyrepl/historical_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,10 +272,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
Expand Down
40 changes: 40 additions & 0 deletions Lib/test/test_pyrepl/test_pyrepl.py
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,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(
Expand Down

0 comments on commit a01ce71

Please sign in to comment.