Skip to content

Commit 0b4c9a1

Browse files
fix(chats): escape regex in search highlight and fix repr'd output (gptme#1557)
* fix(chats): escape regex in search highlight and fix repr'd output Two bugs in _format_message_with_context: 1. re.sub used raw query without re.escape, crashing on regex metacharacters like (, ), [, +, *, . 2. !r in f-string wrapped matched text in quotes (e.g. 'python' instead of python) Also add re.IGNORECASE for consistent case-insensitive highlighting. * fix(chats): remove redundant re.DOTALL flag and strengthen test assertion --------- Co-authored-by: TimeToBuildBob <TimeToBuildBob@users.noreply.github.com>
1 parent 50539e7 commit 0b4c9a1

File tree

2 files changed

+43
-7
lines changed

2 files changed

+43
-7
lines changed

gptme/tools/chats.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -170,15 +170,12 @@ def _format_message_with_context(
170170
context_suffix = context[match_end:].split("\n", 1)[0]
171171
context = f"{context_prefix}{context[match_start:match_end]}{context_suffix}"
172172

173-
# highlighted = f"{prefix}{context_prefix}\033[1m{context[match_start:match_end]}\033[0m{context_suffix}{suffix}"
174173
highlighted = f"{prefix}{context}{suffix}"
175174
highlighted = re.sub(
176-
query,
177-
lambda m: (
178-
f"\033[1;31m{m.group(0)!r}\033[0m"
179-
), # not sure why !r is needed to avoid linting error
175+
re.escape(query),
176+
lambda m: f"\033[1;31m{m.group(0)}\033[0m",
180177
highlighted,
181-
flags=re.DOTALL,
178+
flags=re.IGNORECASE,
182179
)
183180
formatted_matches.append(highlighted)
184181

tests/test_tools_chats.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from gptme.tools import init_tools
2-
from gptme.tools.chats import list_chats, search_chats
2+
from gptme.tools.chats import _format_message_with_context, list_chats, search_chats
33

44

55
def test_chats(capsys):
@@ -15,3 +15,42 @@ def test_chats(capsys):
1515
search_chats("python", system=True)
1616
captured = capsys.readouterr()
1717
assert "Search results" in captured.out
18+
19+
20+
def test_format_message_basic():
21+
"""Test basic match highlighting."""
22+
result = _format_message_with_context("hello world", "world")
23+
assert "world" in result
24+
# Should contain ANSI bold red escape codes
25+
assert "\033[1;31m" in result
26+
27+
28+
def test_format_message_no_match():
29+
"""Test fallback when no match found."""
30+
result = _format_message_with_context("hello world", "xyz")
31+
assert result == "hello world"
32+
33+
34+
def test_format_message_regex_special_chars():
35+
"""Test that regex special characters in query don't crash."""
36+
# These all contain regex metacharacters
37+
for query in ["myFunc(", "file.txt", "a+b", "foo[0]", "x*y", "a|b"]:
38+
content = f"some text with {query} in it"
39+
result = _format_message_with_context(content, query)
40+
assert query in result
41+
42+
43+
def test_format_message_no_repr_quotes():
44+
"""Test that matches are not wrapped in repr quotes."""
45+
result = _format_message_with_context("hello world", "world")
46+
# Should NOT contain repr-style quotes around the match
47+
assert "'world'" not in result
48+
49+
50+
def test_format_message_case_insensitive_highlight():
51+
"""Test that highlighting works case-insensitively and preserves original casing."""
52+
result = _format_message_with_context("Hello WORLD", "world")
53+
# The ANSI highlight should be present
54+
assert "\033[1;31m" in result
55+
# Original casing must be preserved inside the highlight (not lowercased)
56+
assert "WORLD" in result

0 commit comments

Comments
 (0)