Remove obsolete glob pattern support in search_text, removing duplicate glob translation

This commit is contained in:
Dominik Jain authored and Dominik Jain committed 2026-07-23 14:33:06 +02:00
1 parent 52cf8552a0
commit 33125287dc
2 files changed
+1 -118

No files matched your search

-28
View File
@@ -117,34 +117,12 @@ class MatchedConsecutiveLines:
return cls(lines=text_lines, source_file_path=source_file_path)
def glob_to_regex(glob_pat: str) -> str:
regex_parts: list[str] = []
i = 0
while i < len(glob_pat):
ch = glob_pat[i]
if ch == "*":
regex_parts.append(".*")
elif ch == "?":
regex_parts.append(".")
elif ch == "\\":
i += 1
if i < len(glob_pat):
regex_parts.append(re.escape(glob_pat[i]))
else:
regex_parts.append("\\")
else:
regex_parts.append(re.escape(ch))
i += 1
return "".join(regex_parts)
def search_text(
pattern: str,
content: str | None = None,
source_file_path: str | None = None,
context_lines_before: int = 0,
context_lines_after: int = 0,
is_glob: bool = False,
multiline: bool = True,
) -> list[MatchedConsecutiveLines]:
"""
@@ -156,8 +134,6 @@ def search_text(
this has to be passed and the file will be read.
:param context_lines_before: Number of context lines to include before matches
:param context_lines_after: Number of context lines to include after matches
:param is_glob: If True, pattern is treated as a glob-like pattern (e.g., "*.py", "test_??.py")
and will be converted to regex internally
:param multiline: whether to apply multi-line matching, enabling the flags re.DOTALL and re.MULTILINE
:return: List of `TextSearchMatch` objects
:raises: ValueError if the pattern is not valid
@@ -173,10 +149,6 @@ def search_text(
lines = TextUtils.split_lines(content)
total_lines = len(lines)
# Convert pattern to a compiled regex if it's a string
if is_glob:
pattern = glob_to_regex(pattern)
# For multiline matches, optionally use DOTALL so '.' matches newlines
flags = (re.MULTILINE | re.DOTALL) if multiline else 0
compiled_pattern = re.compile(pattern, flags)
+1 -90
View File
@@ -1,10 +1,9 @@
import re
from collections.abc import Callable
import pytest
from serena.util.file_proxy import FileCollection, FileProxy
from serena.util.text_utils import LineType, MultiFileContentReplacer, glob_to_regex, search_files, search_text
from serena.util.text_utils import LineType, MultiFileContentReplacer, search_files, search_text
class TestSearchText:
@@ -177,94 +176,6 @@ class TestSearchText:
match_lines = [line for line in multiline_match.lines if line.match_type == LineType.MATCH]
assert len(match_lines) >= 3
def test_search_text_with_glob_pattern(self):
"""Test searching with glob-like patterns."""
content = """
class UserService:
def get_user(self, user_id):
return {"id": user_id, "name": "Test User"}
def create_user(self, user_data):
print(f"Creating user: {user_data}")
return {"id": 123, **user_data}
def update_user(self, user_id, user_data):
print(f"Updating user {user_id} with {user_data}")
return True
"""
# Search with a glob pattern for all user methods
matches = search_text("*_user*", content=content, is_glob=True, multiline=False)
assert len(matches) == 3
assert "get_user" in matches[0].lines[0].line_content
assert "create_user" in matches[1].lines[0].line_content
assert "update_user" in matches[2].lines[0].line_content
def test_search_text_with_complex_glob_pattern(self):
"""Test searching with more complex glob patterns."""
content = """
def process_data(data):
return [transform(item) for item in data]
def transform(item):
if isinstance(item, dict):
return {k: v.upper() if isinstance(v, str) else v for k, v in item.items()}
elif isinstance(item, list):
return [x * 2 for x in item if isinstance(x, (int, float))]
elif isinstance(item, str):
return item.upper()
else:
return item
"""
# Search with a simplified glob pattern to find all isinstance occurrences
matches = search_text("*isinstance*", content=content, is_glob=True, multiline=False)
# Should match lines with isinstance(item, dict) and isinstance(item, list)
assert len(matches) >= 2
instance_matches = [
line.line_content
for match in matches
for line in match.lines
if line.match_type == LineType.MATCH and "isinstance(item," in line.line_content
]
assert len(instance_matches) >= 2
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, multiline=False)
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, multiline=False)
assert len(matches_curly) == 1
assert "{bar}" in matches_curly[0].lines[0].line_content
def test_glob_to_regex_question_matches_single_char(self):
"""A glob '?' matches exactly one character, matching glob_match's documented semantics."""
rx = glob_to_regex("a?c")
assert re.fullmatch(rx, "abc") is not None
assert re.fullmatch(rx, "ac") is None
assert re.fullmatch(rx, "abbc") is None
def test_search_text_glob_question_single_char(self):
"""search_text('c?t', is_glob=True) must match 'cat' (one char), not 'coat' (two chars)."""
content = "value_cat_end\nvalue_coat_end"
matches = search_text("c?t", content=content, is_glob=True, multiline=False)
assert len(matches) == 1
assert "value_cat_end" in matches[0].lines[0].line_content
def test_search_text_no_matches(self):
"""Test searching with a pattern that doesn't match anything."""
content = """