@@ -43,6 +43,31 @@ def watch_format(self, format) -> None:
4343 self .update (format .format (self .execution_count ))
4444
4545
46+ class ChunkedStatic (textual .containers .Container ):
47+ """This widget splits a static text into a multiple chunks of `textual.widgets.Static` for performance."""
48+
49+ CHUNK_SIZE = 500
50+
51+ def __init__ (self , text : str ) -> None :
52+ super ().__init__ ()
53+
54+ # splitlines - used by rich.text.Text.from_ansi splits on "\r" too.
55+ # Some libraries e.g. tqdm relies on "\r" to rerender on the same line. So we split on "\n"-only.
56+ lines = text .split ("\n " )
57+ start = 0
58+ while start < len (lines ) and not lines [start ].strip ():
59+ start += 1
60+ end = len (lines ) - 1
61+ while end >= start and not lines [end ].strip ():
62+ end -= 1
63+ decoder = rich .ansi .AnsiDecoder ()
64+ self .decoded_lines = [decoder .decode_line (line ) for line in lines [start : end + 1 ]]
65+
66+ def compose (self ) -> textual .app .ComposeResult :
67+ for i in range (0 , len (self .decoded_lines ), self .CHUNK_SIZE ):
68+ yield textual .widgets .Static (rich .text .Text ("\n " ).join (self .decoded_lines [i : i + self .CHUNK_SIZE ]))
69+
70+
4671class Output (textual .containers .Container ):
4772 """Base class for code cell output."""
4873
@@ -60,24 +85,6 @@ def to_nbformat(self) -> nbformat.NotebookNode:
6085 def on_resize (self , event : textual .events .Resize ) -> None :
6186 self .post_message (Output .Resized (self , event .size ))
6287
63- @staticmethod
64- def ansi_to_rich (text : str , strip : bool = True ) -> rich .text .Text :
65- """Convet ansi escaped sequences to rich text.
66-
67- Work around some issues with rich.text.Text.from_ansi and optionally strip blank lines from the start and end."""
68-
69- # splitlines - used by rich.text.Text.from_ansi splits on "\r" too.
70- # E.g. tqdm relies on "\r" to rerender on the same line. So we split on "\n"-only.
71- lines = text .split ("\n " )
72- start = 0
73- while strip and start < len (lines ) and not lines [start ].strip ():
74- start += 1
75- end = len (lines ) - 1
76- while strip and end >= start and not lines [end ].strip ():
77- end -= 1
78- decoder = rich .ansi .AnsiDecoder ()
79- return rich .text .Text ("\n " ).join (decoder .decode_line (line ) for line in lines [start : end + 1 ])
80-
8188
8289class Stream (Output ):
8390 """Stream output in notebook."""
@@ -94,7 +101,7 @@ def to_nbformat(self) -> nbformat.NotebookNode:
94101 return nbformat .v4 .new_output (output_type = "stream" , name = self .stream_name , text = self .text )
95102
96103 def compose (self ) -> tp .Iterable [textual .widgets .Widget ]:
97- yield textual . widgets . Static (self .ansi_to_rich ( self . text ) )
104+ yield ChunkedStatic (self .text )
98105
99106
100107class DisplayData (Output ):
@@ -132,7 +139,7 @@ def compose(self) -> tp.Iterable[textual.widgets.Widget]:
132139 elif self .data .get ("text/markdown" ):
133140 yield textual .widgets .Markdown (self .data ["text/markdown" ])
134141 elif self .data .get ("text/plain" ):
135- yield textual . widgets . Static (self .ansi_to_rich ( self . data ["text/plain" ]) )
142+ yield ChunkedStatic (self .data ["text/plain" ])
136143
137144
138145class ExecuteResult (DisplayData ):
0 commit comments