Skip to content

Commit 8d74eae

Browse files
[3.13] gh-120041: Do not use append_to_screen when completions are visible (GH-120042) (#120051)
* gh-120041: Do not use append_to_screen when completions are visible (GH-120042) (cherry picked from commit 8fc7653) * gh-120041: Refactor check for visible completion menu in completing_reader (GH-120055) (cherry picked from commit bf8e5e5) --------- Co-authored-by: Lysandros Nikolaou <[email protected]>
1 parent 2acbdc2 commit 8d74eae

File tree

3 files changed

+52
-10
lines changed

3 files changed

+52
-10
lines changed

Lib/_pyrepl/completing_reader.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -187,18 +187,20 @@ def do(self) -> None:
187187
if p:
188188
r.insert(p)
189189
if last_is_completer:
190-
if not r.cmpltn_menu_visible:
191-
r.cmpltn_menu_visible = True
190+
r.cmpltn_menu_visible = True
191+
r.cmpltn_message_visible = False
192192
r.cmpltn_menu, r.cmpltn_menu_end = build_menu(
193193
r.console, completions, r.cmpltn_menu_end,
194194
r.use_brackets, r.sort_in_column)
195195
r.dirty = True
196-
elif stem + p in completions:
197-
r.msg = "[ complete but not unique ]"
198-
r.dirty = True
199-
else:
200-
r.msg = "[ not unique ]"
201-
r.dirty = True
196+
elif not r.cmpltn_menu_visible:
197+
r.cmpltn_message_visible = True
198+
if stem + p in completions:
199+
r.msg = "[ complete but not unique ]"
200+
r.dirty = True
201+
else:
202+
r.msg = "[ not unique ]"
203+
r.dirty = True
202204

203205

204206
class self_insert(commands.self_insert):
@@ -208,6 +210,9 @@ def do(self) -> None:
208210

209211
commands.self_insert.do(self)
210212

213+
if r.cmpltn_menu_visible or r.cmpltn_message_visible:
214+
r.calc_screen = r.calc_complete_screen
215+
211216
if r.cmpltn_menu_visible:
212217
stem = r.get_stem()
213218
if len(stem) < 1:
@@ -236,6 +241,7 @@ class CompletingReader(Reader):
236241
### Instance variables
237242
cmpltn_menu: list[str] = field(init=False)
238243
cmpltn_menu_visible: bool = field(init=False)
244+
cmpltn_message_visible: bool = field(init=False)
239245
cmpltn_menu_end: int = field(init=False)
240246
cmpltn_menu_choices: list[str] = field(init=False)
241247

@@ -271,6 +277,7 @@ def finish(self) -> None:
271277
def cmpltn_reset(self) -> None:
272278
self.cmpltn_menu = []
273279
self.cmpltn_menu_visible = False
280+
self.cmpltn_message_visible = False
274281
self.cmpltn_menu_end = 0
275282
self.cmpltn_menu_choices = []
276283

Lib/test/test_pyrepl/support.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def code_to_events(code: str):
3939

4040

4141
def prepare_reader(console: Console, **kwargs):
42-
config = ReadlineConfig(readline_completer=None)
42+
config = ReadlineConfig(readline_completer=kwargs.pop("readline_completer", None))
4343
reader = ReadlineAlikeReader(console=console, config=config)
4444
reader.more_lines = partial(more_lines, namespace=None)
4545
reader.paste_mode = True # Avoid extra indents

Lib/test/test_pyrepl/test_reader.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import itertools
22
import functools
3+
import rlcompleter
34
from unittest import TestCase
45

56
from .support import handle_all_events, handle_events_narrow_console, code_to_events, prepare_reader
@@ -9,7 +10,7 @@
910

1011
class TestReader(TestCase):
1112
def assert_screen_equals(self, reader, expected):
12-
actual = reader.calc_screen()
13+
actual = reader.screen
1314
expected = expected.split("\n")
1415
self.assertListEqual(actual, expected)
1516

@@ -208,3 +209,37 @@ def test_prompt_length(self):
208209
prompt, l = Reader.process_prompt(ps1)
209210
self.assertEqual(prompt, "\033[0;32m樂>\033[0m> ")
210211
self.assertEqual(l, 5)
212+
213+
def test_completions_updated_on_key_press(self):
214+
namespace = {"itertools": itertools}
215+
code = "itertools."
216+
events = itertools.chain(code_to_events(code), [
217+
Event(evt='key', data='\t', raw=bytearray(b'\t')), # Two tabs for completion
218+
Event(evt='key', data='\t', raw=bytearray(b'\t')),
219+
], code_to_events("a"))
220+
221+
completing_reader = functools.partial(
222+
prepare_reader,
223+
readline_completer=rlcompleter.Completer(namespace).complete
224+
)
225+
reader, _ = handle_all_events(events, prepare_reader=completing_reader)
226+
227+
actual = reader.screen
228+
self.assertEqual(len(actual), 2)
229+
self.assertEqual(actual[0].rstrip(), "itertools.accumulate(")
230+
self.assertEqual(actual[1], f"{code}a")
231+
232+
def test_key_press_on_tab_press_once(self):
233+
namespace = {"itertools": itertools}
234+
code = "itertools."
235+
events = itertools.chain(code_to_events(code), [
236+
Event(evt='key', data='\t', raw=bytearray(b'\t')),
237+
], code_to_events("a"))
238+
239+
completing_reader = functools.partial(
240+
prepare_reader,
241+
readline_completer=rlcompleter.Completer(namespace).complete
242+
)
243+
reader, _ = handle_all_events(events, prepare_reader=completing_reader)
244+
245+
self.assert_screen_equals(reader, f"{code}a")

0 commit comments

Comments
 (0)