|
Entropic 2.14.3
Local-first agentic inference engine
|
gitignore-style path matcher (#15, v2.1.4). More...
#include <entropic/mcp/servers/ignore_matcher.h>
Classes | |
| struct | Rule |
| One compiled gitignore rule (public so internal helpers in the .cpp can construct Rules without friend declarations). More... | |
Public Member Functions | |
| IgnoreMatcher ()=default | |
| Construct an empty matcher (no rules). | |
| void | load (const std::filesystem::path &root) |
| Load gitignore + explorerignore from a workspace root. | |
| void | add_pattern (const std::string &pattern, const std::filesystem::path &base={}) |
| Add a single pattern programmatically (test surface). | |
| bool | is_ignored (const std::string &rel_path, bool is_dir) const |
| Test whether a path is ignored. | |
| std::size_t | rule_count () const |
| Number of compiled rules (test surface). | |
| std::uint64_t | regex_evals () const |
| Regex evaluations performed since the last reset (gh#161). | |
| void | reset_regex_evals () const |
| Reset the regex-evaluation counter (gh#161, test surface). | |
Static Public Member Functions | |
| static bool | is_skipped_dir_name (const std::string &name) |
| Directory names never walked, by name alone (gh#161). | |
gitignore-style path matcher (#15, v2.1.4).
Single instance owns a compiled rule set, ordered, with last-match-wins semantics so that negation patterns (!keep.log) can re-include paths that earlier patterns excluded.
is_ignored was O(all rules) per call until gh#161 (v2.13.0): on a repository vendoring boost/opencv/pcl that meant 5,061 rules x 2 regexes for every path, and one recursive glob over header files took 87 seconds. It is now O(rules anchored on the path's own ancestry), because a rule loaded from deps/boost/libs/.gitignore cannot match anything outside deps/boost/libs/, and it stops at the first match found walking that subset BACKWARDS — which is the last match in source order, so the semantics above are unchanged.
Definition at line 68 of file ignore_matcher.h.
|
default |
Construct an empty matcher (no rules).
| void entropic::IgnoreMatcher::add_pattern | ( | const std::string & | pattern, |
| const std::filesystem::path & | base = {} |
||
| ) |
Add a single pattern programmatically (test surface).
Add a pattern programmatically.
| pattern | Gitignore-syntax pattern. |
| base | Anchor directory (relative to root). Empty means "anchored at root." |
Comments and blank lines are skipped here as well as on the file path, so a caller cannot inject a rule that matches everything.
| pattern | Gitignore-syntax line. |
| base | Directory the rule is anchored at, relative to the root. @req REQ-MCP-022 |
Definition at line 423 of file ignore_matcher.cpp.
Test whether a path is ignored.
Test a path against the rule set (last-match-wins for negation).
| rel_path | Forward-slash, relative-to-root path. Must NOT start with /. Trailing slash optional. |
| is_dir | True if the path refers to a directory. |
Matching is path-relative, never filename-only, and negation stays order-sensitive by construction. gh#161 changed HOW that order is walked, never WHAT it means: the scan visits only the buckets on the path's own ancestry, in descending global rule order, and stops at the first match — which is by definition the last matching rule in source order. Before, every rule in the workspace was evaluated twice for every path: 5,061 rules against 187,855 files is the 87 s glob the issue reported.
| rel_path | Path relative to the workspace root, forward-slashed. |
| is_dir | Whether the path names a directory — a dir/ pattern matches the directory and its descendants but not a same-named regular file. |
Definition at line 707 of file ignore_matcher.cpp.
Directory names never walked, by name alone (gh#161).
Single source of truth for the hardcoded skip list: the .gitignore DISCOVERY walk here and the glob/grep walk in filesystem.cpp both consult it, so the two cannot drift. These directories hold no rules a workspace search should honour and are the largest entry counts in a typical tree.
| name | Bare directory name (no separators). |
.git alone is tens of thousands of entries on a large repository and holds nothing a workspace search may return; the other three are the same story for dependency and cache trees. Before gh#161 the DISCOVERY walk honoured none of them — it walked the whole tree looking for .gitignore files, contradicting its own comment — while the glob/grep walk kept an identical list privately in filesystem.cpp. One list, one owner.
| name | Bare directory name. |
Definition at line 107 of file ignore_matcher.cpp.
Load gitignore + explorerignore from a workspace root.
Load all .gitignore files (recursive) + .explorerignore.
Walks root recursively for .gitignore files and loads them with their per-directory anchoring. Then loads <root>/.explorerignore (if present) anchored at root. Existing rules are cleared first.
The discovery walk PRUNES (gh#161): it never descends into a skip-list directory, nor into one the rules loaded so far already exclude — matching git, which cannot re-include anything below an excluded directory. Note the ordering consequence: .explorerignore is layered last so it can override gitignore, which also means its exclusions are not yet known during discovery and therefore do not prune it.
Safe to call repeatedly when the working directory changes.
| root | Workspace root directory (must exist; non-canonical paths are accepted). |
Clears any prior rules first, so load() is safe to call again when the working directory changes. The root .explorerignore is layered LAST, and matching is last-match-wins, so a workspace can both add exclusions and re-include with !.
| root | Workspace root to load from; a missing or non-directory root leaves the matcher empty and is logged. @req REQ-MCP-022 |
Definition at line 472 of file ignore_matcher.cpp.
|
inline |
Regex evaluations performed since the last reset (gh#161).
Deterministic instrumentation: every std::regex_match this matcher runs increments it exactly once, so a test can assert WORK DONE rather than wall-clock time (which is machine-load dependent and therefore untestable). A relaxed atomic add next to a regex match is free in any measurable sense, so the counter is compiled in unconditionally rather than behind a test macro that would let the instrumented and shipped paths diverge.
Definition at line 147 of file ignore_matcher.h.
|
inline |
Reset the regex-evaluation counter (gh#161, test surface).
@utility
Definition at line 156 of file ignore_matcher.h.
|
inline |
Number of compiled rules (test surface).
Definition at line 130 of file ignore_matcher.h.