Skip to content
Merged
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 VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.5.181
0.5.182
2 changes: 1 addition & 1 deletion code2llm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
and entity resolution with multilingual support.
"""

__version__ = "0.5.181"
__version__ = "0.5.182"
__author__ = "STTS Project"

# Core analysis components (lightweight, always needed)
Expand Down
6 changes: 5 additions & 1 deletion code2llm/core/analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,12 +320,16 @@ def _collect_files(self, project_path: Path) -> List[Tuple[str, str]]:
project_name = project_path.name

# Fast path: native Rust directory traversal
skip_dirs = list(
_SKIP_DIR_NAMES
| getattr(self.file_filter, "_simple_dir_excludes", set())
)
native_files = native_walk_project_files(
project_str,
extensions=list(ALL_EXTENSIONS),
filenames=list(ALL_FILENAMES),
filename_prefixes=list(LANGUAGE_FILENAME_PREFIXES),
skip_dirs=list(_SKIP_DIR_NAMES),
skip_dirs=skip_dirs,
respect_gitignore=getattr(self.config.filters, "gitignore_enabled", True),
)
if native_files is not None:
Expand Down
52 changes: 46 additions & 6 deletions code2llm/core/file_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,15 @@
".serverless",
".netlify",
".vercel",
# Additional heavy directories and vendor bundles
"c2004",
"c2004-git-backup",
"workshop",
"managed_components",
"vendor",
".subactor",
".worktrees",
".planfile",
}
)
| ARCHIVE_DIR_NAMES
Expand All @@ -83,13 +92,31 @@ def __init__(self, config: FilterConfig, project_path: Path = None):
# Split patterns into fast substring checks vs pre-compiled regex
self._simple_excludes = []
self._regex_excludes = []
self._simple_dir_excludes: set[str] = set()
wildcard_patterns = []
for p in config.exclude_patterns:
p_lower = p.lower()
p_dir = p_lower.removesuffix("/**").removesuffix("/*").rstrip("/")
if p_dir and not any(c in p_dir for c in ("*", "?", "[")):
self._simple_dir_excludes.add(p_dir)
if "/" in p_dir:
self._simple_dir_excludes.add(p_dir.split("/")[-1])
if any(c in p_lower for c in ("*", "?", "[")):
self._regex_excludes.append(re.compile(fnmatch.translate(p_lower)))
wildcard_patterns.append(p_lower)
else:
self._simple_excludes.append(p_lower)

if wildcard_patterns:
try:
self._combined_regex_excludes = re.compile(
"|".join(f"(?:{fnmatch.translate(p)})" for p in wildcard_patterns)
)
except Exception:
self._combined_regex_excludes = None
else:
self._combined_regex_excludes = None

self._simple_includes = []
self._regex_includes = []
for p in config.include_patterns:
Expand All @@ -107,7 +134,11 @@ def __init__(self, config: FilterConfig, project_path: Path = None):
def should_skip_dir(self, dirname: str) -> bool:
"""Fast O(1) check: skip this directory entirely during tree walk?"""
lower = dirname.lower()
return lower.startswith(".") or lower in _SKIP_DIR_NAMES
return (
lower.startswith(".")
or lower in _SKIP_DIR_NAMES
or lower in self._simple_dir_excludes
)

def _passes_gitignore(self, file_path: str) -> bool:
"""Check if file passes gitignore patterns (True = pass, False = excluded)."""
Expand All @@ -124,14 +155,23 @@ def _passes_excludes(
if pattern in path_lower or (rel_lower and pattern in rel_lower):
return False

# Pre-compiled wildcard excludes
for regex in self._regex_excludes:
# Fast combined regex check
if self._combined_regex_excludes is not None:
if (
regex.match(path_lower)
or regex.match(basename_lower)
or (rel_lower and regex.match(rel_lower))
self._combined_regex_excludes.match(path_lower)
or self._combined_regex_excludes.match(basename_lower)
or (rel_lower and self._combined_regex_excludes.match(rel_lower))
):
return False
else:
# Pre-compiled wildcard excludes
for regex in self._regex_excludes:
if (
regex.match(path_lower)
or regex.match(basename_lower)
or (rel_lower and regex.match(rel_lower))
):
return False

return True

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "code2llm"
version = "0.5.181"
version = "0.5.182"
description = "High-performance Python code flow analysis with optimized TOON format - CFG, DFG, call graphs, and intelligent code queries"
readme = "README.md"
requires-python = ">=3.10"
Expand Down
33 changes: 33 additions & 0 deletions tests/test_edge_cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,39 @@ def test_skip_properties(self):

assert filter_obj.should_skip_function(3, is_property=True)

def test_should_skip_dir(self):
"""Test directory skipping for built-in and custom exclude patterns."""
config = FilterConfig(exclude_patterns=["custom_heavy/**", "workshop/*", "vendor_bundle/"])
filter_obj = FastFileFilter(config)

# Built-in heavy dirs
assert filter_obj.should_skip_dir("c2004")
assert filter_obj.should_skip_dir("c2004-git-backup")
assert filter_obj.should_skip_dir("node_modules")
assert filter_obj.should_skip_dir(".git")
assert filter_obj.should_skip_dir(".subactor")

# Custom exclude pattern extracted dirs
assert filter_obj.should_skip_dir("custom_heavy")
assert filter_obj.should_skip_dir("workshop")
assert filter_obj.should_skip_dir("vendor_bundle")

# Normal dirs should not be skipped
assert not filter_obj.should_skip_dir("src")
assert not filter_obj.should_skip_dir("app")
assert not filter_obj.should_skip_dir("lib")
assert not filter_obj.should_skip_dir("my_module")

def test_combined_regex_excludes(self):
"""Test combined regex exclude filter matching."""
config = FilterConfig(exclude_patterns=["*.tmp", "*_backup.*", "legacy/**"])
filter_obj = FastFileFilter(config)

assert not filter_obj.should_process("temp/test.tmp")
assert not filter_obj.should_process("code_backup.py")
assert not filter_obj.should_process("legacy/old.py")
assert filter_obj.should_process("app/main.py")


class TestNLPEdgeCases:
"""Test NLP pipeline edge cases."""
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading