1
1
"""Provides common utilities to support Rich in cmd2-based applications."""
2
2
3
+ import re
3
4
from collections .abc import Mapping
4
5
from enum import Enum
5
6
from typing import (
28
29
29
30
from .styles import DEFAULT_CMD2_STYLES
30
31
32
+ # A compiled regular expression to detect ANSI escape sequences.
33
+ # The `[a-zA-Z]` at the end of the regex allows it to match all types of
34
+ # escape sequences, including those for styling, cursor movement, etc.
35
+ _ANSI_ESCAPE_SEQUENCE_RE = re .compile (r"\x1b\[[0-9;?]*[a-zA-Z]" )
36
+
31
37
32
38
class AllowStyle (Enum ):
33
39
"""Values for ``cmd2.rich_utils.ALLOW_STYLE``."""
34
40
35
- ALWAYS = ' Always' # Always output ANSI style sequences
36
- NEVER = ' Never' # Remove ANSI style sequences from all output
37
- TERMINAL = ' Terminal' # Remove ANSI style sequences if the output is not going to the terminal
41
+ ALWAYS = " Always" # Always output ANSI style sequences
42
+ NEVER = " Never" # Remove ANSI style sequences from all output
43
+ TERMINAL = " Terminal" # Remove ANSI style sequences if the output is not going to the terminal
38
44
39
45
def __str__ (self ) -> str :
40
46
"""Return value instead of enum name for printing in cmd2's set command."""
@@ -234,7 +240,7 @@ def rich_text_to_string(text: Text) -> str:
234
240
"""Convert a Rich Text object to a string.
235
241
236
242
This function's purpose is to render a Rich Text object, including any styles (e.g., color, bold),
237
- to a plain Python string with ANSI escape codes . It differs from `text.plain`, which strips
243
+ to a plain Python string with ANSI style sequences . It differs from `text.plain`, which strips
238
244
all formatting.
239
245
240
246
:param text: the text object to convert
@@ -259,7 +265,7 @@ def rich_text_to_string(text: Text) -> str:
259
265
260
266
261
267
def string_to_rich_text (text : str ) -> Text :
262
- r"""Create a Text object from a string which can contain ANSI escape codes .
268
+ r"""Create a Text object from a string which can contain ANSI style sequences .
263
269
264
270
This wraps rich.Text.from_ansi() to handle an issue where it removes the
265
271
trailing line break from a string (e.g. "Hello\n" becomes "Hello").
@@ -323,9 +329,9 @@ def prepare_objects_for_rendering(*objects: Any) -> tuple[Any, ...]:
323
329
"""Prepare a tuple of objects for printing by Rich's Console.print().
324
330
325
331
This function converts any non-Rich object whose string representation contains
326
- ANSI style codes into a rich.Text object. This ensures correct display width
327
- calculation, as Rich can then properly parse and account for the non-printing
328
- ANSI codes. All other objects are left untouched, allowing Rich's native
332
+ ANSI escape sequences into a rich.Text object. This ensures correct display width
333
+ calculation, as Rich can then properly parse and account for these non-printing
334
+ codes. All other objects are left untouched, allowing Rich's native
329
335
renderers to handle them.
330
336
331
337
:param objects: objects to prepare
@@ -342,12 +348,10 @@ def prepare_objects_for_rendering(*objects: Any) -> tuple[Any, ...]:
342
348
if isinstance (renderable , ConsoleRenderable ):
343
349
continue
344
350
345
- # Check if the object's string representation contains ANSI styles, and if so,
346
- # replace it with a Rich Text object for correct width calculation.
347
351
renderable_as_str = str (renderable )
348
- renderable_as_text = string_to_rich_text (renderable_as_str )
349
352
350
- if renderable_as_text .plain != renderable_as_str :
351
- object_list [i ] = renderable_as_text
353
+ # Check for any ANSI escape sequences in the string.
354
+ if _ANSI_ESCAPE_SEQUENCE_RE .search (renderable_as_str ):
355
+ object_list [i ] = string_to_rich_text (renderable_as_str )
352
356
353
357
return tuple (object_list )
0 commit comments