Skip to content

Commit 0281e0c

Browse files
committed
Updated rich_utils.ANSI_STYLE_SEQUENCE_RE to only match ANSI SGR (Select Graphic Rendition) sequences for text styling.
1 parent 709245f commit 0281e0c

File tree

4 files changed

+53
-8
lines changed

4 files changed

+53
-8
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ prompt is displayed.
6969
- **max_column_completion_results**: (int) the maximum number of completion results to
7070
display in a single column
7171

72+
## 3.2.2 (February 21, 2026)
73+
74+
- Bug Fixes
75+
- Updated `rich_utils.ANSI_STYLE_SEQUENCE_RE` to only match ANSI SGR (Select Graphic Rendition)
76+
sequences for text styling.
77+
7278
## 3.2.1 (February 21, 2026)
7379

7480
- Bug Fixes

cmd2/rich_utils.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,11 @@
3030

3131
from .styles import DEFAULT_CMD2_STYLES
3232

33-
# A compiled regular expression to detect ANSI style sequences.
34-
ANSI_STYLE_SEQUENCE_RE = re.compile(r"\x1b\[[0-9;?]*m")
33+
# Matches ANSI SGR (Select Graphic Rendition) sequences for text styling.
34+
# \x1b[ - the CSI (Control Sequence Introducer)
35+
# [0-9;]* - zero or more digits or semicolons (parameters for the style)
36+
# m - the SGR final character
37+
ANSI_STYLE_SEQUENCE_RE = re.compile(r"\x1b\[[0-9;]*m")
3538

3639

3740
class AllowStyle(Enum):

cmd2/string_utils.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,10 @@ def stylize(val: str, style: StyleType) -> str:
9595

9696

9797
def strip_style(val: str) -> str:
98-
"""Strip all ANSI style sequences from a string.
98+
"""Strip all ANSI style sequences (colors, bold, etc.) from a string.
99+
100+
This targets SGR sequences specifically and leaves other terminal
101+
control codes intact.
99102
100103
:param val: string to be stripped
101104
:return: the stripped string

tests/test_string_utils.py

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -126,11 +126,44 @@ def test_stylize() -> None:
126126
assert restyled_string == "\x1b[1;4;9;32;44mHello, world!\x1b[0m"
127127

128128

129-
def test_strip_style() -> None:
130-
base_str = HELLO_WORLD
131-
styled_str = su.stylize(base_str, style=Color.GREEN)
132-
assert base_str != styled_str
133-
assert base_str == su.strip_style(styled_str)
129+
def test_strip_basic_styles() -> None:
130+
# Test bold, colors, and resets
131+
assert su.strip_style("\x1b[1mBold\x1b[0m") == "Bold"
132+
assert su.strip_style("\x1b[31mRed\x1b[0m") == "Red"
133+
assert su.strip_style("\x1b[4;32mUnderline Green\x1b[0m") == "Underline Green"
134+
135+
136+
def test_strip_complex_colors() -> None:
137+
# Test 256-color and RGB (TrueColor) sequences
138+
# These use semicolons as separators and end in 'm'
139+
assert su.strip_style("\x1b[38;5;208mOrange\x1b[0m") == "Orange"
140+
assert su.strip_style("\x1b[38;2;255;87;51mCustom RGB\x1b[0m") == "Custom RGB"
141+
142+
143+
def test_preserve_non_style_csi() -> None:
144+
# Test that cursor movements and other CSI codes are not stripped
145+
# Cursor Up (\x1b[A) and Cursor Position (\x1b[10;5H)
146+
cursor_up = "\x1b[A"
147+
cursor_pos = "\x1b[10;5H"
148+
assert su.strip_style(cursor_up) == cursor_up
149+
assert su.strip_style(cursor_pos) == cursor_pos
150+
151+
152+
def test_preserve_private_modes() -> None:
153+
# Test that DEC private modes (containing '?') are not stripped
154+
# Hide cursor (\x1b[?25l) and Show cursor (\x1b[?25h)
155+
hide_cursor = "\x1b[?25l"
156+
show_cursor = "\x1b[?25h"
157+
assert su.strip_style(hide_cursor) == hide_cursor
158+
assert su.strip_style(show_cursor) == show_cursor
159+
160+
161+
def test_mixed_content() -> None:
162+
# Test a string that has both style and control sequences
163+
# Red text + Hide Cursor
164+
mixed = "\x1b[31mRed Text\x1b[0m\x1b[?25l"
165+
# Only the red style should be removed
166+
assert su.strip_style(mixed) == "Red Text\x1b[?25l"
134167

135168

136169
def test_str_width() -> None:

0 commit comments

Comments
 (0)