templater: drop symbols which should be overridden by new 'ctx' (issue5612)
authorYuya Nishihara <yuya@tcha.org>
Thu, 15 Mar 2018 21:38:57 +0900
changeset 37075 46859b437697
parent 37074 2891079fb0c0
child 37076 66d478064d5f
templater: drop symbols which should be overridden by new 'ctx' (issue5612) This problem is caused by impedance mismatch between the templater and the formatter interface, which is that the template keywords are generally evaluated dynamically, but the formatter puts static values into a template mapping. This patch avoids the problem by removing conflicting values from a mapping dict when a 'ctx' is switched.
mercurial/formatter.py
mercurial/templater.py
tests/test-identify.t
--- a/mercurial/formatter.py	Thu Mar 15 21:22:52 2018 +0900
+++ b/mercurial/formatter.py	Thu Mar 15 21:38:57 2018 +0900
@@ -504,6 +504,10 @@
             'ui': ui,
         }
 
+    def availablekeys(self, context, mapping):
+        return {k for k, g in self._gettermap.iteritems()
+                if g(self, context, mapping, k) is not None}
+
     def knownkeys(self):
         return self._knownkeys
 
--- a/mercurial/templater.py	Thu Mar 15 21:22:52 2018 +0900
+++ b/mercurial/templater.py	Thu Mar 15 21:38:57 2018 +0900
@@ -563,6 +563,10 @@
     __metaclass__ = abc.ABCMeta
 
     @abc.abstractmethod
+    def availablekeys(self, context, mapping):
+        """Return a set of available resource keys based on the given mapping"""
+
+    @abc.abstractmethod
     def knownkeys(self):
         """Return a set of supported resource keys"""
 
@@ -571,6 +575,9 @@
         """Return a resource for the key if available; otherwise None"""
 
 class nullresourcemapper(resourcemapper):
+    def availablekeys(self, context, mapping):
+        return set()
+
     def knownkeys(self):
         return set()
 
@@ -616,10 +623,23 @@
     def overlaymap(self, origmapping, newmapping):
         """Create combined mapping from the original mapping and partial
         mapping to override the original"""
-        mapping = origmapping.copy()
+        # do not copy symbols which overrides the defaults depending on
+        # new resources, so the defaults will be re-evaluated (issue5612)
+        knownres = self._resources.knownkeys()
+        newres = self._resources.availablekeys(self, newmapping)
+        mapping = {k: v for k, v in origmapping.iteritems()
+                   if (k in knownres  # not a symbol per self.symbol()
+                       or newres.isdisjoint(self._defaultrequires(k)))}
         mapping.update(newmapping)
         return mapping
 
+    def _defaultrequires(self, key):
+        """Resource keys required by the specified default symbol function"""
+        v = self._defaults.get(key)
+        if v is None or not callable(v):
+            return ()
+        return getattr(v, '_requires', ())
+
     def symbol(self, mapping, key):
         """Resolve symbol to value or function; None if nothing found"""
         v = None
--- a/tests/test-identify.t	Thu Mar 15 21:22:52 2018 +0900
+++ b/tests/test-identify.t	Thu Mar 15 21:38:57 2018 +0900
@@ -63,6 +63,16 @@
   $ hg id -T '{parents % "{rev} {node|shortest} {desc}\n"}'
   0 cb9a a
 
+test nested template: '{tags}'/'{node}' constants shouldn't override the
+default keywords, but '{id}' persists because there's no default keyword
+for '{id}' (issue5612)
+
+  $ hg id -T '{tags}\n'
+  tip
+  $ hg id -T '{revset("null:.") % "{rev}:{node|short} {tags} {id}\n"}'
+  -1:000000000000  cb9a9f314b8b
+  0:cb9a9f314b8b tip cb9a9f314b8b
+
 with modifications
 
   $ echo b > a