Entropic 2.14.3
Local-first agentic inference engine
Loading...
Searching...
No Matches
entropic::IgnoreMatcher Class Reference

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).
 

Detailed Description

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.

Version
2.13.0

Definition at line 68 of file ignore_matcher.h.

Constructor & Destructor Documentation

◆ IgnoreMatcher()

entropic::IgnoreMatcher::IgnoreMatcher ( )
default

Construct an empty matcher (no rules).

Version
2.1.4

Member Function Documentation

◆ add_pattern()

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.

Parameters
patternGitignore-syntax pattern.
baseAnchor directory (relative to root). Empty means "anchored at root."
Version
2.1.4

Comments and blank lines are skipped here as well as on the file path, so a caller cannot inject a rule that matches everything.

Parameters
patternGitignore-syntax line.
baseDirectory the rule is anchored at, relative to the root. @req REQ-MCP-022
Version
2.13.0

Definition at line 423 of file ignore_matcher.cpp.

◆ is_ignored()

bool entropic::IgnoreMatcher::is_ignored ( const std::string &  rel_path,
bool  is_dir 
) const

Test whether a path is ignored.

Test a path against the rule set (last-match-wins for negation).

Parameters
rel_pathForward-slash, relative-to-root path. Must NOT start with /. Trailing slash optional.
is_dirTrue if the path refers to a directory.
Returns
true if the path matches an active (non-negated) exclusion rule.
Version
2.1.4

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.

Parameters
rel_pathPath relative to the workspace root, forward-slashed.
is_dirWhether the path names a directory — a dir/ pattern matches the directory and its descendants but not a same-named regular file.
Returns
true when the LAST matching rule excludes the path; false when it re-includes it or nothing matched. @req REQ-MCP-022
Version
2.13.0

Definition at line 707 of file ignore_matcher.cpp.

◆ is_skipped_dir_name()

bool entropic::IgnoreMatcher::is_skipped_dir_name ( const std::string &  name)
static

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.

Parameters
nameBare directory name (no separators).
Returns
true when the directory must never be descended into. @utility
Version
2.13.0

.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.

Parameters
nameBare directory name.
Returns
true when the directory must never be descended into. @req REQ-MCP-022
Version
2.13.0

Definition at line 107 of file ignore_matcher.cpp.

◆ load()

void entropic::IgnoreMatcher::load ( const std::filesystem::path &  root)

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.

Parameters
rootWorkspace root directory (must exist; non-canonical paths are accepted).
Version
2.13.0

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 !.

Parameters
rootWorkspace root to load from; a missing or non-directory root leaves the matcher empty and is logged. @req REQ-MCP-022
Version
2.13.0

Definition at line 472 of file ignore_matcher.cpp.

◆ regex_evals()

std::uint64_t entropic::IgnoreMatcher::regex_evals ( ) const
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.

Returns
Cumulative regex evaluations. @utility
Version
2.13.0

Definition at line 147 of file ignore_matcher.h.

◆ reset_regex_evals()

void entropic::IgnoreMatcher::reset_regex_evals ( ) const
inline

Reset the regex-evaluation counter (gh#161, test surface).

@utility

Version
2.13.0

Definition at line 156 of file ignore_matcher.h.

◆ rule_count()

std::size_t entropic::IgnoreMatcher::rule_count ( ) const
inline

Number of compiled rules (test surface).

Returns
Rule count. @utility
Version
2.1.4

Definition at line 130 of file ignore_matcher.h.


The documentation for this class was generated from the following files: