--- 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')