From e19b0b6d34dca5d91b56aa57da1f1b8573f9564b Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Fri, 13 Jun 2025 11:54:56 +0200 Subject: [PATCH] Fix glob pattern escaping --- src/serena/text_utils.py | 16 +++------------- test/serena/test_text_utils.py | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/serena/text_utils.py b/src/serena/text_utils.py index 338b2956..380eb911 100644 --- a/src/serena/text_utils.py +++ b/src/serena/text_utils.py @@ -153,19 +153,9 @@ def search_text( # Convert pattern to a compiled regex if it's a string if is_glob and isinstance(pattern, str): - # Convert glob pattern to regex - # Escape all regex special characters except * and ? - regex_special_chars = r"\^$.|+()[{" - escaped_pattern = "" - for char in pattern: - if char in regex_special_chars: - escaped_pattern += "\\" + char - elif char == "*": - escaped_pattern += ".*" - elif char == "?": - escaped_pattern += "." - else: - escaped_pattern += char + # Convert glob pattern to regex. Use re.escape to handle special + # characters and replace glob wildcards afterwards. + escaped_pattern = "".join(".*" if ch == "*" else "." if ch == "?" else re.escape(ch) for ch in pattern) # For glob patterns, don't anchor with ^ and $ to allow partial line matches compiled_pattern = re.compile(escaped_pattern) elif isinstance(pattern, str): diff --git a/test/serena/test_text_utils.py b/test/serena/test_text_utils.py index 1fa6929b..860ce6aa 100644 --- a/test/serena/test_text_utils.py +++ b/test/serena/test_text_utils.py @@ -182,6 +182,24 @@ class TestSearchText: assert any("isinstance(item, dict)" in line for line in instance_matches) assert any("isinstance(item, list)" in line for line in instance_matches) + def test_search_text_glob_with_special_chars(self): + """Glob patterns containing regex special characters should match literally.""" + content = """ + def func_square(): + print("value[42]") + + def func_curly(): + print("value{bar}") + """ + + matches_square = search_text(r"*\[42\]*", content=content, is_glob=True) + assert len(matches_square) == 1 + assert "[42]" in matches_square[0].lines[0].line_content + + matches_curly = search_text("*{bar}*", content=content, is_glob=True) + assert len(matches_curly) == 1 + assert "{bar}" in matches_curly[0].lines[0].line_content + def test_search_text_no_matches(self): """Test searching with a pattern that doesn't match anything.""" content = """