comparison mercurial/formatter.py @ 31172:16272d8c24f6

formatter: add support for changeset templating Some formatter-based commands provide fields that are identical to the ones defined in templatekw, but we had to specify them manually to support all changeset-based template keywords. This patch adds fm.context() that populates all templatekw. These keywords are available only in template output, so we still need to set important keywords via fm.data() if they should be available in e.g. JSON output. Currently fm.context() takes only 'ctx' argument. It will eventually be extended to take 'fctx' to support file-based keywords (e.g. {path}) seen in hgweb.
author Yuya Nishihara <yuya@tcha.org>
date Sat, 25 Feb 2017 17:00:07 +0900
parents e64b70c96338
children 5660c45ecba6
comparison
equal deleted inserted replaced
31171:1ec89cf0ea49 31172:16272d8c24f6
10 The formatter provides API to show data in various ways. The following 10 The formatter provides API to show data in various ways. The following
11 functions should be used in place of ui.write(): 11 functions should be used in place of ui.write():
12 12
13 - fm.write() for unconditional output 13 - fm.write() for unconditional output
14 - fm.condwrite() to show some extra data conditionally in plain output 14 - fm.condwrite() to show some extra data conditionally in plain output
15 - fm.context() to provide changectx to template output
15 - fm.data() to provide extra data to JSON or template output 16 - fm.data() to provide extra data to JSON or template output
16 - fm.plain() to show raw text that isn't provided to JSON or template output 17 - fm.plain() to show raw text that isn't provided to JSON or template output
17 18
18 To show structured data (e.g. date tuples, dicts, lists), apply fm.format*() 19 To show structured data (e.g. date tuples, dicts, lists), apply fm.format*()
19 beforehand so the data is converted to the appropriate data type. Use 20 beforehand so the data is converted to the appropriate data type. Use
169 def formatlist(self, data, name, fmt='%s', sep=' '): 170 def formatlist(self, data, name, fmt='%s', sep=' '):
170 '''convert iterable to appropriate list format''' 171 '''convert iterable to appropriate list format'''
171 # name is mandatory argument for now, but it could be optional if 172 # name is mandatory argument for now, but it could be optional if
172 # we have default template keyword, e.g. {item} 173 # we have default template keyword, e.g. {item}
173 return self._converter.formatlist(data, name, fmt, sep) 174 return self._converter.formatlist(data, name, fmt, sep)
175 def context(self, **ctxs):
176 '''insert context objects to be used to render template keywords'''
177 pass
174 def data(self, **data): 178 def data(self, **data):
175 '''insert data into item that's not shown in default output''' 179 '''insert data into item that's not shown in default output'''
176 self._item.update(data) 180 self._item.update(data)
177 def write(self, fields, deftext, *fielddata, **opts): 181 def write(self, fields, deftext, *fielddata, **opts):
178 '''do default text output while assigning data to item''' 182 '''do default text output while assigning data to item'''
343 347
344 class templateformatter(baseformatter): 348 class templateformatter(baseformatter):
345 def __init__(self, ui, topic, opts): 349 def __init__(self, ui, topic, opts):
346 baseformatter.__init__(self, ui, topic, opts, _templateconverter) 350 baseformatter.__init__(self, ui, topic, opts, _templateconverter)
347 self._topic = topic 351 self._topic = topic
348 self._t = gettemplater(ui, topic, opts.get('template', '')) 352 self._t = gettemplater(ui, topic, opts.get('template', ''),
349 def _showitem(self): 353 cache=templatekw.defaulttempl)
350 g = self._t(self._topic, ui=self._ui, **self._item) 354 self._cache = {} # for templatekw/funcs to store reusable data
355 def context(self, **ctxs):
356 '''insert context objects to be used to render template keywords'''
357 assert all(k == 'ctx' for k in ctxs)
358 self._item.update(ctxs)
359 def _showitem(self):
360 # TODO: add support for filectx. probably each template keyword or
361 # function will have to declare dependent resources. e.g.
362 # @templatekeyword(..., requires=('ctx',))
363 if 'ctx' in self._item:
364 props = templatekw.keywords.copy()
365 # explicitly-defined fields precede templatekw
366 props.update(self._item)
367 # but template resources must be always available
368 props['templ'] = self._t
369 props['repo'] = props['ctx'].repo()
370 props['revcache'] = {}
371 else:
372 props = self._item
373 g = self._t(self._topic, ui=self._ui, cache=self._cache, **props)
351 self._ui.write(templater.stringify(g)) 374 self._ui.write(templater.stringify(g))
352 375
353 def lookuptemplate(ui, topic, tmpl): 376 def lookuptemplate(ui, topic, tmpl):
354 # looks like a literal template? 377 # looks like a literal template?
355 if '{' in tmpl: 378 if '{' in tmpl:
380 return tmpl, None 403 return tmpl, None
381 404
382 # constant string? 405 # constant string?
383 return tmpl, None 406 return tmpl, None
384 407
385 def gettemplater(ui, topic, spec): 408 def gettemplater(ui, topic, spec, cache=None):
386 tmpl, mapfile = lookuptemplate(ui, topic, spec) 409 tmpl, mapfile = lookuptemplate(ui, topic, spec)
387 assert not (tmpl and mapfile) 410 assert not (tmpl and mapfile)
388 if mapfile: 411 if mapfile:
389 return templater.templater.frommapfile(mapfile) 412 return templater.templater.frommapfile(mapfile, cache=cache)
390 return maketemplater(ui, topic, tmpl) 413 return maketemplater(ui, topic, tmpl, cache=cache)
391 414
392 def maketemplater(ui, topic, tmpl, cache=None): 415 def maketemplater(ui, topic, tmpl, cache=None):
393 """Create a templater from a string template 'tmpl'""" 416 """Create a templater from a string template 'tmpl'"""
394 aliases = ui.configitems('templatealias') 417 aliases = ui.configitems('templatealias')
395 t = templater.templater(cache=cache, aliases=aliases) 418 t = templater.templater(cache=cache, aliases=aliases)