From c6f6b5449fb275ef5d78a8a3e8f7f800fbc1a644 Mon Sep 17 00:00:00 2001 From: Lysandros Nikolaou Date: Mon, 20 May 2024 16:01:27 -0400 Subject: [PATCH 1/4] gh-111201: Add append to screen method to avoid recalculation --- Lib/_pyrepl/commands.py | 1 + Lib/_pyrepl/reader.py | 34 +++++++++++++++++++++++++++++----- Lib/_pyrepl/types.py | 1 + Lib/_pyrepl/unix_console.py | 4 ++-- 4 files changed, 33 insertions(+), 7 deletions(-) diff --git a/Lib/_pyrepl/commands.py b/Lib/_pyrepl/commands.py index 51c7afebede5a8..436f49be08278f 100644 --- a/Lib/_pyrepl/commands.py +++ b/Lib/_pyrepl/commands.py @@ -359,6 +359,7 @@ class self_insert(EditCommand): def do(self) -> None: r = self.reader r.insert(self.event * r.get_arg()) + r.calc_screen = r.append_to_screen class insert_nl(EditCommand): diff --git a/Lib/_pyrepl/reader.py b/Lib/_pyrepl/reader.py index d15a150180811d..c7beb0e77b4512 100644 --- a/Lib/_pyrepl/reader.py +++ b/Lib/_pyrepl/reader.py @@ -35,7 +35,7 @@ # types Command = commands.Command if False: - from .types import Callback, SimpleContextManager, KeySpec, CommandName + from .types import Callback, SimpleContextManager, KeySpec, CommandName, Callable, Self def disp_str(buffer: str) -> tuple[str, list[int]]: @@ -229,9 +229,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: Callable[[Self], list[str]] = field(init=False) def __post_init__(self) -> None: # Enable the use of `insert` without a `prepare` call - necessary to @@ -241,14 +243,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 @@ -561,8 +585,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/types.py b/Lib/_pyrepl/types.py index f9d48b828c720b..94ba07110b1644 100644 --- a/Lib/_pyrepl/types.py +++ b/Lib/_pyrepl/types.py @@ -1,3 +1,4 @@ +from typing import Self from collections.abc import Callable, Iterator Callback = Callable[[], object] diff --git a/Lib/_pyrepl/unix_console.py b/Lib/_pyrepl/unix_console.py index 7c59f48df406e6..3614cfbbe1b96c 100644 --- a/Lib/_pyrepl/unix_console.py +++ b/Lib/_pyrepl/unix_console.py @@ -214,7 +214,7 @@ def change_encoding(self, encoding: str) -> None: """ self.encoding = encoding - def refresh(self, screen, c_xy): + def refresh(self, screen: list[str], c_xy: tuple[int, int]) -> None: """ Refresh the console screen. @@ -293,7 +293,7 @@ def refresh(self, screen, c_xy): self.__show_cursor() - self.screen = screen + self.screen = screen.copy() self.move_cursor(cx, cy) self.flushoutput() From 34712d0a3a2f30c76e32c9b40e49f9fa0b5a6d63 Mon Sep 17 00:00:00 2001 From: Lysandros Nikolaou Date: Tue, 21 May 2024 10:28:14 -0400 Subject: [PATCH 2/4] Rework method to choose function --- Lib/_pyrepl/commands.py | 4 +++- Lib/_pyrepl/reader.py | 18 +++++++++++++----- Lib/_pyrepl/types.py | 1 - Lib/_pyrepl/unix_console.py | 2 +- Lib/_pyrepl/utils.py | 6 ++++++ 5 files changed, 23 insertions(+), 8 deletions(-) diff --git a/Lib/_pyrepl/commands.py b/Lib/_pyrepl/commands.py index 436f49be08278f..dfc081e69f56e4 100644 --- a/Lib/_pyrepl/commands.py +++ b/Lib/_pyrepl/commands.py @@ -22,6 +22,8 @@ from __future__ import annotations import os +from .utils import CalcScreen + # Categories of actions: # killing # yanking @@ -359,7 +361,7 @@ class self_insert(EditCommand): def do(self) -> None: r = self.reader r.insert(self.event * r.get_arg()) - r.calc_screen = r.append_to_screen + r.calc_screen_method = CalcScreen.CALC_APPEND_SCREEN class insert_nl(EditCommand): diff --git a/Lib/_pyrepl/reader.py b/Lib/_pyrepl/reader.py index c7beb0e77b4512..83f6d8fa38924a 100644 --- a/Lib/_pyrepl/reader.py +++ b/Lib/_pyrepl/reader.py @@ -28,14 +28,14 @@ from . import commands, console, input -from .utils import ANSI_ESCAPE_SEQUENCE, wlen +from .utils import ANSI_ESCAPE_SEQUENCE, wlen, CalcScreen from .trace import trace # types Command = commands.Command if False: - from .types import Callback, SimpleContextManager, KeySpec, CommandName, Callable, Self + from .types import Callback, SimpleContextManager, KeySpec, CommandName def disp_str(buffer: str) -> tuple[str, list[int]]: @@ -233,7 +233,7 @@ class Reader: 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: Callable[[Self], list[str]] = field(init=False) + calc_screen_method: CalcScreen = CalcScreen.CALC_COMPLETE_SCREEN def __post_init__(self) -> None: # Enable the use of `insert` without a `prepare` call - necessary to @@ -246,7 +246,6 @@ def __post_init__(self) -> None: 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 @@ -269,7 +268,7 @@ def append_to_screen(self) -> list[str]: self.cxy = self.pos2xy() # Reset the function that is used for completing the screen - self.calc_screen = self.calc_complete_screen + self.calc_screen_method = CalcScreen.CALC_COMPLETE_SCREEN return new_screen def calc_complete_screen(self) -> list[str]: @@ -327,6 +326,15 @@ def calc_complete_screen(self) -> list[str]: screeninfo.append((0, [])) return screen + def calc_screen(self) -> list[str]: + match self.calc_screen_method: + case CalcScreen.CALC_COMPLETE_SCREEN: + return self.calc_complete_screen() + case CalcScreen.CALC_APPEND_SCREEN: + return self.append_to_screen() + case _: + assert False, "No valid calc_screen" + def process_prompt(self, prompt: str) -> tuple[str, int]: """Process the prompt. diff --git a/Lib/_pyrepl/types.py b/Lib/_pyrepl/types.py index 94ba07110b1644..f9d48b828c720b 100644 --- a/Lib/_pyrepl/types.py +++ b/Lib/_pyrepl/types.py @@ -1,4 +1,3 @@ -from typing import Self from collections.abc import Callable, Iterator Callback = Callable[[], object] diff --git a/Lib/_pyrepl/unix_console.py b/Lib/_pyrepl/unix_console.py index 3614cfbbe1b96c..ec7d0636b9aeb3 100644 --- a/Lib/_pyrepl/unix_console.py +++ b/Lib/_pyrepl/unix_console.py @@ -214,7 +214,7 @@ def change_encoding(self, encoding: str) -> None: """ self.encoding = encoding - def refresh(self, screen: list[str], c_xy: tuple[int, int]) -> None: + def refresh(self, screen, c_xy): """ Refresh the console screen. diff --git a/Lib/_pyrepl/utils.py b/Lib/_pyrepl/utils.py index cd1df7c49a216d..f864b475e27a98 100644 --- a/Lib/_pyrepl/utils.py +++ b/Lib/_pyrepl/utils.py @@ -1,9 +1,15 @@ import re import unicodedata +from enum import Enum ANSI_ESCAPE_SEQUENCE = re.compile(r"\x1b\[[ -@]*[A-~]") +class CalcScreen(Enum): + CALC_COMPLETE_SCREEN = 1 + CALC_APPEND_SCREEN = 2 + + def str_width(c: str) -> int: w = unicodedata.east_asian_width(c) if w in ('N', 'Na', 'H', 'A'): From e516be31d5237c96c8b19e3adc143741e676c4a6 Mon Sep 17 00:00:00 2001 From: Lysandros Nikolaou Date: Tue, 21 May 2024 10:47:53 -0400 Subject: [PATCH 3/4] Only use new method when a single character is added to the end --- Lib/_pyrepl/commands.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Lib/_pyrepl/commands.py b/Lib/_pyrepl/commands.py index dfc081e69f56e4..fce66a0aa5a77c 100644 --- a/Lib/_pyrepl/commands.py +++ b/Lib/_pyrepl/commands.py @@ -360,8 +360,10 @@ def do(self) -> None: class self_insert(EditCommand): def do(self) -> None: r = self.reader - r.insert(self.event * r.get_arg()) - r.calc_screen_method = CalcScreen.CALC_APPEND_SCREEN + text = self.event * r.get_arg() + r.insert(text) + if len(text) == 1 and r.pos == len(r.buffer): + r.calc_screen_method = CalcScreen.CALC_APPEND_SCREEN class insert_nl(EditCommand): From ca97bd55e74845cb2aa632468c1e40985b81b609 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Tue, 21 May 2024 21:48:37 +0200 Subject: [PATCH 4/4] Remove enum --- Lib/_pyrepl/commands.py | 4 +--- Lib/_pyrepl/completing_reader.py | 16 ++++++++-------- Lib/_pyrepl/reader.py | 18 ++++++------------ Lib/_pyrepl/utils.py | 6 ------ 4 files changed, 15 insertions(+), 29 deletions(-) diff --git a/Lib/_pyrepl/commands.py b/Lib/_pyrepl/commands.py index fce66a0aa5a77c..3d9722d1586c2a 100644 --- a/Lib/_pyrepl/commands.py +++ b/Lib/_pyrepl/commands.py @@ -22,8 +22,6 @@ from __future__ import annotations import os -from .utils import CalcScreen - # Categories of actions: # killing # yanking @@ -363,7 +361,7 @@ def do(self) -> None: text = self.event * r.get_arg() r.insert(text) if len(text) == 1 and r.pos == len(r.buffer): - r.calc_screen_method = CalcScreen.CALC_APPEND_SCREEN + 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 83f6d8fa38924a..d29fe9ac4c8995 100644 --- a/Lib/_pyrepl/reader.py +++ b/Lib/_pyrepl/reader.py @@ -28,14 +28,16 @@ from . import commands, console, input -from .utils import ANSI_ESCAPE_SEQUENCE, wlen, CalcScreen +from .utils import ANSI_ESCAPE_SEQUENCE, wlen from .trace import trace # 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]]: @@ -233,7 +235,7 @@ class Reader: 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_method: CalcScreen = CalcScreen.CALC_COMPLETE_SCREEN + calc_screen: CalcScreen = field(init=False) def __post_init__(self) -> None: # Enable the use of `insert` without a `prepare` call - necessary to @@ -246,6 +248,7 @@ def __post_init__(self) -> None: 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 @@ -268,7 +271,7 @@ def append_to_screen(self) -> list[str]: self.cxy = self.pos2xy() # Reset the function that is used for completing the screen - self.calc_screen_method = CalcScreen.CALC_COMPLETE_SCREEN + self.calc_screen = self.calc_complete_screen return new_screen def calc_complete_screen(self) -> list[str]: @@ -326,15 +329,6 @@ def calc_complete_screen(self) -> list[str]: screeninfo.append((0, [])) return screen - def calc_screen(self) -> list[str]: - match self.calc_screen_method: - case CalcScreen.CALC_COMPLETE_SCREEN: - return self.calc_complete_screen() - case CalcScreen.CALC_APPEND_SCREEN: - return self.append_to_screen() - case _: - assert False, "No valid calc_screen" - def process_prompt(self, prompt: str) -> tuple[str, int]: """Process the prompt. diff --git a/Lib/_pyrepl/utils.py b/Lib/_pyrepl/utils.py index f864b475e27a98..cd1df7c49a216d 100644 --- a/Lib/_pyrepl/utils.py +++ b/Lib/_pyrepl/utils.py @@ -1,15 +1,9 @@ import re import unicodedata -from enum import Enum ANSI_ESCAPE_SEQUENCE = re.compile(r"\x1b\[[ -@]*[A-~]") -class CalcScreen(Enum): - CALC_COMPLETE_SCREEN = 1 - CALC_APPEND_SCREEN = 2 - - def str_width(c: str) -> int: w = unicodedata.east_asian_width(c) if w in ('N', 'Na', 'H', 'A'): pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy