mirror of
https://github.com/facebook/sapling.git
synced 2024-10-06 23:07:18 +03:00
match: use dynmatcher if possible
Summary: Use dynmatcher if possible, this is for migrating more matcher build logic to Rust. Reviewed By: quark-zju Differential Revision: D40767733 fbshipit-source-id: dd78490d2cc85715c36490f2887662fb7203b5b3
This commit is contained in:
parent
d4b4b66732
commit
d0f6ca3ab4
@ -313,6 +313,7 @@ coreconfigitem("experimental", "sparse-read.min-gap-size", default="256K")
|
||||
coreconfigitem("experimental", "treemanifest", default=False)
|
||||
coreconfigitem("experimental", "treematcher", default=True)
|
||||
coreconfigitem("experimental", "regexmatcher", default=True)
|
||||
coreconfigitem("experimental", "dynmatcher", default=False)
|
||||
coreconfigitem("experimental", "uncommitondirtywdir", default=True)
|
||||
coreconfigitem("experimental", "xdiff", default=True)
|
||||
coreconfigitem("extensions", ".*", default=None, generic=True)
|
||||
|
@ -202,6 +202,26 @@ def match(
|
||||
includekindpats = include and normalize(include, "glob", root, cwd, auditor, warn)
|
||||
excludekindpats = exclude and normalize(exclude, "glob", root, cwd, auditor, warn)
|
||||
|
||||
# Try to use Rust dyn matcher if possible. Currently, Rust dyn matcher is
|
||||
# missing below features:
|
||||
# * exact matcher
|
||||
# * emptyalways parameter
|
||||
# * explicit files in Matcher trait
|
||||
# * pattern kinds other than 'glob' and 're'
|
||||
if _usedynmatcher and not m:
|
||||
matcher = _builddynmatcher(
|
||||
root=root,
|
||||
cwd=cwd,
|
||||
patternskindpats=patternskindpats or [],
|
||||
includekindpats=includekindpats or [],
|
||||
excludekindpats=excludekindpats or [],
|
||||
default=default,
|
||||
badfn=badfn,
|
||||
)
|
||||
if matcher:
|
||||
return matcher
|
||||
# else fallback to original logic
|
||||
|
||||
if not m:
|
||||
if _kindpatsalwaysmatch(patternskindpats):
|
||||
m = alwaysmatcher(root, cwd, badfn, relativeuipath=True)
|
||||
@ -230,6 +250,64 @@ def match(
|
||||
return m
|
||||
|
||||
|
||||
def _builddynmatcher(
|
||||
root,
|
||||
cwd,
|
||||
patternskindpats: List[str],
|
||||
includekindpats: List[str],
|
||||
excludekindpats: List[str],
|
||||
default: str = "glob",
|
||||
badfn=None,
|
||||
) -> Optional["dynmatcher"]:
|
||||
def generatenormalizedpatterns(
|
||||
kindpats, default, recursive, ispatterns
|
||||
) -> Optional[List[str]]:
|
||||
if ispatterns:
|
||||
if not kindpats:
|
||||
# empty patterns means nevermatcher here
|
||||
return None
|
||||
elif _kindpatsalwaysmatch(kindpats) or any(_explicitfiles(kindpats)):
|
||||
# * Rust AlwaysMatcher doesn't support relative ui path now
|
||||
# * Rust Matchers doesn't support explicit files
|
||||
return None
|
||||
|
||||
res = _kindpatstoglobsregexs(kindpats, recursive=recursive)
|
||||
if not res:
|
||||
# Rust build matcher only supports 'glob' and 're' now
|
||||
return None
|
||||
globs, regexs = res
|
||||
return [f"glob:{x}" for x in globs] + [f"re:{x}" for x in regexs]
|
||||
|
||||
normalizedpatterns = generatenormalizedpatterns(
|
||||
patternskindpats, default, False, True
|
||||
)
|
||||
if normalizedpatterns is None:
|
||||
return None
|
||||
normalizedinclude = generatenormalizedpatterns(includekindpats, "glob", True, False)
|
||||
if normalizedinclude is None:
|
||||
return None
|
||||
normalizedexclude = generatenormalizedpatterns(excludekindpats, "glob", True, False)
|
||||
if normalizedexclude is None:
|
||||
return None
|
||||
|
||||
try:
|
||||
m = dynmatcher(
|
||||
root,
|
||||
cwd,
|
||||
normalizedpatterns,
|
||||
normalizedinclude,
|
||||
normalizedexclude,
|
||||
casesensitive=True,
|
||||
badfn=badfn,
|
||||
)
|
||||
return m
|
||||
except (error.RustError, ValueError):
|
||||
# possible exceptions:
|
||||
# * TreeMatcher: Regex("Compiled regex exceeds size limit of 10485760 bytes.")
|
||||
# * RegexMatcher: doesn't support '\b' and '\B'
|
||||
return None
|
||||
|
||||
|
||||
def exact(root, cwd, files, badfn=None) -> "exactmatcher":
|
||||
return exactmatcher(root, cwd, files, badfn=badfn)
|
||||
|
||||
@ -909,6 +987,50 @@ class regexmatcher(basematcher):
|
||||
return f"<regexmatcher pattern={self._pattern!r}>"
|
||||
|
||||
|
||||
class dynmatcher(basematcher):
|
||||
"""Rust dyn matcher created by the build matcher API."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
root,
|
||||
cwd,
|
||||
patterns: List[str],
|
||||
include: List[str],
|
||||
exclude: List[str],
|
||||
casesensitive: bool = True,
|
||||
badfn=None,
|
||||
):
|
||||
super(dynmatcher, self).__init__(root, cwd, badfn)
|
||||
self._matcher = pathmatcher.dynmatcher(
|
||||
patterns, include, exclude, casesensitive
|
||||
)
|
||||
self.patterns = patterns
|
||||
self.include = include
|
||||
self.exclude = exclude
|
||||
self.casesensitive = casesensitive
|
||||
|
||||
def matchfn(self, f):
|
||||
return self._matcher.matches_file(f)
|
||||
|
||||
def visitdir(self, dir):
|
||||
matched = self._matcher.matches_directory(dir)
|
||||
if matched is None:
|
||||
return True
|
||||
elif matched is True:
|
||||
return "all"
|
||||
else:
|
||||
assert matched is False, f"expected False, but got {matched}"
|
||||
return False
|
||||
|
||||
def __repr__(self):
|
||||
return "<dynmatcher patterns=%r include=%r exclude=%r casesensitive=%r>" % (
|
||||
self.patterns,
|
||||
self.include,
|
||||
self.exclude,
|
||||
self.casesensitive,
|
||||
)
|
||||
|
||||
|
||||
def normalizerootdir(dir: str, funcname) -> str:
|
||||
if dir == ".":
|
||||
util.nouideprecwarn(
|
||||
@ -1921,6 +2043,7 @@ def readpatternfile(filepath, warn, sourceinfo: bool = False):
|
||||
|
||||
_usetreematcher = True
|
||||
_useregexmatcher = True
|
||||
_usedynmatcher = True
|
||||
|
||||
|
||||
def init(ui) -> None:
|
||||
@ -1928,3 +2051,5 @@ def init(ui) -> None:
|
||||
_usetreematcher = ui.configbool("experimental", "treematcher")
|
||||
global _useregexmatcher
|
||||
_useregexmatcher = ui.configbool("experimental", "regexmatcher")
|
||||
global _usedynmatcher
|
||||
_usedynmatcher = ui.configbool("experimental", "dynmatcher")
|
||||
|
Loading…
Reference in New Issue
Block a user