From aefe2e626eeb2f05cda10a17926f8ba9b7a504ca Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 22 May 2024 18:03:04 +0200 Subject: [PATCH] [3.13] gh-111201: Add append to screen method to avoid recalculation (GH-119274) (#119405) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit c886bece3b3a49f8a0f188aecfc1d6ff89d281e6) Co-authored-by: Lysandros Nikolaou Co-authored-by: Ɓukasz Langa --- Lib/_pyrepl/commands.py | 5 ++++- Lib/_pyrepl/completing_reader.py | 16 +++++++-------- Lib/_pyrepl/reader.py | 34 ++++++++++++++++++++++++++++---- Lib/_pyrepl/unix_console.py | 2 +- 4 files changed, 43 insertions(+), 14 deletions(-) diff --git a/Lib/_pyrepl/commands.py b/Lib/_pyrepl/commands.py index 51c7afebede5a8..3d9722d1586c2a 100644 --- a/Lib/_pyrepl/commands.py +++ b/Lib/_pyrepl/commands.py @@ -358,7 +358,10 @@ def do(self) -> None: class self_insert(EditCommand): def do(self) -> None: r = self.reader - r.insert(self.event * r.get_arg()) + text = self.event * r.get_arg() + r.insert(text) + if len(text) == 1 and r.pos == len(r.buffer): + r.calc_screen = r.append_to_screen class insert_nl(EditCommand): diff --git a/Lib/_pyrepl/completing_reader.py b/Lib/_pyrepl/completing_reader.py index 19fc06feaf3ced..3f8506bfe8f8f8 100644 --- a/Lib/_pyrepl/completing_reader.py +++ b/Lib/_pyrepl/completing_reader.py @@ -187,8 +187,8 @@ def do(self) -> None: if p: r.insert(p) if last_is_completer: - if not r.cmpltn_menu_vis: - r.cmpltn_menu_vis = 1 + if not r.cmpltn_menu_visible: + r.cmpltn_menu_visible = True r.cmpltn_menu, r.cmpltn_menu_end = build_menu( r.console, completions, r.cmpltn_menu_end, r.use_brackets, r.sort_in_column) @@ -208,7 +208,7 @@ def do(self) -> None: commands.self_insert.do(self) - if r.cmpltn_menu_vis: + if r.cmpltn_menu_visible: stem = r.get_stem() if len(stem) < 1: r.cmpltn_reset() @@ -235,7 +235,7 @@ class CompletingReader(Reader): ### Instance variables cmpltn_menu: list[str] = field(init=False) - cmpltn_menu_vis: int = field(init=False) + cmpltn_menu_visible: bool = field(init=False) cmpltn_menu_end: int = field(init=False) cmpltn_menu_choices: list[str] = field(init=False) @@ -255,9 +255,9 @@ def after_command(self, cmd: Command) -> None: if not isinstance(cmd, (complete, self_insert)): self.cmpltn_reset() - def calc_screen(self) -> list[str]: - screen = super().calc_screen() - if self.cmpltn_menu_vis: + def calc_complete_screen(self) -> list[str]: + screen = super().calc_complete_screen() + if self.cmpltn_menu_visible: ly = self.lxy[1] screen[ly:ly] = self.cmpltn_menu self.screeninfo[ly:ly] = [(0, [])]*len(self.cmpltn_menu) @@ -270,7 +270,7 @@ def finish(self) -> None: def cmpltn_reset(self) -> None: self.cmpltn_menu = [] - self.cmpltn_menu_vis = 0 + self.cmpltn_menu_visible = False self.cmpltn_menu_end = 0 self.cmpltn_menu_choices = [] diff --git a/Lib/_pyrepl/reader.py b/Lib/_pyrepl/reader.py index 2c8c9e7dc4b5da..9a207a241d5f8d 100644 --- a/Lib/_pyrepl/reader.py +++ b/Lib/_pyrepl/reader.py @@ -35,7 +35,9 @@ # types Command = commands.Command if False: + from typing import Callable from .types import Callback, SimpleContextManager, KeySpec, CommandName + CalcScreen = Callable[[], list[str]] def disp_str(buffer: str) -> tuple[str, list[int]]: @@ -231,9 +233,11 @@ class Reader: keymap: tuple[tuple[str, str], ...] = () input_trans: input.KeymapTranslator = field(init=False) input_trans_stack: list[input.KeymapTranslator] = field(default_factory=list) + screen: list[str] = field(default_factory=list) screeninfo: list[tuple[int, list[int]]] = field(init=False) cxy: tuple[int, int] = field(init=False) lxy: tuple[int, int] = field(init=False) + calc_screen: CalcScreen = field(init=False) def __post_init__(self) -> None: # Enable the use of `insert` without a `prepare` call - necessary to @@ -243,14 +247,36 @@ def __post_init__(self) -> None: self.input_trans = input.KeymapTranslator( self.keymap, invalid_cls="invalid-key", character_cls="self-insert" ) - self.screeninfo = [(0, [0])] + self.screeninfo = [(0, [])] self.cxy = self.pos2xy() self.lxy = (self.pos, 0) + self.calc_screen = self.calc_complete_screen def collect_keymap(self) -> tuple[tuple[KeySpec, CommandName], ...]: return default_keymap - def calc_screen(self) -> list[str]: + def append_to_screen(self) -> list[str]: + new_screen = self.screen.copy() or [''] + + new_character = self.buffer[-1] + new_character_len = wlen(new_character) + + last_line_len = wlen(new_screen[-1]) + if last_line_len + new_character_len >= self.console.width: # We need to wrap here + new_screen[-1] += '\\' + self.screeninfo[-1][1].append(1) + new_screen.append(self.buffer[-1]) + self.screeninfo.append((0, [new_character_len])) + else: + new_screen[-1] += self.buffer[-1] + self.screeninfo[-1][1].append(new_character_len) + self.cxy = self.pos2xy() + + # Reset the function that is used for completing the screen + self.calc_screen = self.calc_complete_screen + return new_screen + + def calc_complete_screen(self) -> list[str]: """The purpose of this method is to translate changes in self.buffer into changes in self.screen. Currently it rips everything down and starts from scratch, which whilst not @@ -563,8 +589,8 @@ def update_screen(self) -> None: def refresh(self) -> None: """Recalculate and refresh the screen.""" # this call sets up self.cxy, so call it first. - screen = self.calc_screen() - self.console.refresh(screen, self.cxy) + self.screen = self.calc_screen() + self.console.refresh(self.screen, self.cxy) self.dirty = False def do_cmd(self, cmd: tuple[str, list[str]]) -> None: diff --git a/Lib/_pyrepl/unix_console.py b/Lib/_pyrepl/unix_console.py index 605318c82ae2ea..c213012bfb2bde 100644 --- a/Lib/_pyrepl/unix_console.py +++ b/Lib/_pyrepl/unix_console.py @@ -283,7 +283,7 @@ def refresh(self, screen, c_xy): self.__show_cursor() - self.screen = screen + self.screen = screen.copy() self.move_cursor(cx, cy) self.flushoutput()