Skip to content

Commit 7191d19

Browse files
committed
fix(indexing): relax shallow find_files matching
1 parent c89a839 commit 7191d19

File tree

2 files changed

+85
-5
lines changed

2 files changed

+85
-5
lines changed

src/code_index_mcp/indexing/shallow_index_manager.py

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,43 @@ def find_files(self, pattern: str = "*") -> List[str]:
107107
if not isinstance(pattern, str):
108108
return []
109109
norm = (pattern.strip() or "*").replace('\\\\','/').replace('\\','/')
110-
regex = self._compile_glob_regex(norm)
111110
files = self._file_list or []
111+
112+
# Fast path: wildcard all
112113
if norm == "*":
113114
return list(files)
114-
return [f for f in files if regex.match(f) is not None]
115+
116+
# 1) Exact, case-sensitive
117+
exact_regex = self._compile_glob_regex(norm)
118+
exact_hits = [f for f in files if exact_regex.match(f) is not None]
119+
if exact_hits or '/' in norm:
120+
return exact_hits
121+
122+
# 2) Recursive **/ fallback (case-sensitive)
123+
recursive_pattern = f"**/{norm}"
124+
rec_regex = self._compile_glob_regex(recursive_pattern)
125+
rec_hits = [f for f in files if rec_regex.match(f) is not None]
126+
if rec_hits:
127+
return self._dedupe_preserve_order(exact_hits + rec_hits)
128+
129+
# 3) Case-insensitive (root only)
130+
ci_regex = self._compile_glob_regex(norm, ignore_case=True)
131+
ci_hits = [f for f in files if ci_regex.match(f) is not None]
132+
if ci_hits:
133+
return self._dedupe_preserve_order(exact_hits + rec_hits + ci_hits)
134+
135+
# 4) Case-insensitive recursive
136+
rec_ci_regex = self._compile_glob_regex(recursive_pattern, ignore_case=True)
137+
rec_ci_hits = [f for f in files if rec_ci_regex.match(f) is not None]
138+
if rec_ci_hits:
139+
return self._dedupe_preserve_order(
140+
exact_hits + rec_hits + ci_hits + rec_ci_hits
141+
)
142+
143+
return []
115144

116145
@staticmethod
117-
def _compile_glob_regex(pattern: str) -> re.Pattern:
146+
def _compile_glob_regex(pattern: str, ignore_case: bool = False) -> re.Pattern:
118147
i = 0
119148
out = []
120149
special = ".^$+{}[]|()"
@@ -134,7 +163,18 @@ def _compile_glob_regex(pattern: str) -> re.Pattern:
134163
else:
135164
out.append(c)
136165
i += 1
137-
return re.compile('^' + ''.join(out) + '$')
166+
flags = re.IGNORECASE if ignore_case else 0
167+
return re.compile('^' + ''.join(out) + '$', flags=flags)
168+
169+
@staticmethod
170+
def _dedupe_preserve_order(items: List[str]) -> List[str]:
171+
seen = set()
172+
result = []
173+
for item in items:
174+
if item not in seen:
175+
seen.add(item)
176+
result.append(item)
177+
return result
138178

139179
def cleanup(self) -> None:
140180
with self._lock:
@@ -152,4 +192,3 @@ def cleanup(self) -> None:
152192
def get_shallow_index_manager() -> ShallowIndexManager:
153193
return _shallow_manager
154194

155-
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
from pathlib import Path
2+
3+
import pytest
4+
5+
from code_index_mcp.indexing.shallow_index_manager import ShallowIndexManager
6+
7+
8+
@pytest.fixture()
9+
def temp_manager(tmp_path):
10+
project = Path("test/sample-projects/python/user_management").resolve()
11+
m = ShallowIndexManager()
12+
assert m.set_project_path(str(project))
13+
assert m.build_index()
14+
assert m.load_index()
15+
yield m
16+
m.cleanup()
17+
18+
19+
def test_simple_filename_triggers_recursive(temp_manager):
20+
res = temp_manager.find_files("user.py")
21+
assert "models/user.py" in res
22+
23+
24+
def test_case_insensitive(temp_manager):
25+
res = temp_manager.find_files("USER.PY")
26+
assert "models/user.py" in [p.lower() for p in res]
27+
28+
29+
def test_pattern_with_slash_not_lenient(temp_manager):
30+
res = temp_manager.find_files("models/user.py")
31+
assert res == ["models/user.py"]
32+
33+
34+
def test_wildcard_all_unchanged(temp_manager):
35+
res = temp_manager.find_files("*")
36+
# sample project has 12 files
37+
assert len(res) == 12
38+
39+
40+
def test_non_string_returns_empty(temp_manager):
41+
assert temp_manager.find_files(None) == [] # type: ignore[arg-type]

0 commit comments

Comments
 (0)