Skip to content

Commit

Permalink
pythongh-120041: Do not use append_to_screen when completions are vis…
Browse files Browse the repository at this point in the history
…ible (pythonGH-120042)

(cherry picked from commit 8fc7653)

Co-authored-by: Lysandros Nikolaou <lisandrosnik@gmail.com>
  • Loading branch information
lysnikolaou authored and miss-islington committed Jun 4, 2024
1 parent 6725c78 commit 9fbf9bf
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 11 deletions.
7 changes: 6 additions & 1 deletion Lib/_pyrepl/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,12 @@ def do(self) -> None:
r = self.reader
text = self.event * r.get_arg()
r.insert(text)
if len(text) == 1 and r.pos == len(r.buffer):
if (
len(text) == 1 and
r.pos == len(r.buffer) and
not r.cmpltn_menu_visible and # type: ignore[attr-defined]
not r.cmpltn_message_visible # type: ignore[attr-defined]
):
r.calc_screen = r.append_to_screen


Expand Down
20 changes: 12 additions & 8 deletions Lib/_pyrepl/completing_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,18 +187,20 @@ def do(self) -> None:
if p:
r.insert(p)
if last_is_completer:
if not r.cmpltn_menu_visible:
r.cmpltn_menu_visible = True
r.cmpltn_menu_visible = True
r.cmpltn_message_visible = False
r.cmpltn_menu, r.cmpltn_menu_end = build_menu(
r.console, completions, r.cmpltn_menu_end,
r.use_brackets, r.sort_in_column)
r.dirty = True
elif stem + p in completions:
r.msg = "[ complete but not unique ]"
r.dirty = True
else:
r.msg = "[ not unique ]"
r.dirty = True
elif not r.cmpltn_menu_visible:
r.cmpltn_message_visible = True
if stem + p in completions:
r.msg = "[ complete but not unique ]"
r.dirty = True
else:
r.msg = "[ not unique ]"
r.dirty = True


class self_insert(commands.self_insert):
Expand Down Expand Up @@ -236,6 +238,7 @@ class CompletingReader(Reader):
### Instance variables
cmpltn_menu: list[str] = field(init=False)
cmpltn_menu_visible: bool = field(init=False)
cmpltn_message_visible: bool = field(init=False)
cmpltn_menu_end: int = field(init=False)
cmpltn_menu_choices: list[str] = field(init=False)

Expand Down Expand Up @@ -271,6 +274,7 @@ def finish(self) -> None:
def cmpltn_reset(self) -> None:
self.cmpltn_menu = []
self.cmpltn_menu_visible = False
self.cmpltn_message_visible = False
self.cmpltn_menu_end = 0
self.cmpltn_menu_choices = []

Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_pyrepl/support.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def code_to_events(code: str):


def prepare_reader(console: Console, **kwargs):
config = ReadlineConfig(readline_completer=None)
config = ReadlineConfig(readline_completer=kwargs.pop("readline_completer", None))
reader = ReadlineAlikeReader(console=console, config=config)
reader.more_lines = partial(more_lines, namespace=None)
reader.paste_mode = True # Avoid extra indents
Expand Down
37 changes: 36 additions & 1 deletion Lib/test/test_pyrepl/test_reader.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import itertools
import functools
import rlcompleter
from unittest import TestCase

from .support import handle_all_events, handle_events_narrow_console, code_to_events, prepare_reader
Expand All @@ -9,7 +10,7 @@

class TestReader(TestCase):
def assert_screen_equals(self, reader, expected):
actual = reader.calc_screen()
actual = reader.screen
expected = expected.split("\n")
self.assertListEqual(actual, expected)

Expand Down Expand Up @@ -208,3 +209,37 @@ def test_prompt_length(self):
prompt, l = Reader.process_prompt(ps1)
self.assertEqual(prompt, "\033[0;32m樂>\033[0m> ")
self.assertEqual(l, 5)

def test_completions_updated_on_key_press(self):
namespace = {"itertools": itertools}
code = "itertools."
events = itertools.chain(code_to_events(code), [
Event(evt='key', data='\t', raw=bytearray(b'\t')), # Two tabs for completion
Event(evt='key', data='\t', raw=bytearray(b'\t')),
], code_to_events("a"))

completing_reader = functools.partial(
prepare_reader,
readline_completer=rlcompleter.Completer(namespace).complete
)
reader, _ = handle_all_events(events, prepare_reader=completing_reader)

actual = reader.screen
self.assertEqual(len(actual), 2)
self.assertEqual(actual[0].rstrip(), "itertools.accumulate(")
self.assertEqual(actual[1], f"{code}a")

def test_key_press_on_tab_press_once(self):
namespace = {"itertools": itertools}
code = "itertools."
events = itertools.chain(code_to_events(code), [
Event(evt='key', data='\t', raw=bytearray(b'\t')),
], code_to_events("a"))

completing_reader = functools.partial(
prepare_reader,
readline_completer=rlcompleter.Completer(namespace).complete
)
reader, _ = handle_all_events(events, prepare_reader=completing_reader)

self.assert_screen_equals(reader, f"{code}a")

0 comments on commit 9fbf9bf

Please sign in to comment.