Skip to content

Commit a05d071

Browse files
committed
Sanitize whitespace characters in CompletionItem display fields to ensure correct rendering in the completion menu.
1 parent b726c52 commit a05d071

2 files changed

Lines changed: 33 additions & 1 deletion

File tree

cmd2/completion.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,15 @@ class CompletionItem:
7676
display_plain: str = field(init=False)
7777
display_meta_plain: str = field(init=False)
7878

79+
@staticmethod
80+
def _sanitize_display_string(val: str) -> str:
81+
"""Sanitize a string for display in the completion menu.
82+
83+
This replaces whitespace characters that are rendered as
84+
control sequences (like ^J or ^I) with spaces.
85+
"""
86+
return re.sub(r'\r\n|[\n\r\t\f\v]', ' ', val)
87+
7988
def __post_init__(self) -> None:
8089
"""Finalize the object after initialization."""
8190
# Derive text from value if it wasn't explicitly provided
@@ -86,7 +95,11 @@ def __post_init__(self) -> None:
8695
if not self.display:
8796
object.__setattr__(self, "display", self.text)
8897

89-
# Pre-calculate plain text versions by stripping ANSI sequences.
98+
# Sanitize display and display_meta
99+
object.__setattr__(self, "display", self._sanitize_display_string(self.display))
100+
object.__setattr__(self, "display_meta", self._sanitize_display_string(self.display_meta))
101+
102+
# Create plain text versions by stripping ANSI sequences.
90103
# These are stored as attributes for fast access during sorting/filtering.
91104
object.__setattr__(self, "display_plain", su.strip_style(self.display))
92105
object.__setattr__(self, "display_meta_plain", su.strip_style(self.display_meta))

tests/test_completion.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -932,6 +932,25 @@ def test_plain_fields() -> None:
932932
assert completion_item.display_meta_plain == "A tasty apple"
933933

934934

935+
def test_sanitization() -> None:
936+
"""Test display string sanitization in CompletionItem."""
937+
# Test all problematic characters being replaced by a single space.
938+
# Also verify that \r\n is replaced by a single space.
939+
display = "str1\r\nstr2\nstr3\rstr4\tstr5\fstr6\vstr7"
940+
expected = "str1 str2 str3 str4 str5 str6 str7"
941+
942+
# Since display defaults to text if not provided, we test both text and display fields
943+
completion_item = CompletionItem("item", display=display, display_meta=display)
944+
assert completion_item.display == expected
945+
assert completion_item.display_meta == expected
946+
947+
# Verify that text derived display is also sanitized
948+
text = "item\nwith\nnewlines"
949+
expected_text_display = "item with newlines"
950+
completion_item = CompletionItem(text)
951+
assert completion_item.display == expected_text_display
952+
953+
935954
def test_styled_completion_sort() -> None:
936955
"""Test that sorting is done with the display_plain field."""
937956

0 commit comments

Comments
 (0)