templatefuncs: specialize "no match" value of search() to allow % operation
authorYuya Nishihara <yuya@tcha.org>
Wed, 12 Dec 2018 22:45:02 +0900
changeset 40935 4591c9791a82
parent 40934 d3e688b9ef2e
child 40936 e06719b7544d
templatefuncs: specialize "no match" value of search() to allow % operation If Python had Maybe or Option, the type of the search() result would be Option<Mapping>, which can be considered as a 0/1 container of a Mapping. So it makes sense that {search(r'no match pattern', x) % "whatever"} is mapped to an empty string.
mercurial/templatefuncs.py
mercurial/templater.py
mercurial/templateutil.py
tests/test-template-functions.t
--- a/mercurial/templatefuncs.py	Wed Dec 12 22:19:57 2018 +0900
+++ b/mercurial/templatefuncs.py	Wed Dec 12 22:45:02 2018 +0900
@@ -609,7 +609,7 @@
 
     match = patre.search(src)
     if not match:
-        return
+        return templateutil.mappingnone()
 
     lm = {b'0': match.group(0)}
     lm.update((b'%d' % i, v) for i, v in enumerate(match.groups(), 1))
--- a/mercurial/templater.py	Wed Dec 12 22:19:57 2018 +0900
+++ b/mercurial/templater.py	Wed Dec 12 22:45:02 2018 +0900
@@ -53,6 +53,10 @@
     represents a single mapping (i.e. a dict), which may have default output
     format.
 
+mappingnone
+    represents None of Optional[mappable], which will be mapped to an empty
+    string by % operation.
+
 mappedgenerator
     a lazily-evaluated list of byte strings, which is e.g. a result of %
     operation.
--- a/mercurial/templateutil.py	Wed Dec 12 22:19:57 2018 +0900
+++ b/mercurial/templateutil.py	Wed Dec 12 22:45:02 2018 +0900
@@ -495,6 +495,19 @@
     def tovalue(self, context, mapping):
         return super(mappingdict, self).tovalue(context, mapping)[0]
 
+class mappingnone(wrappedvalue):
+    """Wrapper for None, but supports map operation
+
+    This represents None of Optional[mappable]. It's similar to
+    mapplinglist([]), but the underlying value is not [], but None.
+    """
+
+    def __init__(self):
+        super(mappingnone, self).__init__(None)
+
+    def itermaps(self, context):
+        return iter([])
+
 class mappedgenerator(wrapped):
     """Wrapper for generator of strings which acts as a list
 
--- a/tests/test-template-functions.t	Wed Dec 12 22:19:57 2018 +0900
+++ b/tests/test-template-functions.t	Wed Dec 12 22:45:02 2018 +0900
@@ -635,11 +635,9 @@
   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