# HG changeset patch # User Matt Harbison # Date 1440298338 14400 # Node ID 7d132557e44ac8ecbda418292e8a25475987671b # Parent 6ae14d1ca3aa7929338f57c4f308aaca88b4d108 util: extract stringmatcher() from revset This is used to match against tags, bookmarks, etc in revsets. It will be used in a future patch to do the same tag matching in templater. diff -r 6ae14d1ca3aa -r 7d132557e44a mercurial/revset.py --- a/mercurial/revset.py Mon Oct 05 17:36:32 2015 -0700 +++ b/mercurial/revset.py Sat Aug 22 22:52:18 2015 -0400 @@ -690,7 +690,7 @@ bm = getstring(args[0], # i18n: "bookmark" is a keyword _('the argument to bookmark must be a string')) - kind, pattern, matcher = _stringmatcher(bm) + kind, pattern, matcher = util.stringmatcher(bm) bms = set() if kind == 'literal': bmrev = repo._bookmarks.get(pattern, None) @@ -731,7 +731,7 @@ # not a string, but another revspec, e.g. tip() pass else: - kind, pattern, matcher = _stringmatcher(b) + kind, pattern, matcher = util.stringmatcher(b) if kind == 'literal': # note: falls through to the revspec case if no branch with # this name exists @@ -1019,7 +1019,7 @@ # i18n: "extra" is a keyword value = getstring(args['value'], _('second argument to extra must be ' 'a string')) - kind, value, matcher = _stringmatcher(value) + kind, value, matcher = util.stringmatcher(value) def _matchvalue(r): extra = repo[r].extra() @@ -1466,7 +1466,7 @@ ns = getstring(args[0], # i18n: "named" is a keyword _('the argument to named must be a string')) - kind, pattern, matcher = _stringmatcher(ns) + kind, pattern, matcher = util.stringmatcher(ns) namespaces = set() if kind == 'literal': if pattern not in repo.names: @@ -2034,7 +2034,7 @@ m = matchmod.exact(repo.root, repo.root, ['.hgsubstate']) def submatches(names): - k, p, m = _stringmatcher(pat) + k, p, m = util.stringmatcher(pat) for name in names: if m(name): yield name @@ -2064,47 +2064,8 @@ return subset.filter(matches) -def _stringmatcher(pattern): - """ - accepts a string, possibly starting with 're:' or 'literal:' prefix. - returns the matcher name, pattern, and matcher function. - missing or unknown prefixes are treated as literal matches. - - helper for tests: - >>> def test(pattern, *tests): - ... kind, pattern, matcher = _stringmatcher(pattern) - ... return (kind, pattern, [bool(matcher(t)) for t in tests]) - - exact matching (no prefix): - >>> test('abcdefg', 'abc', 'def', 'abcdefg') - ('literal', 'abcdefg', [False, False, True]) - - regex matching ('re:' prefix) - >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar') - ('re', 'a.+b', [False, False, True]) - - force exact matches ('literal:' prefix) - >>> test('literal:re:foobar', 'foobar', 're:foobar') - ('literal', 're:foobar', [False, True]) - - unknown prefixes are ignored and treated as literals - >>> test('foo:bar', 'foo', 'bar', 'foo:bar') - ('literal', 'foo:bar', [False, False, True]) - """ - if pattern.startswith('re:'): - pattern = pattern[3:] - try: - regex = re.compile(pattern) - except re.error as e: - raise error.ParseError(_('invalid regular expression: %s') - % e) - return 're', pattern, regex.search - elif pattern.startswith('literal:'): - pattern = pattern[8:] - return 'literal', pattern, pattern.__eq__ - def _substringmatcher(pattern): - kind, pattern, matcher = _stringmatcher(pattern) + kind, pattern, matcher = util.stringmatcher(pattern) if kind == 'literal': matcher = lambda s: pattern in s return kind, pattern, matcher @@ -2124,7 +2085,7 @@ pattern = getstring(args[0], # i18n: "tag" is a keyword _('the argument to tag must be a string')) - kind, pattern, matcher = _stringmatcher(pattern) + kind, pattern, matcher = util.stringmatcher(pattern) if kind == 'literal': # avoid resolving all tags tn = repo._tagscache.tags.get(pattern, None) diff -r 6ae14d1ca3aa -r 7d132557e44a mercurial/util.py --- a/mercurial/util.py Mon Oct 05 17:36:32 2015 -0700 +++ b/mercurial/util.py Sat Aug 22 22:52:18 2015 -0400 @@ -1605,6 +1605,45 @@ start, stop = lower(date), upper(date) return lambda x: x >= start and x <= stop +def stringmatcher(pattern): + """ + accepts a string, possibly starting with 're:' or 'literal:' prefix. + returns the matcher name, pattern, and matcher function. + missing or unknown prefixes are treated as literal matches. + + helper for tests: + >>> def test(pattern, *tests): + ... kind, pattern, matcher = stringmatcher(pattern) + ... return (kind, pattern, [bool(matcher(t)) for t in tests]) + + exact matching (no prefix): + >>> test('abcdefg', 'abc', 'def', 'abcdefg') + ('literal', 'abcdefg', [False, False, True]) + + regex matching ('re:' prefix) + >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar') + ('re', 'a.+b', [False, False, True]) + + force exact matches ('literal:' prefix) + >>> test('literal:re:foobar', 'foobar', 're:foobar') + ('literal', 're:foobar', [False, True]) + + unknown prefixes are ignored and treated as literals + >>> test('foo:bar', 'foo', 'bar', 'foo:bar') + ('literal', 'foo:bar', [False, False, True]) + """ + if pattern.startswith('re:'): + pattern = pattern[3:] + try: + regex = remod.compile(pattern) + except remod.error as e: + raise error.ParseError(_('invalid regular expression: %s') + % e) + return 're', pattern, regex.search + elif pattern.startswith('literal:'): + pattern = pattern[8:] + return 'literal', pattern, pattern.__eq__ + def shortuser(user): """Return a short representation of a user name or email address.""" f = user.find('@')