mercurial/templater.py
changeset 35471 d6cfa722b044
parent 35421 a51541681b8d
child 35472 32c278eb876f
equal deleted inserted replaced
35470:7906354cbc68 35471:d6cfa722b044
   380 
   380 
   381 def _runrecursivesymbol(context, mapping, key):
   381 def _runrecursivesymbol(context, mapping, key):
   382     raise error.Abort(_("recursive reference '%s' in template") % key)
   382     raise error.Abort(_("recursive reference '%s' in template") % key)
   383 
   383 
   384 def runsymbol(context, mapping, key, default=''):
   384 def runsymbol(context, mapping, key, default=''):
   385     v = mapping.get(key)
   385     v = context.symbol(mapping, key)
   386     if v is None:
       
   387         v = context._defaults.get(key)
       
   388     if v is None:
   386     if v is None:
   389         # put poison to cut recursion. we can't move this to parsing phase
   387         # put poison to cut recursion. we can't move this to parsing phase
   390         # because "x = {x}" is allowed if "x" is a keyword. (issue4758)
   388         # because "x = {x}" is allowed if "x" is a keyword. (issue4758)
   391         safemapping = mapping.copy()
   389         safemapping = mapping.copy()
   392         safemapping[key] = _recursivesymbolblocker(key)
   390         safemapping[key] = _recursivesymbolblocker(key)
   624             s = evalstring(context, mapping, args[i]).strip()
   622             s = evalstring(context, mapping, args[i]).strip()
   625             if s:
   623             if s:
   626                 return [s]
   624                 return [s]
   627         return []
   625         return []
   628 
   626 
   629     ctx = mapping['ctx']
   627     ctx = context.resource(mapping, 'ctx')
   630     chunks = ctx.diff(match=ctx.match([], getpatterns(0), getpatterns(1)))
   628     chunks = ctx.diff(match=ctx.match([], getpatterns(0), getpatterns(1)))
   631 
   629 
   632     return ''.join(chunks)
   630     return ''.join(chunks)
   633 
   631 
   634 @templatefunc('extdata(source)', argspec='source')
   632 @templatefunc('extdata(source)', argspec='source')
   637     if 'source' not in args:
   635     if 'source' not in args:
   638         # i18n: "extdata" is a keyword
   636         # i18n: "extdata" is a keyword
   639         raise error.ParseError(_('extdata expects one argument'))
   637         raise error.ParseError(_('extdata expects one argument'))
   640 
   638 
   641     source = evalstring(context, mapping, args['source'])
   639     source = evalstring(context, mapping, args['source'])
   642     cache = mapping['cache'].setdefault('extdata', {})
   640     cache = context.resource(mapping, 'cache').setdefault('extdata', {})
   643     ctx = mapping['ctx']
   641     ctx = context.resource(mapping, 'ctx')
   644     if source in cache:
   642     if source in cache:
   645         data = cache[source]
   643         data = cache[source]
   646     else:
   644     else:
   647         data = cache[source] = scmutil.extdatasource(ctx.repo(), source)
   645         data = cache[source] = scmutil.extdatasource(ctx.repo(), source)
   648     return data.get(ctx.rev(), '')
   646     return data.get(ctx.rev(), '')
   654     if not len(args) == 1:
   652     if not len(args) == 1:
   655         # i18n: "files" is a keyword
   653         # i18n: "files" is a keyword
   656         raise error.ParseError(_("files expects one argument"))
   654         raise error.ParseError(_("files expects one argument"))
   657 
   655 
   658     raw = evalstring(context, mapping, args[0])
   656     raw = evalstring(context, mapping, args[0])
   659     ctx = mapping['ctx']
   657     ctx = context.resource(mapping, 'ctx')
   660     m = ctx.match([raw])
   658     m = ctx.match([raw])
   661     files = list(ctx.matches(m))
   659     files = list(ctx.matches(m))
   662     return templatekw.showlist("file", files, mapping)
   660     return templatekw.showlist("file", files, mapping)
   663 
   661 
   664 @templatefunc('fill(text[, width[, initialident[, hangindent]]])')
   662 @templatefunc('fill(text[, width[, initialident[, hangindent]]])')
   690     """Obtain the preferred form of a changeset hash. (DEPRECATED)"""
   688     """Obtain the preferred form of a changeset hash. (DEPRECATED)"""
   691     if len(args) != 1:
   689     if len(args) != 1:
   692         # i18n: "formatnode" is a keyword
   690         # i18n: "formatnode" is a keyword
   693         raise error.ParseError(_("formatnode expects one argument"))
   691         raise error.ParseError(_("formatnode expects one argument"))
   694 
   692 
   695     ui = mapping['ui']
   693     ui = context.resource(mapping, 'ui')
   696     node = evalstring(context, mapping, args[0])
   694     node = evalstring(context, mapping, args[0])
   697     if ui.debugflag:
   695     if ui.debugflag:
   698         return node
   696         return node
   699     return templatefilters.short(node)
   697     return templatefilters.short(node)
   700 
   698 
   856     automatic colorization."""
   854     automatic colorization."""
   857     if len(args) != 2:
   855     if len(args) != 2:
   858         # i18n: "label" is a keyword
   856         # i18n: "label" is a keyword
   859         raise error.ParseError(_("label expects two arguments"))
   857         raise error.ParseError(_("label expects two arguments"))
   860 
   858 
   861     ui = mapping['ui']
   859     ui = context.resource(mapping, 'ui')
   862     thing = evalstring(context, mapping, args[1])
   860     thing = evalstring(context, mapping, args[1])
   863     # preserve unknown symbol as literal so effects like 'red', 'bold',
   861     # preserve unknown symbol as literal so effects like 'red', 'bold',
   864     # etc. don't need to be quoted
   862     # etc. don't need to be quoted
   865     label = evalstringliteral(context, mapping, args[0])
   863     label = evalstringliteral(context, mapping, args[0])
   866 
   864 
  1028     the current working directory."""
  1026     the current working directory."""
  1029     if len(args) != 1:
  1027     if len(args) != 1:
  1030         # i18n: "relpath" is a keyword
  1028         # i18n: "relpath" is a keyword
  1031         raise error.ParseError(_("relpath expects one argument"))
  1029         raise error.ParseError(_("relpath expects one argument"))
  1032 
  1030 
  1033     repo = mapping['ctx'].repo()
  1031     repo = context.resource(mapping, 'ctx').repo()
  1034     path = evalstring(context, mapping, args[0])
  1032     path = evalstring(context, mapping, args[0])
  1035     return repo.pathto(path)
  1033     return repo.pathto(path)
  1036 
  1034 
  1037 @templatefunc('revset(query[, formatargs...])')
  1035 @templatefunc('revset(query[, formatargs...])')
  1038 def revset(context, mapping, args):
  1036 def revset(context, mapping, args):
  1041     if not len(args) > 0:
  1039     if not len(args) > 0:
  1042         # i18n: "revset" is a keyword
  1040         # i18n: "revset" is a keyword
  1043         raise error.ParseError(_("revset expects one or more arguments"))
  1041         raise error.ParseError(_("revset expects one or more arguments"))
  1044 
  1042 
  1045     raw = evalstring(context, mapping, args[0])
  1043     raw = evalstring(context, mapping, args[0])
  1046     ctx = mapping['ctx']
  1044     ctx = context.resource(mapping, 'ctx')
  1047     repo = ctx.repo()
  1045     repo = ctx.repo()
  1048 
  1046 
  1049     def query(expr):
  1047     def query(expr):
  1050         m = revsetmod.match(repo.ui, expr, repo=repo)
  1048         m = revsetmod.match(repo.ui, expr, repo=repo)
  1051         return m(repo)
  1049         return m(repo)
  1053     if len(args) > 1:
  1051     if len(args) > 1:
  1054         formatargs = [evalfuncarg(context, mapping, a) for a in args[1:]]
  1052         formatargs = [evalfuncarg(context, mapping, a) for a in args[1:]]
  1055         revs = query(revsetlang.formatspec(raw, *formatargs))
  1053         revs = query(revsetlang.formatspec(raw, *formatargs))
  1056         revs = list(revs)
  1054         revs = list(revs)
  1057     else:
  1055     else:
  1058         revsetcache = mapping['cache'].setdefault("revsetcache", {})
  1056         cache = context.resource(mapping, 'cache')
       
  1057         revsetcache = cache.setdefault("revsetcache", {})
  1059         if raw in revsetcache:
  1058         if raw in revsetcache:
  1060             revs = revsetcache[raw]
  1059             revs = revsetcache[raw]
  1061         else:
  1060         else:
  1062             revs = query(raw)
  1061             revs = query(raw)
  1063             revs = list(revs)
  1062             revs = list(revs)
  1114                                 _("shortest() expects an integer minlength"))
  1113                                 _("shortest() expects an integer minlength"))
  1115 
  1114 
  1116     # _partialmatch() of filtered changelog could take O(len(repo)) time,
  1115     # _partialmatch() of filtered changelog could take O(len(repo)) time,
  1117     # which would be unacceptably slow. so we look for hash collision in
  1116     # which would be unacceptably slow. so we look for hash collision in
  1118     # unfiltered space, which means some hashes may be slightly longer.
  1117     # unfiltered space, which means some hashes may be slightly longer.
  1119     cl = mapping['ctx']._repo.unfiltered().changelog
  1118     cl = context.resource(mapping, 'ctx')._repo.unfiltered().changelog
  1120     return cl.shortest(node, minlength)
  1119     return cl.shortest(node, minlength)
  1121 
  1120 
  1122 @templatefunc('strip(text[, chars])')
  1121 @templatefunc('strip(text[, chars])')
  1123 def strip(context, mapping, args):
  1122 def strip(context, mapping, args):
  1124     """Strip characters from a string. By default,
  1123     """Strip characters from a string. By default,
  1300             defaults = {}
  1299             defaults = {}
  1301         self._defaults = defaults
  1300         self._defaults = defaults
  1302         self._aliasmap = _aliasrules.buildmap(aliases)
  1301         self._aliasmap = _aliasrules.buildmap(aliases)
  1303         self._cache = {}  # key: (func, data)
  1302         self._cache = {}  # key: (func, data)
  1304 
  1303 
       
  1304     def symbol(self, mapping, key):
       
  1305         """Resolve symbol to value or function; None if nothing found"""
       
  1306         v = mapping.get(key)
       
  1307         if v is None:
       
  1308             v = self._defaults.get(key)
       
  1309         return v
       
  1310 
       
  1311     def resource(self, mapping, key):
       
  1312         """Return internal data (e.g. cache) used for keyword/function
       
  1313         evaluation"""
       
  1314         return mapping[key]
       
  1315 
  1305     def _load(self, t):
  1316     def _load(self, t):
  1306         '''load, parse, and cache a template'''
  1317         '''load, parse, and cache a template'''
  1307         if t not in self._cache:
  1318         if t not in self._cache:
  1308             # put poison to cut recursion while compiling 't'
  1319             # put poison to cut recursion while compiling 't'
  1309             self._cache[t] = (_runrecursivesymbol, t)
  1320             self._cache[t] = (_runrecursivesymbol, t)