From 602fcf97df1665538d4e9841f9dc6bc33e38bece Mon Sep 17 00:00:00 2001 From: Matt Wozniski Date: Sun, 25 Aug 2024 18:54:06 -0400 Subject: [PATCH] gh-123177: Fix prompt for wrapped lines in pyrepl (#123324) When display lines above the cursor come from the cache, the first line to not come from the cache may be a wrapped line, starting half way through a logical line in the buffer. Detect and handle this case to avoid accidentally drawing a stray prompt in the middle of a logical line. --- Lib/_pyrepl/reader.py | 10 +++++- Lib/test/test_pyrepl/test_reader.py | 31 +++++++++++++++++++ ...-08-25-18-27-49.gh-issue-123177.yLuyqE.rst | 2 ++ 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-08-25-18-27-49.gh-issue-123177.yLuyqE.rst diff --git a/Lib/_pyrepl/reader.py b/Lib/_pyrepl/reader.py index 13b1f3eb9d1..aa3f5fd283e 100644 --- a/Lib/_pyrepl/reader.py +++ b/Lib/_pyrepl/reader.py @@ -345,7 +345,10 @@ class Reader: pos = self.pos pos -= offset + prompt_from_cache = (offset and self.buffer[offset - 1] != "\n") + lines = "".join(self.buffer[offset:]).split("\n") + cursor_found = False lines_beyond_cursor = 0 for ln, line in enumerate(lines, num_common_lines): @@ -359,7 +362,12 @@ class Reader: # No need to keep formatting lines. # The console can't show them. break - prompt = self.get_prompt(ln, ll >= pos >= 0) + if prompt_from_cache: + # Only the first line's prompt can come from the cache + prompt_from_cache = False + prompt = "" + else: + prompt = self.get_prompt(ln, ll >= pos >= 0) while "\n" in prompt: pre_prompt, _, prompt = prompt.partition("\n") last_refresh_line_end_offsets.append(offset) diff --git a/Lib/test/test_pyrepl/test_reader.py b/Lib/test/test_pyrepl/test_reader.py index e82c3ca0bb5..6c72a1d39c5 100644 --- a/Lib/test/test_pyrepl/test_reader.py +++ b/Lib/test/test_pyrepl/test_reader.py @@ -30,6 +30,37 @@ class TestReader(TestCase): reader, _ = handle_events_narrow_console(events) self.assert_screen_equals(reader, f"{9*"a"}\\\n{9*"a"}\\\naa") + def test_calc_screen_prompt_handling(self): + def prepare_reader_keep_prompts(*args, **kwargs): + reader = prepare_reader(*args, **kwargs) + del reader.get_prompt + reader.ps1 = ">>> " + reader.ps2 = ">>> " + reader.ps3 = "... " + reader.ps4 = "" + reader.can_colorize = False + reader.paste_mode = False + return reader + + events = code_to_events("if some_condition:\nsome_function()") + reader, _ = handle_events_narrow_console( + events, + prepare_reader=prepare_reader_keep_prompts, + ) + # fmt: off + self.assert_screen_equals( + reader, + ( + ">>> if so\\\n" + "me_condit\\\n" + "ion:\n" + "... s\\\n" + "ome_funct\\\n" + "ion()" + ) + ) + # fmt: on + def test_calc_screen_wrap_three_lines_mixed_character(self): # fmt: off code = ( diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-08-25-18-27-49.gh-issue-123177.yLuyqE.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-08-25-18-27-49.gh-issue-123177.yLuyqE.rst new file mode 100644 index 00000000000..1f1791d9a6c --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-08-25-18-27-49.gh-issue-123177.yLuyqE.rst @@ -0,0 +1,2 @@ +Fix a bug causing stray prompts to appear in the middle of wrapped lines in +the new REPL.