formatter: add function to convert list to appropriate format (issue5217)
authorYuya Nishihara <yuya@tcha.org>
Sun, 10 Jul 2016 21:03:06 +0900
changeset 29676 c3a9cd78b151
parent 29675 12f04946053c
child 29677 69890b5dd32b
formatter: add function to convert list to appropriate format (issue5217) Before, it wasn't possible for formatter to handle array structure other than date tuple. We've discussed that at the last sprint, which ended we would probably want to allow only templatable data structure, i.e. a list of dicts: data(tags=[{'tag': a}, {'tag': b}, ...]) Unfortunately, it turned out not working well with template functions: "{ifcontains(a, tags, ...)}" ^^^^^^^^^^^^^^^^^^ "a in tags", where tags should be a plain list/set of tags So the formatter must at least know if the type [{}] was constructed from a plain list or was actually a list of dicts. This patch introduces new explicit interface to convert an array structure to an appropriate data type for the current formatter, which can be used as follows: fm.write('tags', _('tags: %s\n'), fm.formatlist(tags, name='tag')) No separate fm.data() call should be necessary.
mercurial/formatter.py
--- a/mercurial/formatter.py	Sun Jul 31 16:38:16 2016 +0900
+++ b/mercurial/formatter.py	Sun Jul 10 21:03:06 2016 +0900
@@ -18,6 +18,7 @@
 from . import (
     encoding,
     error,
+    templatekw,
     templater,
     util,
 )
@@ -45,6 +46,10 @@
         if self._item is not None:
             self._showitem()
         self._item = {}
+    @staticmethod
+    def formatlist(data, name, fmt='%s', sep=' '):
+        '''convert iterable to appropriate list format'''
+        return list(data)
     def data(self, **data):
         '''insert data into item that's not shown in default output'''
         self._item.update(data)
@@ -78,6 +83,10 @@
         return False
     def startitem(self):
         pass
+    @staticmethod
+    def formatlist(data, name, fmt='%s', sep=' '):
+        '''stringify iterable separated by sep'''
+        return sep.join(fmt % e for e in data)
     def data(self, **data):
         pass
     def write(self, fields, deftext, *fielddata, **opts):
@@ -112,7 +121,7 @@
         self._ui.write(pickle.dumps(self._data))
 
 def _jsonifyobj(v):
-    if isinstance(v, tuple):
+    if isinstance(v, (list, tuple)):
         return '[' + ', '.join(_jsonifyobj(e) for e in v) + ']'
     elif v is None:
         return 'null'
@@ -157,6 +166,16 @@
     def _showitem(self):
         g = self._t(self._topic, ui=self._ui, **self._item)
         self._ui.write(templater.stringify(g))
+    @staticmethod
+    def formatlist(data, name, fmt='%s', sep=' '):
+        '''build object that can be evaluated as either plain string or list'''
+        # name is mandatory argument for now, but it could be optional if
+        # we have default template keyword, e.g. {item}
+        data = list(data)
+        def f():
+            yield plainformatter.formatlist(data, name, fmt, sep)
+        return templatekw._hybrid(f(), data, lambda x: {name: x},
+                                  lambda d: fmt % d[name])
 
 def lookuptemplate(ui, topic, tmpl):
     # looks like a literal template?