22
22
from __future__ import annotations
23
23
24
24
import sys
25
+ import _colorize
25
26
26
27
from contextlib import contextmanager
27
28
from dataclasses import dataclass , field , fields
28
- from _colorize import can_colorize , ANSIColors
29
-
30
29
31
30
from . import commands , console , input
32
- from .utils import wlen , unbracket , disp_str
31
+ from .utils import wlen , unbracket , disp_str , gen_colors
33
32
from .trace import trace
34
33
35
34
38
37
from .types import Callback , SimpleContextManager , KeySpec , CommandName
39
38
40
39
41
- # syntax classes:
42
-
40
+ # syntax classes
43
41
SYNTAX_WHITESPACE , SYNTAX_WORD , SYNTAX_SYMBOL = range (3 )
44
42
45
43
@@ -105,8 +103,7 @@ def make_default_commands() -> dict[CommandName, type[Command]]:
105
103
(r"\M-9" , "digit-arg" ),
106
104
(r"\M-\n" , "accept" ),
107
105
("\\ \\ " , "self-insert" ),
108
- (r"\x1b[200~" , "enable_bracketed_paste" ),
109
- (r"\x1b[201~" , "disable_bracketed_paste" ),
106
+ (r"\x1b[200~" , "perform-bracketed-paste" ),
110
107
(r"\x03" , "ctrl-c" ),
111
108
]
112
109
+ [(c , "self-insert" ) for c in map (chr , range (32 , 127 )) if c != "\\ " ]
@@ -144,16 +141,17 @@ class Reader:
144
141
Instance variables of note include:
145
142
146
143
* buffer:
147
- A * list* (*not* a string at the moment :-) containing all the
148
- characters that have been entered .
144
+ A per-character list containing all the characters that have been
145
+ entered. Does not include color information .
149
146
* console:
150
147
Hopefully encapsulates the OS dependent stuff.
151
148
* pos:
152
149
A 0-based index into 'buffer' for where the insertion point
153
150
is.
154
151
* screeninfo:
155
- Ahem. This list contains some info needed to move the
156
- insertion point around reasonably efficiently.
152
+ A list of screen position tuples. Each list element is a tuple
153
+ representing information on visible line length for a given line.
154
+ Allows for efficient skipping of color escape sequences.
157
155
* cxy, lxy:
158
156
the position of the insertion point in screen ...
159
157
* syntax_table:
@@ -203,7 +201,6 @@ class Reader:
203
201
dirty : bool = False
204
202
finished : bool = False
205
203
paste_mode : bool = False
206
- in_bracketed_paste : bool = False
207
204
commands : dict [str , type [Command ]] = field (default_factory = make_default_commands )
208
205
last_command : type [Command ] | None = None
209
206
syntax_table : dict [str , int ] = field (default_factory = make_default_syntax_table )
@@ -221,7 +218,6 @@ class Reader:
221
218
## cached metadata to speed up screen refreshes
222
219
@dataclass
223
220
class RefreshCache :
224
- in_bracketed_paste : bool = False
225
221
screen : list [str ] = field (default_factory = list )
226
222
screeninfo : list [tuple [int , list [int ]]] = field (init = False )
227
223
line_end_offsets : list [int ] = field (default_factory = list )
@@ -235,7 +231,6 @@ def update_cache(self,
235
231
screen : list [str ],
236
232
screeninfo : list [tuple [int , list [int ]]],
237
233
) -> None :
238
- self .in_bracketed_paste = reader .in_bracketed_paste
239
234
self .screen = screen .copy ()
240
235
self .screeninfo = screeninfo .copy ()
241
236
self .pos = reader .pos
@@ -248,8 +243,7 @@ def valid(self, reader: Reader) -> bool:
248
243
return False
249
244
dimensions = reader .console .width , reader .console .height
250
245
dimensions_changed = dimensions != self .dimensions
251
- paste_changed = reader .in_bracketed_paste != self .in_bracketed_paste
252
- return not (dimensions_changed or paste_changed )
246
+ return not dimensions_changed
253
247
254
248
def get_cached_location (self , reader : Reader ) -> tuple [int , int ]:
255
249
if self .invalidated :
@@ -279,7 +273,7 @@ def __post_init__(self) -> None:
279
273
self .screeninfo = [(0 , [])]
280
274
self .cxy = self .pos2xy ()
281
275
self .lxy = (self .pos , 0 )
282
- self .can_colorize = can_colorize ()
276
+ self .can_colorize = _colorize . can_colorize ()
283
277
284
278
self .last_refresh_cache .screeninfo = self .screeninfo
285
279
self .last_refresh_cache .pos = self .pos
@@ -316,6 +310,12 @@ def calc_screen(self) -> list[str]:
316
310
pos -= offset
317
311
318
312
prompt_from_cache = (offset and self .buffer [offset - 1 ] != "\n " )
313
+
314
+ if self .can_colorize :
315
+ colors = list (gen_colors (self .get_unicode ()))
316
+ else :
317
+ colors = None
318
+ trace ("colors = {colors}" , colors = colors )
319
319
lines = "" .join (self .buffer [offset :]).split ("\n " )
320
320
cursor_found = False
321
321
lines_beyond_cursor = 0
@@ -343,9 +343,8 @@ def calc_screen(self) -> list[str]:
343
343
screeninfo .append ((0 , []))
344
344
pos -= line_len + 1
345
345
prompt , prompt_len = self .process_prompt (prompt )
346
- chars , char_widths = disp_str (line )
346
+ chars , char_widths = disp_str (line , colors , offset )
347
347
wrapcount = (sum (char_widths ) + prompt_len ) // self .console .width
348
- trace ("wrapcount = {wrapcount}" , wrapcount = wrapcount )
349
348
if wrapcount == 0 or not char_widths :
350
349
offset += line_len + 1 # Takes all of the line plus the newline
351
350
last_refresh_line_end_offsets .append (offset )
@@ -479,7 +478,7 @@ def get_prompt(self, lineno: int, cursor_on_line: bool) -> str:
479
478
'lineno'."""
480
479
if self .arg is not None and cursor_on_line :
481
480
prompt = f"(arg: { self .arg } ) "
482
- elif self .paste_mode and not self . in_bracketed_paste :
481
+ elif self .paste_mode :
483
482
prompt = "(paste) "
484
483
elif "\n " in self .buffer :
485
484
if lineno == 0 :
@@ -492,7 +491,11 @@ def get_prompt(self, lineno: int, cursor_on_line: bool) -> str:
492
491
prompt = self .ps1
493
492
494
493
if self .can_colorize :
495
- prompt = f"{ ANSIColors .BOLD_MAGENTA } { prompt } { ANSIColors .RESET } "
494
+ prompt = (
495
+ f"{ _colorize .theme ["PROMPT" ]} "
496
+ f"{ prompt } "
497
+ f"{ _colorize .theme ["RESET" ]} "
498
+ )
496
499
return prompt
497
500
498
501
def push_input_trans (self , itrans : input .KeymapTranslator ) -> None :
@@ -567,6 +570,7 @@ def insert(self, text: str | list[str]) -> None:
567
570
def update_cursor (self ) -> None :
568
571
"""Move the cursor to reflect changes in self.pos"""
569
572
self .cxy = self .pos2xy ()
573
+ trace ("update_cursor({pos}) = {cxy}" , pos = self .pos , cxy = self .cxy )
570
574
self .console .move_cursor (* self .cxy )
571
575
572
576
def after_command (self , cmd : Command ) -> None :
@@ -633,9 +637,6 @@ def update_screen(self) -> None:
633
637
634
638
def refresh (self ) -> None :
635
639
"""Recalculate and refresh the screen."""
636
- if self .in_bracketed_paste and self .buffer and not self .buffer [- 1 ] == "\n " :
637
- return
638
-
639
640
# this call sets up self.cxy, so call it first.
640
641
self .screen = self .calc_screen ()
641
642
self .console .refresh (self .screen , self .cxy )
0 commit comments