Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ If working through CoreCoder was useful, here are a few other tools I've built a

## Contributing / License

Before you send anything, run `pytest tests/ -q` (86 tests), `ruff check`, and `compileall`, and make sure they're green. MIT licensed: fork it, learn from it, ship something better. A mention of this project is appreciated.
Before you send anything, run `pytest tests/ -q` (87 tests), `ruff check`, and `compileall`, and make sure they're green. MIT licensed: fork it, learn from it, ship something better. A mention of this project is appreciated.

---

Expand Down
2 changes: 1 addition & 1 deletion README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ quit / exit 退出(Ctrl+C 取消当前回合)

## 贡献 / License

动手之前先跑一遍 `pytest tests/ -q`(86 个测试)、`ruff check` 和 `compileall`,绿了再提。MIT License,欢迎 fork 拿去造更好的东西,能在 README 里留一句出处就更好。
动手之前先跑一遍 `pytest tests/ -q`(87 个测试)、`ruff check` 和 `compileall`,绿了再提。MIT License,欢迎 fork 拿去造更好的东西,能在 README 里留一句出处就更好。

---

Expand Down
20 changes: 16 additions & 4 deletions corecoder/tools/grep.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,11 @@ def execute(self, pattern: str, path: str = ".", include: str | None = None) ->

if base.is_file():
files = [base]
scan_truncated = False
else:
files = self._walk(base, include)
files, scan_truncated = self._walk(base, include)

scan_limit_msg = "... (5000 file scan limit reached; results may be incomplete)"
matches = []
for fp in files:
try:
Expand All @@ -59,14 +61,23 @@ def execute(self, pattern: str, path: str = ".", include: str | None = None) ->
matches.append(f"{fp}:{lineno}: {line.rstrip()}")
if len(matches) >= 200:
matches.append("... (200 match limit reached)")
if scan_truncated:
matches.append(scan_limit_msg)
return "\n".join(matches)

return "\n".join(matches) if matches else "No matches found."
if matches:
if scan_truncated:
matches.append(scan_limit_msg)
return "\n".join(matches)
if scan_truncated:
return f"No matches found in scanned files.\n{scan_limit_msg}"
return "No matches found."

@staticmethod
def _walk(root: Path, include: str | None) -> list[Path]:
def _walk(root: Path, include: str | None) -> tuple[list[Path], bool]:
"""Walk dir tree, skipping junk dirs."""
results = []
truncated = False
for item in root.rglob(include or "*"):
# skip junk dirs *inside* the search root - matching item.parts would
# also catch an ancestor named e.g. "build" and hide the whole tree
Expand All @@ -75,5 +86,6 @@ def _walk(root: Path, include: str | None) -> list[Path]:
if item.is_file():
results.append(item)
if len(results) >= 5000:
truncated = True
break
return results
return results, truncated
12 changes: 12 additions & 0 deletions tests/test_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,18 @@ def test_grep_searches_under_skip_named_ancestor(tmp_path):
assert "needle" in r


def test_grep_reports_truncated_file_scan(monkeypatch, tmp_path):
"""A truncated file scan must be reported as an incomplete result."""
grep = get_tool("grep")
def fake_walk(root, include):
return [], True
monkeypatch.setattr(type(grep), "_walk", staticmethod(fake_walk))
r = grep.execute(pattern="needle", path=str(tmp_path))
assert "No matches found in scanned files." in r
assert "5000 file scan limit reached" in r
assert "results may be incomplete" in r


def test_grep_skips_junk_dirs_inside_root(tmp_path):
"""Junk dirs *inside* the search root are still skipped."""
(tmp_path / "node_modules").mkdir()
Expand Down