# HG changeset patch # User Yuya Nishihara # Date 1544620797 -32400 # Node ID d3e688b9ef2e4b33e94be1359a5c57befdff5833 # Parent 18513d6ef7d4a38341d5c26ab3f17cd095ed5a0c templatefuncs: add regexp search() function that extracts substring This can be used to extract an issue number from a commit message, for example: {search(r'\(issue([0-9]*)\)', desc) % '{1}'} diff -r 18513d6ef7d4 -r d3e688b9ef2e mercurial/templatefuncs.py --- a/mercurial/templatefuncs.py Fri Nov 30 00:44:04 2018 +0100 +++ b/mercurial/templatefuncs.py Wed Dec 12 22:19:57 2018 +0900 @@ -20,6 +20,7 @@ error, minirst, obsutil, + pycompat, registrar, revset as revsetmod, revsetlang, @@ -581,6 +582,40 @@ return minirst.format(text, style=style, keep=['verbose']) +@templatefunc('search(pattern, text)') +def search(context, mapping, args): + """Look for the first text matching the regular expression pattern. + Groups are accessible as ``{1}``, ``{2}``, ... in %-mapped template.""" + if len(args) != 2: + # i18n: "search" is a keyword + raise error.ParseError(_(b'search expects two arguments')) + + pat = evalstring(context, mapping, args[0]) + src = evalstring(context, mapping, args[1]) + try: + patre = re.compile(pat) + except re.error: + # i18n: "search" is a keyword + raise error.ParseError(_(b'search got an invalid pattern: %s') % pat) + # named groups shouldn't shadow *reserved* resource keywords + badgroups = (context.knownresourcekeys() + & set(pycompat.byteskwargs(patre.groupindex))) + if badgroups: + raise error.ParseError( + # i18n: "search" is a keyword + _(b'invalid group %(group)s in search pattern: %(pat)s') + % {b'group': b', '.join("'%s'" % g for g in sorted(badgroups)), + b'pat': pat}) + + match = patre.search(src) + if not match: + return + + lm = {b'0': match.group(0)} + lm.update((b'%d' % i, v) for i, v in enumerate(match.groups(), 1)) + lm.update(pycompat.byteskwargs(match.groupdict())) + return templateutil.mappingdict(lm, tmpl=b'{0}') + @templatefunc('separate(sep, args...)', argspec='sep *args') def separate(context, mapping, args): """Add a separator between non-empty arguments.""" diff -r 18513d6ef7d4 -r d3e688b9ef2e tests/test-template-functions.t --- a/tests/test-template-functions.t Fri Nov 30 00:44:04 2018 +0100 +++ b/tests/test-template-functions.t Wed Dec 12 22:19:57 2018 +0900 @@ -603,6 +603,53 @@ $ hg debugtemplate '{ifeq(0, 0, "", count(0))}' $ hg debugtemplate '{ifeq(0, 1, count(0), "")}' +Test search() function: + + $ hg log -R a -r2 -T '{desc}\n' + no person + + $ hg log -R a -r2 -T '{search(r"p.*", desc)}\n' + person + + as bool + + $ hg log -R a -r2 -T '{if(search(r"p.*", desc), "", "not ")}found\n' + found + $ hg log -R a -r2 -T '{if(search(r"q", desc), "", "not ")}found\n' + not found + + match as json + + $ hg log -R a -r2 -T '{search(r"(no) p.*", desc)|json}\n' + {"0": "no person", "1": "no"} + $ hg log -R a -r2 -T '{search(r"q", desc)|json}\n' + null + + group reference + + $ hg log -R a -r2 -T '{search(r"(no) (p.*)", desc) % "{1|upper} {2|hex}"}\n' + NO 706572736f6e + $ hg log -R a -r2 -T '{search(r"(?P[a-z]*)", desc) % "{foo}"}\n' + no + $ hg log -R a -r2 -T '{search(r"(?P[a-z]*)", desc).foo}\n' + no + + group reference with no match + (TODO: we'll probably want to map it to an empty value) + + $ hg log -R a -r2 -T '{search(r"q", desc) % "match: {0}"}\n' + hg: parse error: None is not iterable of mappings + [255] + + bad group names + + $ hg log -R a -r2 -T '{search(r"(?P<0>.)", desc) % "{0}"}\n' + hg: parse error: search got an invalid pattern: (?P<0>.) + [255] + $ hg log -R a -r2 -T '{search(r"(?P.)", desc) % "{repo}"}\n' + hg: parse error: invalid group 'repo' in search pattern: (?P.) + [255] + Test the sub function of templating for expansion: $ hg log -R latesttag -r 10 --template '{sub("[0-9]", "x", "{rev}")}\n'