Mercurial > hg-stable
changeset 32492:a04bc55201c3
match: extract base class for matchers
We will soon start splitting up the current matcher class into more
specialized classes, so we'll want a base class for all the things
that don't vary much between different matchers.
author | Martin von Zweigbergk <martinvonz@google.com> |
---|---|
date | Wed, 17 May 2017 23:45:13 -0700 |
parents | 2def402bd16d |
children | 9f781f43f2ce |
files | mercurial/match.py |
diffstat | 1 files changed, 92 insertions(+), 73 deletions(-) [+] |
line wrap: on
line diff
--- a/mercurial/match.py Mon May 22 11:08:52 2017 -0700 +++ b/mercurial/match.py Wed May 17 23:45:13 2017 -0700 @@ -202,19 +202,107 @@ kindpats.append((kind, pat, '')) return kindpats -class matcher(object): +class basematcher(object): + + def __init__(self, root, cwd, badfn=None): + self._root = root + self._cwd = cwd + if badfn is not None: + self.bad = badfn + self._files = [] # exact files and roots of patterns + self.matchfn = lambda f: False + + def __call__(self, fn): + return self.matchfn(fn) + def __iter__(self): + for f in self._files: + yield f + # Callbacks related to how the matcher is used by dirstate.walk. + # Subscribers to these events must monkeypatch the matcher object. + def bad(self, f, msg): + '''Callback from dirstate.walk for each explicit file that can't be + found/accessed, with an error message.''' + pass + + # If an explicitdir is set, it will be called when an explicitly listed + # directory is visited. + explicitdir = None + + # If an traversedir is set, it will be called when a directory discovered + # by recursive traversal is visited. + traversedir = None + + def abs(self, f): + '''Convert a repo path back to path that is relative to the root of the + matcher.''' + return f + + def rel(self, f): + '''Convert repo path back to path that is relative to cwd of matcher.''' + return util.pathto(self._root, self._cwd, f) + + def uipath(self, f): + '''Convert repo path to a display path. If patterns or -I/-X were used + to create this matcher, the display path will be relative to cwd. + Otherwise it is relative to the root of the repo.''' + return self.rel(f) + + def files(self): + '''Explicitly listed files or patterns or roots: + if no patterns or .always(): empty list, + if exact: list exact files, + if not .anypats(): list all files and dirs, + else: optimal roots''' + return self._files + + @propertycache + def _fileset(self): + return set(self._files) + + def exact(self, f): + '''Returns True if f is in .files().''' + return f in self._fileset + + def visitdir(self, dir): + '''Decides whether a directory should be visited based on whether it + has potential matches in it or one of its subdirectories. This is + based on the match's primary, included, and excluded patterns. + + Returns the string 'all' if the given directory and all subdirectories + should be visited. Otherwise returns True or False indicating whether + the given directory should be visited. + + This function's behavior is undefined if it has returned False for + one of the dir's parent directories. + ''' + return False + + def anypats(self): + '''Matcher uses patterns or include/exclude.''' + return False + + def always(self): + '''Matcher will match everything and .files() will be empty + - optimization might be possible and necessary.''' + return False + + def isexact(self): + return False + + def prefix(self): + return not self.always() and not self.isexact() and not self.anypats() + +class matcher(basematcher): def __init__(self, root, cwd, normalize, patterns, include=None, exclude=None, default='glob', exact=False, auditor=None, ctx=None, listsubrepos=False, warn=None, badfn=None): + super(matcher, self).__init__(root, cwd, badfn) if include is None: include = [] if exclude is None: exclude = [] - self._root = root - self._cwd = cwd - self._files = [] # exact files and roots of patterns self._anypats = bool(include or exclude) self._always = False self._pathrestricted = bool(include or exclude or patterns) @@ -228,9 +316,6 @@ # dirs are directories which are non-recursively included. self._includedirs = set() - if badfn is not None: - self.bad = badfn - matchfns = [] if include: kindpats = normalize(include, 'glob', root, cwd, auditor, warn) @@ -281,70 +366,14 @@ self.matchfn = m - def __call__(self, fn): - return self.matchfn(fn) - def __iter__(self): - for f in self._files: - yield f - - # Callbacks related to how the matcher is used by dirstate.walk. - # Subscribers to these events must monkeypatch the matcher object. - def bad(self, f, msg): - '''Callback from dirstate.walk for each explicit file that can't be - found/accessed, with an error message.''' - pass - - # If an explicitdir is set, it will be called when an explicitly listed - # directory is visited. - explicitdir = None - - # If an traversedir is set, it will be called when a directory discovered - # by recursive traversal is visited. - traversedir = None - - def abs(self, f): - '''Convert a repo path back to path that is relative to the root of the - matcher.''' - return f - - def rel(self, f): - '''Convert repo path back to path that is relative to cwd of matcher.''' - return util.pathto(self._root, self._cwd, f) - def uipath(self, f): - '''Convert repo path to a display path. If patterns or -I/-X were used - to create this matcher, the display path will be relative to cwd. - Otherwise it is relative to the root of the repo.''' return (self._pathrestricted and self.rel(f)) or self.abs(f) - def files(self): - '''Explicitly listed files or patterns or roots: - if no patterns or .always(): empty list, - if exact: list exact files, - if not .anypats(): list all files and dirs, - else: optimal roots''' - return self._files - - @propertycache - def _fileset(self): - return set(self._files) - @propertycache def _dirs(self): return set(util.dirs(self._fileset)) | {'.'} def visitdir(self, dir): - '''Decides whether a directory should be visited based on whether it - has potential matches in it or one of its subdirectories. This is - based on the match's primary, included, and excluded patterns. - - Returns the string 'all' if the given directory and all subdirectories - should be visited. Otherwise returns True or False indicating whether - the given directory should be visited. - - This function's behavior is undefined if it has returned False for - one of the dir's parent directories. - ''' if self.prefix() and dir in self._fileset: return 'all' if dir in self._excluderoots: @@ -363,25 +392,15 @@ any(parentdir in self._fileset for parentdir in util.finddirs(dir))) - def exact(self, f): - '''Returns True if f is in .files().''' - return f in self._fileset - def anypats(self): - '''Matcher uses patterns or include/exclude.''' return self._anypats def always(self): - '''Matcher will match everything and .files() will be empty - - optimization might be possible and necessary.''' return self._always def isexact(self): return self.matchfn == self.exact - def prefix(self): - return not self.always() and not self.isexact() and not self.anypats() - def __repr__(self): return ('<matcher files=%r, patterns=%r, includes=%r, excludes=%r>' % (self._files, self.patternspat, self.includepat,