comparison mercurial/revset.py @ 26481:7d132557e44a

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.
author Matt Harbison <matt_harbison@yahoo.com>
date Sat, 22 Aug 2015 22:52:18 -0400
parents 428a8747f4ee
children 832feae7c986
comparison
equal deleted inserted replaced
26480:6ae14d1ca3aa 26481:7d132557e44a
688 args = getargs(x, 0, 1, _('bookmark takes one or no arguments')) 688 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
689 if args: 689 if args:
690 bm = getstring(args[0], 690 bm = getstring(args[0],
691 # i18n: "bookmark" is a keyword 691 # i18n: "bookmark" is a keyword
692 _('the argument to bookmark must be a string')) 692 _('the argument to bookmark must be a string'))
693 kind, pattern, matcher = _stringmatcher(bm) 693 kind, pattern, matcher = util.stringmatcher(bm)
694 bms = set() 694 bms = set()
695 if kind == 'literal': 695 if kind == 'literal':
696 bmrev = repo._bookmarks.get(pattern, None) 696 bmrev = repo._bookmarks.get(pattern, None)
697 if not bmrev: 697 if not bmrev:
698 raise error.RepoLookupError(_("bookmark '%s' does not exist") 698 raise error.RepoLookupError(_("bookmark '%s' does not exist")
729 b = getstring(x, '') 729 b = getstring(x, '')
730 except error.ParseError: 730 except error.ParseError:
731 # not a string, but another revspec, e.g. tip() 731 # not a string, but another revspec, e.g. tip()
732 pass 732 pass
733 else: 733 else:
734 kind, pattern, matcher = _stringmatcher(b) 734 kind, pattern, matcher = util.stringmatcher(b)
735 if kind == 'literal': 735 if kind == 'literal':
736 # note: falls through to the revspec case if no branch with 736 # note: falls through to the revspec case if no branch with
737 # this name exists 737 # this name exists
738 if pattern in repo.branchmap(): 738 if pattern in repo.branchmap():
739 return subset.filter(lambda r: matcher(getbi(r)[0])) 739 return subset.filter(lambda r: matcher(getbi(r)[0]))
1017 1017
1018 if 'value' in args: 1018 if 'value' in args:
1019 # i18n: "extra" is a keyword 1019 # i18n: "extra" is a keyword
1020 value = getstring(args['value'], _('second argument to extra must be ' 1020 value = getstring(args['value'], _('second argument to extra must be '
1021 'a string')) 1021 'a string'))
1022 kind, value, matcher = _stringmatcher(value) 1022 kind, value, matcher = util.stringmatcher(value)
1023 1023
1024 def _matchvalue(r): 1024 def _matchvalue(r):
1025 extra = repo[r].extra() 1025 extra = repo[r].extra()
1026 return label in extra and (value is None or matcher(extra[label])) 1026 return label in extra and (value is None or matcher(extra[label]))
1027 1027
1464 args = getargs(x, 1, 1, _('named requires a namespace argument')) 1464 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1465 1465
1466 ns = getstring(args[0], 1466 ns = getstring(args[0],
1467 # i18n: "named" is a keyword 1467 # i18n: "named" is a keyword
1468 _('the argument to named must be a string')) 1468 _('the argument to named must be a string'))
1469 kind, pattern, matcher = _stringmatcher(ns) 1469 kind, pattern, matcher = util.stringmatcher(ns)
1470 namespaces = set() 1470 namespaces = set()
1471 if kind == 'literal': 1471 if kind == 'literal':
1472 if pattern not in repo.names: 1472 if pattern not in repo.names:
1473 raise error.RepoLookupError(_("namespace '%s' does not exist") 1473 raise error.RepoLookupError(_("namespace '%s' does not exist")
1474 % ns) 1474 % ns)
2032 pat = getstring(args[0], _("subrepo requires a pattern")) 2032 pat = getstring(args[0], _("subrepo requires a pattern"))
2033 2033
2034 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate']) 2034 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate'])
2035 2035
2036 def submatches(names): 2036 def submatches(names):
2037 k, p, m = _stringmatcher(pat) 2037 k, p, m = util.stringmatcher(pat)
2038 for name in names: 2038 for name in names:
2039 if m(name): 2039 if m(name):
2040 yield name 2040 yield name
2041 2041
2042 def matches(x): 2042 def matches(x):
2062 2062
2063 return False 2063 return False
2064 2064
2065 return subset.filter(matches) 2065 return subset.filter(matches)
2066 2066
2067 def _stringmatcher(pattern):
2068 """
2069 accepts a string, possibly starting with 're:' or 'literal:' prefix.
2070 returns the matcher name, pattern, and matcher function.
2071 missing or unknown prefixes are treated as literal matches.
2072
2073 helper for tests:
2074 >>> def test(pattern, *tests):
2075 ... kind, pattern, matcher = _stringmatcher(pattern)
2076 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
2077
2078 exact matching (no prefix):
2079 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
2080 ('literal', 'abcdefg', [False, False, True])
2081
2082 regex matching ('re:' prefix)
2083 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
2084 ('re', 'a.+b', [False, False, True])
2085
2086 force exact matches ('literal:' prefix)
2087 >>> test('literal:re:foobar', 'foobar', 're:foobar')
2088 ('literal', 're:foobar', [False, True])
2089
2090 unknown prefixes are ignored and treated as literals
2091 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
2092 ('literal', 'foo:bar', [False, False, True])
2093 """
2094 if pattern.startswith('re:'):
2095 pattern = pattern[3:]
2096 try:
2097 regex = re.compile(pattern)
2098 except re.error as e:
2099 raise error.ParseError(_('invalid regular expression: %s')
2100 % e)
2101 return 're', pattern, regex.search
2102 elif pattern.startswith('literal:'):
2103 pattern = pattern[8:]
2104 return 'literal', pattern, pattern.__eq__
2105
2106 def _substringmatcher(pattern): 2067 def _substringmatcher(pattern):
2107 kind, pattern, matcher = _stringmatcher(pattern) 2068 kind, pattern, matcher = util.stringmatcher(pattern)
2108 if kind == 'literal': 2069 if kind == 'literal':
2109 matcher = lambda s: pattern in s 2070 matcher = lambda s: pattern in s
2110 return kind, pattern, matcher 2071 return kind, pattern, matcher
2111 2072
2112 def tag(repo, subset, x): 2073 def tag(repo, subset, x):
2122 cl = repo.changelog 2083 cl = repo.changelog
2123 if args: 2084 if args:
2124 pattern = getstring(args[0], 2085 pattern = getstring(args[0],
2125 # i18n: "tag" is a keyword 2086 # i18n: "tag" is a keyword
2126 _('the argument to tag must be a string')) 2087 _('the argument to tag must be a string'))
2127 kind, pattern, matcher = _stringmatcher(pattern) 2088 kind, pattern, matcher = util.stringmatcher(pattern)
2128 if kind == 'literal': 2089 if kind == 'literal':
2129 # avoid resolving all tags 2090 # avoid resolving all tags
2130 tn = repo._tagscache.tags.get(pattern, None) 2091 tn = repo._tagscache.tags.get(pattern, None)
2131 if tn is None: 2092 if tn is None:
2132 raise error.RepoLookupError(_("tag '%s' does not exist") 2093 raise error.RepoLookupError(_("tag '%s' does not exist")