mercurial/templatekw.py
changeset 36921 32f9b7e3f056
parent 36634 cafd0586876b
child 37019 c97b936d8bb5
--- a/mercurial/templatekw.py	Thu Mar 08 23:10:46 2018 +0900
+++ b/mercurial/templatekw.py	Thu Mar 08 23:15:09 2018 +0900
@@ -23,156 +23,24 @@
     pycompat,
     registrar,
     scmutil,
+    templateutil,
     util,
 )
 
-class _hybrid(object):
-    """Wrapper for list or dict to support legacy template
-
-    This class allows us to handle both:
-    - "{files}" (legacy command-line-specific list hack) and
-    - "{files % '{file}\n'}" (hgweb-style with inlining and function support)
-    and to access raw values:
-    - "{ifcontains(file, files, ...)}", "{ifcontains(key, extras, ...)}"
-    - "{get(extras, key)}"
-    - "{files|json}"
-    """
-
-    def __init__(self, gen, values, makemap, joinfmt, keytype=None):
-        if gen is not None:
-            self.gen = gen  # generator or function returning generator
-        self._values = values
-        self._makemap = makemap
-        self.joinfmt = joinfmt
-        self.keytype = keytype  # hint for 'x in y' where type(x) is unresolved
-    def gen(self):
-        """Default generator to stringify this as {join(self, ' ')}"""
-        for i, x in enumerate(self._values):
-            if i > 0:
-                yield ' '
-            yield self.joinfmt(x)
-    def itermaps(self):
-        makemap = self._makemap
-        for x in self._values:
-            yield makemap(x)
-    def __contains__(self, x):
-        return x in self._values
-    def __getitem__(self, key):
-        return self._values[key]
-    def __len__(self):
-        return len(self._values)
-    def __iter__(self):
-        return iter(self._values)
-    def __getattr__(self, name):
-        if name not in (r'get', r'items', r'iteritems', r'iterkeys',
-                        r'itervalues', r'keys', r'values'):
-            raise AttributeError(name)
-        return getattr(self._values, name)
-
-class _mappable(object):
-    """Wrapper for non-list/dict object to support map operation
-
-    This class allows us to handle both:
-    - "{manifest}"
-    - "{manifest % '{rev}:{node}'}"
-    - "{manifest.rev}"
-
-    Unlike a _hybrid, this does not simulate the behavior of the underling
-    value. Use unwrapvalue() or unwraphybrid() to obtain the inner object.
-    """
-
-    def __init__(self, gen, key, value, makemap):
-        if gen is not None:
-            self.gen = gen  # generator or function returning generator
-        self._key = key
-        self._value = value  # may be generator of strings
-        self._makemap = makemap
-
-    def gen(self):
-        yield pycompat.bytestr(self._value)
-
-    def tomap(self):
-        return self._makemap(self._key)
-
-    def itermaps(self):
-        yield self.tomap()
-
-def hybriddict(data, key='key', value='value', fmt=None, gen=None):
-    """Wrap data to support both dict-like and string-like operations"""
-    prefmt = pycompat.identity
-    if fmt is None:
-        fmt = '%s=%s'
-        prefmt = pycompat.bytestr
-    return _hybrid(gen, data, lambda k: {key: k, value: data[k]},
-                   lambda k: fmt % (prefmt(k), prefmt(data[k])))
-
-def hybridlist(data, name, fmt=None, gen=None):
-    """Wrap data to support both list-like and string-like operations"""
-    prefmt = pycompat.identity
-    if fmt is None:
-        fmt = '%s'
-        prefmt = pycompat.bytestr
-    return _hybrid(gen, data, lambda x: {name: x}, lambda x: fmt % prefmt(x))
-
-def unwraphybrid(thing):
-    """Return an object which can be stringified possibly by using a legacy
-    template"""
-    gen = getattr(thing, 'gen', None)
-    if gen is None:
-        return thing
-    if callable(gen):
-        return gen()
-    return gen
-
-def unwrapvalue(thing):
-    """Move the inner value object out of the wrapper"""
-    if not util.safehasattr(thing, '_value'):
-        return thing
-    return thing._value
-
-def wraphybridvalue(container, key, value):
-    """Wrap an element of hybrid container to be mappable
-
-    The key is passed to the makemap function of the given container, which
-    should be an item generated by iter(container).
-    """
-    makemap = getattr(container, '_makemap', None)
-    if makemap is None:
-        return value
-    if util.safehasattr(value, '_makemap'):
-        # a nested hybrid list/dict, which has its own way of map operation
-        return value
-    return _mappable(None, key, value, makemap)
-
-def compatdict(context, mapping, name, data, key='key', value='value',
-               fmt=None, plural=None, separator=' '):
-    """Wrap data like hybriddict(), but also supports old-style list template
-
-    This exists for backward compatibility with the old-style template. Use
-    hybriddict() for new template keywords.
-    """
-    c = [{key: k, value: v} for k, v in data.iteritems()]
-    t = context.resource(mapping, 'templ')
-    f = _showlist(name, c, t, mapping, plural, separator)
-    return hybriddict(data, key=key, value=value, fmt=fmt, gen=f)
-
-def compatlist(context, mapping, name, data, element=None, fmt=None,
-               plural=None, separator=' '):
-    """Wrap data like hybridlist(), but also supports old-style list template
-
-    This exists for backward compatibility with the old-style template. Use
-    hybridlist() for new template keywords.
-    """
-    t = context.resource(mapping, 'templ')
-    f = _showlist(name, data, t, mapping, plural, separator)
-    return hybridlist(data, name=element or name, fmt=fmt, gen=f)
+_hybrid = templateutil.hybrid
+_mappable = templateutil.mappable
+_showlist = templateutil._showlist
+hybriddict = templateutil.hybriddict
+hybridlist = templateutil.hybridlist
+compatdict = templateutil.compatdict
+compatlist = templateutil.compatlist
 
 def showdict(name, data, mapping, plural=None, key='key', value='value',
              fmt=None, separator=' '):
     ui = mapping.get('ui')
     if ui:
-        ui.deprecwarn("templatekw.showdict() is deprecated, use compatdict()",
-                      '4.6')
+        ui.deprecwarn("templatekw.showdict() is deprecated, use "
+                      "templateutil.compatdict()", '4.6')
     c = [{key: k, value: v} for k, v in data.iteritems()]
     f = _showlist(name, c, mapping['templ'], mapping, plural, separator)
     return hybriddict(data, key=key, value=value, fmt=fmt, gen=f)
@@ -180,82 +48,13 @@
 def showlist(name, values, mapping, plural=None, element=None, separator=' '):
     ui = mapping.get('ui')
     if ui:
-        ui.deprecwarn("templatekw.showlist() is deprecated, use compatlist()",
-                      '4.6')
+        ui.deprecwarn("templatekw.showlist() is deprecated, use "
+                      "templateutil.compatlist()", '4.6')
     if not element:
         element = name
     f = _showlist(name, values, mapping['templ'], mapping, plural, separator)
     return hybridlist(values, name=element, gen=f)
 
-def _showlist(name, values, templ, mapping, plural=None, separator=' '):
-    '''expand set of values.
-    name is name of key in template map.
-    values is list of strings or dicts.
-    plural is plural of name, if not simply name + 's'.
-    separator is used to join values as a string
-
-    expansion works like this, given name 'foo'.
-
-    if values is empty, expand 'no_foos'.
-
-    if 'foo' not in template map, return values as a string,
-    joined by 'separator'.
-
-    expand 'start_foos'.
-
-    for each value, expand 'foo'. if 'last_foo' in template
-    map, expand it instead of 'foo' for last key.
-
-    expand 'end_foos'.
-    '''
-    strmapping = pycompat.strkwargs(mapping)
-    if not plural:
-        plural = name + 's'
-    if not values:
-        noname = 'no_' + plural
-        if noname in templ:
-            yield templ(noname, **strmapping)
-        return
-    if name not in templ:
-        if isinstance(values[0], bytes):
-            yield separator.join(values)
-        else:
-            for v in values:
-                r = dict(v)
-                r.update(mapping)
-                yield r
-        return
-    startname = 'start_' + plural
-    if startname in templ:
-        yield templ(startname, **strmapping)
-    vmapping = mapping.copy()
-    def one(v, tag=name):
-        try:
-            vmapping.update(v)
-        # Python 2 raises ValueError if the type of v is wrong. Python
-        # 3 raises TypeError.
-        except (AttributeError, TypeError, ValueError):
-            try:
-                # Python 2 raises ValueError trying to destructure an e.g.
-                # bytes. Python 3 raises TypeError.
-                for a, b in v:
-                    vmapping[a] = b
-            except (TypeError, ValueError):
-                vmapping[name] = v
-        return templ(tag, **pycompat.strkwargs(vmapping))
-    lastname = 'last_' + name
-    if lastname in templ:
-        last = values.pop()
-    else:
-        last = None
-    for v in values:
-        yield one(v)
-    if last is not None:
-        yield one(last, tag=lastname)
-    endname = 'end_' + plural
-    if endname in templ:
-        yield templ(endname, **strmapping)
-
 def getlatesttags(context, mapping, pattern=None):
     '''return date, distance and name for the latest tag of rev'''
     repo = context.resource(mapping, 'repo')