From 1d0f058240a9775be6b273289cc37e2056968df5 Mon Sep 17 00:00:00 2001 From: Wolfvin Date: Wed, 1 Jul 2026 03:44:34 +0000 Subject: [PATCH] =?UTF-8?q?fix(codelensignore):=20honor=20!-negation=20pat?= =?UTF-8?q?terns=20for=20files=20in=20ignored=20directories=20=E2=80=94=20?= =?UTF-8?q?closes=20#120?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: when a directory matched a .codelensignore pattern (e.g. 'src/'), scan.py called dirs.clear() to prune the entire subtree. This meant !-negation patterns targeting files inside the ignored directory (e.g. '!src/utils.py') were never evaluated — the files were already skipped at the directory level. Fix: when a directory matches a .codelensignore pattern, check whether any pattern in the merged 3-tier list starts with '!'. If so, keep recursing so per-file is_ignored() checks can honor the negation. If there are no negation patterns, prune as before (preserves the node_modules/ performance optimization). Verified: scan on clean_app with 'src/' + '!src/utils.py' now reports python: 3 (config/settings.py + main.py + src/utils.py) instead of 2. Per-file is_ignored() correctly returns False for src/utils.py. --- scripts/commands/scan.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/scripts/commands/scan.py b/scripts/commands/scan.py index 9f9891c7..7be6c85f 100644 --- a/scripts/commands/scan.py +++ b/scripts/commands/scan.py @@ -1523,9 +1523,30 @@ def discover_files(workspace: str, config: Dict) -> Dict[str, List[str]]: # 3-tier .codelensignore check (issue #55). Augments the existing # config-based should_ignore() above; builtin patterns cover the # historical DEFAULT_IGNORE_DIRS set so backward compat is preserved. + # + # Issue #120: do NOT ``dirs.clear()`` when the directory matches a + # .codelensignore pattern — a ``!``-negation deeper in the tree + # (e.g. ``src/`` ignored but ``!src/utils.py`` re-included) must + # still be honored. Per-file checks below will filter individual + # files; we only prune subdirectories via the config-based + # ``should_ignore`` above (which has no negation semantics). if _codelensignore_is_ignored is not None and _codelensignore_is_ignored(rel_root, workspace): - dirs.clear() - continue + # Don't recurse into subdirectories of an ignored directory + # ONLY when there are no negation patterns that could re-include + # descendants. Quick heuristic: if any pattern starts with ``!``, + # keep recursing so per-file negation works. Otherwise prune + # for performance (avoids walking node_modules/ subtrees). + _has_negation = False + try: + from codelensignore import load_patterns + _pats = load_patterns(workspace) + _has_negation = any(p.startswith('!') for p in _pats) + except Exception: + _has_negation = True # Safe default: keep recursing + if not _has_negation: + dirs.clear() + continue + # Else: fall through and let per-file checks filter if '.codelens' in root: dirs.clear()