comparison mercurial/templateutil.py @ 37399:0b64416224d9

templater: add class representing a nested mappings The mappinggenerator class is necessary to fix hgweb bugs without BC. The mappinglist is for nested formatter items. They are similar, so factored out the base class. The mappinglist could be implemented by using the mappinggenerator, but we'll probably need a direct access to the raw list, so they are implemented as separate classes. Note that tovalue() isn't conforming to the spec yet in that it may return a list of dicts containing unprintable resources. This problem will be fixed later. Tests will be added by subsequent patches.
author Yuya Nishihara <yuya@tcha.org>
date Sat, 17 Mar 2018 22:47:02 +0900
parents 676664592313
children 7c902a8345ef
comparison
equal deleted inserted replaced
37398:3235afdfcf1c 37399:0b64416224d9
167 return gen() 167 return gen()
168 return gen 168 return gen
169 169
170 def tovalue(self, context, mapping): 170 def tovalue(self, context, mapping):
171 return _unthunk(context, mapping, self._value) 171 return _unthunk(context, mapping, self._value)
172
173 class _mappingsequence(wrapped):
174 """Wrapper for sequence of template mappings
175
176 This represents an inner template structure (i.e. a list of dicts),
177 which can also be rendered by the specified named/literal template.
178
179 Template mappings may be nested.
180 """
181
182 def __init__(self, name=None, tmpl=None, sep=''):
183 if name is not None and tmpl is not None:
184 raise error.ProgrammingError('name and tmpl are mutually exclusive')
185 self._name = name
186 self._tmpl = tmpl
187 self._defaultsep = sep
188
189 def join(self, context, mapping, sep):
190 mapsiter = _iteroverlaymaps(context, mapping, self.itermaps(context))
191 if self._name:
192 itemiter = (context.process(self._name, m) for m in mapsiter)
193 elif self._tmpl:
194 itemiter = (context.expand(self._tmpl, m) for m in mapsiter)
195 else:
196 raise error.ParseError(_('not displayable without template'))
197 return joinitems(itemiter, sep)
198
199 def show(self, context, mapping):
200 return self.join(context, mapping, self._defaultsep)
201
202 def tovalue(self, context, mapping):
203 return list(self.itermaps(context))
204
205 class mappinggenerator(_mappingsequence):
206 """Wrapper for generator of template mappings
207
208 The function ``make(context, *args)`` should return a generator of
209 mapping dicts.
210 """
211
212 def __init__(self, make, args=(), name=None, tmpl=None, sep=''):
213 super(mappinggenerator, self).__init__(name, tmpl, sep)
214 self._make = make
215 self._args = args
216
217 def itermaps(self, context):
218 return self._make(context, *self._args)
219
220 class mappinglist(_mappingsequence):
221 """Wrapper for list of template mappings"""
222
223 def __init__(self, mappings, name=None, tmpl=None, sep=''):
224 super(mappinglist, self).__init__(name, tmpl, sep)
225 self._mappings = mappings
226
227 def itermaps(self, context):
228 return iter(self._mappings)
172 229
173 def hybriddict(data, key='key', value='value', fmt=None, gen=None): 230 def hybriddict(data, key='key', value='value', fmt=None, gen=None):
174 """Wrap data to support both dict-like and string-like operations""" 231 """Wrap data to support both dict-like and string-like operations"""
175 prefmt = pycompat.identity 232 prefmt = pycompat.identity
176 if fmt is None: 233 if fmt is None:
508 if not sym: 565 if not sym:
509 return _("incompatible use of template filter '%s'") % fn 566 return _("incompatible use of template filter '%s'") % fn
510 return (_("template filter '%s' is not compatible with keyword '%s'") 567 return (_("template filter '%s' is not compatible with keyword '%s'")
511 % (fn, sym)) 568 % (fn, sym))
512 569
570 def _iteroverlaymaps(context, origmapping, newmappings):
571 """Generate combined mappings from the original mapping and an iterable
572 of partial mappings to override the original"""
573 for i, nm in enumerate(newmappings):
574 lm = context.overlaymap(origmapping, nm)
575 lm['index'] = i
576 yield lm
577
513 def runmap(context, mapping, data): 578 def runmap(context, mapping, data):
514 darg, targ = data 579 darg, targ = data
515 d = evalrawexp(context, mapping, darg) 580 d = evalrawexp(context, mapping, darg)
516 if isinstance(d, wrapped): 581 if isinstance(d, wrapped):
517 diter = d.itermaps(context) 582 diter = d.itermaps(context)