comparison mercurial/templater.py @ 35468:32c278eb876f

templater: keep default resources per template engine (API) This allows us to register a repo object as a resource in hgweb template, without loosing '{repo}' symbol: symbol('repo') -> mapping['repo'] (n/a) -> defaults['repo'] resource('repo') -> mapping['repo'] (n/a) -> resources['repo'] I'm thinking of redesigning the templatekw API to take (context, mapping) in place of **(context._resources + mapping), but that will be a big change and not implemented yet. props['templ'] is ported to the resources dict as an example. .. api:: mapping does not contain all template resources. use context.resource() in template functions.
author Yuya Nishihara <yuya@tcha.org>
date Thu, 21 Dec 2017 21:29:06 +0900
parents d6cfa722b044
children a33be093ec62
comparison
equal deleted inserted replaced
35467:d6cfa722b044 35468:32c278eb876f
391 try: 391 try:
392 v = context.process(key, safemapping) 392 v = context.process(key, safemapping)
393 except TemplateNotFound: 393 except TemplateNotFound:
394 v = default 394 v = default
395 if callable(v): 395 if callable(v):
396 return v(**pycompat.strkwargs(mapping)) 396 # TODO: templatekw functions will be updated to take (context, mapping)
397 # pair instead of **props
398 props = context._resources.copy()
399 props.update(mapping)
400 return v(**props)
397 return v 401 return v
398 402
399 def buildtemplate(exp, context): 403 def buildtemplate(exp, context):
400 ctmpl = [compileexp(e, context, methods) for e in exp[1:]] 404 ctmpl = [compileexp(e, context, methods) for e in exp[1:]]
401 return (runtemplate, ctmpl) 405 return (runtemplate, ctmpl)
655 659
656 raw = evalstring(context, mapping, args[0]) 660 raw = evalstring(context, mapping, args[0])
657 ctx = context.resource(mapping, 'ctx') 661 ctx = context.resource(mapping, 'ctx')
658 m = ctx.match([raw]) 662 m = ctx.match([raw])
659 files = list(ctx.matches(m)) 663 files = list(ctx.matches(m))
660 return templatekw.showlist("file", files, mapping) 664 # TODO: pass (context, mapping) pair to keyword function
665 props = context._resources.copy()
666 props.update(mapping)
667 return templatekw.showlist("file", files, props)
661 668
662 @templatefunc('fill(text[, width[, initialident[, hangindent]]])') 669 @templatefunc('fill(text[, width[, initialident[, hangindent]]])')
663 def fill(context, mapping, args): 670 def fill(context, mapping, args):
664 """Fill many 671 """Fill many
665 paragraphs with optional indentation. See the "fill" filter.""" 672 paragraphs with optional indentation. See the "fill" filter."""
876 883
877 pattern = None 884 pattern = None
878 if len(args) == 1: 885 if len(args) == 1:
879 pattern = evalstring(context, mapping, args[0]) 886 pattern = evalstring(context, mapping, args[0])
880 887
881 return templatekw.showlatesttags(pattern, **pycompat.strkwargs(mapping)) 888 # TODO: pass (context, mapping) pair to keyword function
889 props = context._resources.copy()
890 props.update(mapping)
891 return templatekw.showlatesttags(pattern, **pycompat.strkwargs(props))
882 892
883 @templatefunc('localdate(date[, tz])') 893 @templatefunc('localdate(date[, tz])')
884 def localdate(context, mapping, args): 894 def localdate(context, mapping, args):
885 """Converts a date to the specified timezone. 895 """Converts a date to the specified timezone.
886 The default is local date.""" 896 The default is local date."""
1060 else: 1070 else:
1061 revs = query(raw) 1071 revs = query(raw)
1062 revs = list(revs) 1072 revs = list(revs)
1063 revsetcache[raw] = revs 1073 revsetcache[raw] = revs
1064 1074
1075 # TODO: pass (context, mapping) pair to keyword function
1076 props = context._resources.copy()
1077 props.update(mapping)
1065 return templatekw.showrevslist("revision", revs, 1078 return templatekw.showrevslist("revision", revs,
1066 **pycompat.strkwargs(mapping)) 1079 **pycompat.strkwargs(props))
1067 1080
1068 @templatefunc('rstdoc(text, style)') 1081 @templatefunc('rstdoc(text, style)')
1069 def rstdoc(context, mapping, args): 1082 def rstdoc(context, mapping, args):
1070 """Format reStructuredText.""" 1083 """Format reStructuredText."""
1071 if len(args) != 2: 1084 if len(args) != 2:
1288 {key%format}. 1301 {key%format}.
1289 1302
1290 filter uses function to transform value. syntax is 1303 filter uses function to transform value. syntax is
1291 {key|filter1|filter2|...}.''' 1304 {key|filter1|filter2|...}.'''
1292 1305
1293 def __init__(self, loader, filters=None, defaults=None, aliases=()): 1306 def __init__(self, loader, filters=None, defaults=None, resources=None,
1307 aliases=()):
1294 self._loader = loader 1308 self._loader = loader
1295 if filters is None: 1309 if filters is None:
1296 filters = {} 1310 filters = {}
1297 self._filters = filters 1311 self._filters = filters
1298 if defaults is None: 1312 if defaults is None:
1299 defaults = {} 1313 defaults = {}
1314 if resources is None:
1315 resources = {}
1300 self._defaults = defaults 1316 self._defaults = defaults
1317 self._resources = resources
1301 self._aliasmap = _aliasrules.buildmap(aliases) 1318 self._aliasmap = _aliasrules.buildmap(aliases)
1302 self._cache = {} # key: (func, data) 1319 self._cache = {} # key: (func, data)
1303 1320
1304 def symbol(self, mapping, key): 1321 def symbol(self, mapping, key):
1305 """Resolve symbol to value or function; None if nothing found""" 1322 """Resolve symbol to value or function; None if nothing found"""
1309 return v 1326 return v
1310 1327
1311 def resource(self, mapping, key): 1328 def resource(self, mapping, key):
1312 """Return internal data (e.g. cache) used for keyword/function 1329 """Return internal data (e.g. cache) used for keyword/function
1313 evaluation""" 1330 evaluation"""
1314 return mapping[key] 1331 v = mapping.get(key)
1332 if v is None:
1333 v = self._resources.get(key)
1334 if v is None:
1335 raise KeyError
1336 return v
1315 1337
1316 def _load(self, t): 1338 def _load(self, t):
1317 '''load, parse, and cache a template''' 1339 '''load, parse, and cache a template'''
1318 if t not in self._cache: 1340 if t not in self._cache:
1319 # put poison to cut recursion while compiling 't' 1341 # put poison to cut recursion while compiling 't'
1404 class TemplateNotFound(error.Abort): 1426 class TemplateNotFound(error.Abort):
1405 pass 1427 pass
1406 1428
1407 class templater(object): 1429 class templater(object):
1408 1430
1409 def __init__(self, filters=None, defaults=None, cache=None, aliases=(), 1431 def __init__(self, filters=None, defaults=None, resources=None,
1410 minchunk=1024, maxchunk=65536): 1432 cache=None, aliases=(), minchunk=1024, maxchunk=65536):
1411 '''set up template engine. 1433 '''set up template engine.
1412 filters is dict of functions. each transforms a value into another. 1434 filters is dict of functions. each transforms a value into another.
1413 defaults is dict of default map definitions. 1435 defaults is dict of default map definitions.
1436 resources is dict of internal data (e.g. cache), which are inaccessible
1437 from user template.
1414 aliases is list of alias (name, replacement) pairs. 1438 aliases is list of alias (name, replacement) pairs.
1415 ''' 1439 '''
1416 if filters is None: 1440 if filters is None:
1417 filters = {} 1441 filters = {}
1418 if defaults is None: 1442 if defaults is None:
1419 defaults = {} 1443 defaults = {}
1444 if resources is None:
1445 resources = {}
1420 if cache is None: 1446 if cache is None:
1421 cache = {} 1447 cache = {}
1422 self.cache = cache.copy() 1448 self.cache = cache.copy()
1423 self.map = {} 1449 self.map = {}
1424 self.filters = templatefilters.filters.copy() 1450 self.filters = templatefilters.filters.copy()
1425 self.filters.update(filters) 1451 self.filters.update(filters)
1426 self.defaults = defaults 1452 self.defaults = defaults
1453 self._resources = {'templ': self}
1454 self._resources.update(resources)
1427 self._aliases = aliases 1455 self._aliases = aliases
1428 self.minchunk, self.maxchunk = minchunk, maxchunk 1456 self.minchunk, self.maxchunk = minchunk, maxchunk
1429 self.ecache = {} 1457 self.ecache = {}
1430 1458
1431 @classmethod 1459 @classmethod
1432 def frommapfile(cls, mapfile, filters=None, defaults=None, cache=None, 1460 def frommapfile(cls, mapfile, filters=None, defaults=None, resources=None,
1433 minchunk=1024, maxchunk=65536): 1461 cache=None, minchunk=1024, maxchunk=65536):
1434 """Create templater from the specified map file""" 1462 """Create templater from the specified map file"""
1435 t = cls(filters, defaults, cache, [], minchunk, maxchunk) 1463 t = cls(filters, defaults, resources, cache, [], minchunk, maxchunk)
1436 cache, tmap, aliases = _readmapfile(mapfile) 1464 cache, tmap, aliases = _readmapfile(mapfile)
1437 t.cache.update(cache) 1465 t.cache.update(cache)
1438 t.map = tmap 1466 t.map = tmap
1439 t._aliases = aliases 1467 t._aliases = aliases
1440 return t 1468 return t
1467 try: 1495 try:
1468 ecls = engines[ttype] 1496 ecls = engines[ttype]
1469 except KeyError: 1497 except KeyError:
1470 raise error.Abort(_('invalid template engine: %s') % ttype) 1498 raise error.Abort(_('invalid template engine: %s') % ttype)
1471 self.ecache[ttype] = ecls(self.load, self.filters, self.defaults, 1499 self.ecache[ttype] = ecls(self.load, self.filters, self.defaults,
1472 self._aliases) 1500 self._resources, self._aliases)
1473 proc = self.ecache[ttype] 1501 proc = self.ecache[ttype]
1474 1502
1475 stream = proc.process(t, mapping) 1503 stream = proc.process(t, mapping)
1476 if self.minchunk: 1504 if self.minchunk:
1477 stream = util.increasingchunks(stream, min=self.minchunk, 1505 stream = util.increasingchunks(stream, min=self.minchunk,