mercurial/templater.py
changeset 28545 1d461ee26e1b
parent 28462 dbba18ba26d4
child 28546 1987ed32efca
equal deleted inserted replaced
28544:4d93d73b8aec 28545:1d461ee26e1b
   175 
   175 
   176     if quote:
   176     if quote:
   177         raise error.ParseError(_("unterminated string"), start)
   177         raise error.ParseError(_("unterminated string"), start)
   178     return parsed, pos
   178     return parsed, pos
   179 
   179 
       
   180 def parse(tmpl):
       
   181     """Parse template string into tree"""
       
   182     parsed, pos = _parsetemplate(tmpl, 0, len(tmpl))
       
   183     assert pos == len(tmpl), 'unquoted template should be consumed'
       
   184     return ('template', parsed)
       
   185 
   180 def compiletemplate(tmpl, context):
   186 def compiletemplate(tmpl, context):
   181     parsed, pos = _parsetemplate(tmpl, 0, len(tmpl))
   187     """Parse and compile template string to (func, data) pair"""
   182     return [compileexp(e, context, methods) for e in parsed]
   188     return compileexp(parse(tmpl), context, methods)
   183 
   189 
   184 def compileexp(exp, context, curmethods):
   190 def compileexp(exp, context, curmethods):
   185     t = exp[0]
   191     t = exp[0]
   186     if t in curmethods:
   192     if t in curmethods:
   187         return curmethods[t](exp, context)
   193         return curmethods[t](exp, context)
   200     if x[0] == 'list':
   206     if x[0] == 'list':
   201         return getlist(x[1]) + [x[2]]
   207         return getlist(x[1]) + [x[2]]
   202     return [x]
   208     return [x]
   203 
   209 
   204 def gettemplate(exp, context):
   210 def gettemplate(exp, context):
       
   211     """Compile given template tree or load named template from map file;
       
   212     returns (func, data) pair"""
   205     if exp[0] == 'template':
   213     if exp[0] == 'template':
   206         return [compileexp(e, context, methods) for e in exp[1]]
   214         return compileexp(exp, context, methods)
   207     if exp[0] == 'symbol':
   215     if exp[0] == 'symbol':
   208         # unlike runsymbol(), here 'symbol' is always taken as template name
   216         # unlike runsymbol(), here 'symbol' is always taken as template name
   209         # even if it exists in mapping. this allows us to override mapping
   217         # even if it exists in mapping. this allows us to override mapping
   210         # by web templates, e.g. 'changelogtag' is redefined in map file.
   218         # by web templates, e.g. 'changelogtag' is redefined in map file.
   211         return context._load(exp[1])
   219         return context._load(exp[1])
   306         raise error.Abort(_("template filter '%s' is not compatible with "
   314         raise error.Abort(_("template filter '%s' is not compatible with "
   307                            "keyword '%s'") % (filt.func_name, dt))
   315                            "keyword '%s'") % (filt.func_name, dt))
   308 
   316 
   309 def buildmap(exp, context):
   317 def buildmap(exp, context):
   310     func, data = compileexp(exp[1], context, methods)
   318     func, data = compileexp(exp[1], context, methods)
   311     ctmpl = gettemplate(exp[2], context)
   319     tfunc, tdata = gettemplate(exp[2], context)
   312     return (runmap, (func, data, ctmpl))
   320     return (runmap, (func, data, tfunc, tdata))
   313 
   321 
   314 def runmap(context, mapping, data):
   322 def runmap(context, mapping, data):
   315     func, data, ctmpl = data
   323     func, data, tfunc, tdata = data
   316     d = func(context, mapping, data)
   324     d = func(context, mapping, data)
   317     if util.safehasattr(d, 'itermaps'):
   325     if util.safehasattr(d, 'itermaps'):
   318         diter = d.itermaps()
   326         diter = d.itermaps()
   319     else:
   327     else:
   320         try:
   328         try:
   328     for i in diter:
   336     for i in diter:
   329         lm = mapping.copy()
   337         lm = mapping.copy()
   330         if isinstance(i, dict):
   338         if isinstance(i, dict):
   331             lm.update(i)
   339             lm.update(i)
   332             lm['originalnode'] = mapping.get('node')
   340             lm['originalnode'] = mapping.get('node')
   333             yield runtemplate(context, lm, ctmpl)
   341             yield tfunc(context, lm, tdata)
   334         else:
   342         else:
   335             # v is not an iterable of dicts, this happen when 'key'
   343             # v is not an iterable of dicts, this happen when 'key'
   336             # has been fully expanded already and format is useless.
   344             # has been fully expanded already and format is useless.
   337             # If so, return the expanded value.
   345             # If so, return the expanded value.
   338             yield i
   346             yield i
   855             filters = {}
   863             filters = {}
   856         self._filters = filters
   864         self._filters = filters
   857         if defaults is None:
   865         if defaults is None:
   858             defaults = {}
   866             defaults = {}
   859         self._defaults = defaults
   867         self._defaults = defaults
   860         self._cache = {}
   868         self._cache = {}  # key: (func, data)
   861 
   869 
   862     def _load(self, t):
   870     def _load(self, t):
   863         '''load, parse, and cache a template'''
   871         '''load, parse, and cache a template'''
   864         if t not in self._cache:
   872         if t not in self._cache:
   865             # put poison to cut recursion while compiling 't'
   873             # put poison to cut recursion while compiling 't'
   866             self._cache[t] = [(_runrecursivesymbol, t)]
   874             self._cache[t] = (_runrecursivesymbol, t)
   867             try:
   875             try:
   868                 self._cache[t] = compiletemplate(self._loader(t), self)
   876                 self._cache[t] = compiletemplate(self._loader(t), self)
   869             except: # re-raises
   877             except: # re-raises
   870                 del self._cache[t]
   878                 del self._cache[t]
   871                 raise
   879                 raise
   873 
   881 
   874     def process(self, t, mapping):
   882     def process(self, t, mapping):
   875         '''Perform expansion. t is name of map element to expand.
   883         '''Perform expansion. t is name of map element to expand.
   876         mapping contains added elements for use during expansion. Is a
   884         mapping contains added elements for use during expansion. Is a
   877         generator.'''
   885         generator.'''
   878         return _flatten(runtemplate(self, mapping, self._load(t)))
   886         func, data = self._load(t)
       
   887         return _flatten(func(self, mapping, data))
   879 
   888 
   880 engines = {'default': engine}
   889 engines = {'default': engine}
   881 
   890 
   882 def stylelist():
   891 def stylelist():
   883     paths = templatepaths()
   892     paths = templatepaths()