Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/templater.py @ 31886:bdda942f4b9c
templater: add support for keyword arguments
Unlike revset, function arguments are pre-processed in templater. That's why
we need to define argspec per function. An argspec field looks somewhat
redundant in @templatefunc definition as a name field contains human-readable
list of arguments. I'll make function doc be built from argspec later.
Ported separate() function as an example.
author | Yuya Nishihara <yuya@tcha.org> |
---|---|
date | Mon, 03 Apr 2017 21:22:39 +0900 |
parents | d18b624c1c06 |
children | f7b3677f66cd |
comparison
equal
deleted
inserted
replaced
31885:d18b624c1c06 | 31886:bdda942f4b9c |
---|---|
368 def runtemplate(context, mapping, template): | 368 def runtemplate(context, mapping, template): |
369 for func, data in template: | 369 for func, data in template: |
370 yield func(context, mapping, data) | 370 yield func(context, mapping, data) |
371 | 371 |
372 def buildfilter(exp, context): | 372 def buildfilter(exp, context): |
373 arg = compileexp(exp[1], context, methods) | |
374 n = getsymbol(exp[2]) | 373 n = getsymbol(exp[2]) |
375 if n in context._filters: | 374 if n in context._filters: |
376 filt = context._filters[n] | 375 filt = context._filters[n] |
376 arg = compileexp(exp[1], context, methods) | |
377 return (runfilter, (arg, filt)) | 377 return (runfilter, (arg, filt)) |
378 if n in funcs: | 378 if n in funcs: |
379 f = funcs[n] | 379 f = funcs[n] |
380 return (f, [arg]) | 380 args = _buildfuncargs(exp[1], context, methods, n, f._argspec) |
381 return (f, args) | |
381 raise error.ParseError(_("unknown function '%s'") % n) | 382 raise error.ParseError(_("unknown function '%s'") % n) |
382 | 383 |
383 def runfilter(context, mapping, data): | 384 def runfilter(context, mapping, data): |
384 arg, filt = data | 385 arg, filt = data |
385 thing = evalfuncarg(context, mapping, arg) | 386 thing = evalfuncarg(context, mapping, arg) |
450 except ZeroDivisionError: | 451 except ZeroDivisionError: |
451 raise error.Abort(_('division by zero is not defined')) | 452 raise error.Abort(_('division by zero is not defined')) |
452 | 453 |
453 def buildfunc(exp, context): | 454 def buildfunc(exp, context): |
454 n = getsymbol(exp[1]) | 455 n = getsymbol(exp[1]) |
455 args = [compileexp(x, context, exprmethods) for x in getlist(exp[2])] | |
456 if n in funcs: | 456 if n in funcs: |
457 f = funcs[n] | 457 f = funcs[n] |
458 args = _buildfuncargs(exp[2], context, exprmethods, n, f._argspec) | |
458 return (f, args) | 459 return (f, args) |
459 if n in context._filters: | 460 if n in context._filters: |
461 args = _buildfuncargs(exp[2], context, exprmethods, n, argspec=None) | |
460 if len(args) != 1: | 462 if len(args) != 1: |
461 raise error.ParseError(_("filter %s expects one argument") % n) | 463 raise error.ParseError(_("filter %s expects one argument") % n) |
462 f = context._filters[n] | 464 f = context._filters[n] |
463 return (runfilter, (args[0], f)) | 465 return (runfilter, (args[0], f)) |
464 raise error.ParseError(_("unknown function '%s'") % n) | 466 raise error.ParseError(_("unknown function '%s'") % n) |
467 | |
468 def _buildfuncargs(exp, context, curmethods, funcname, argspec): | |
469 """Compile parsed tree of function arguments into list or dict of | |
470 (func, data) pairs""" | |
471 def compiledict(xs): | |
472 return dict((k, compileexp(x, context, curmethods)) | |
473 for k, x in xs.iteritems()) | |
474 def compilelist(xs): | |
475 return [compileexp(x, context, curmethods) for x in xs] | |
476 | |
477 if not argspec: | |
478 # filter or function with no argspec: return list of positional args | |
479 return compilelist(getlist(exp)) | |
480 | |
481 # function with argspec: return dict of named args | |
482 _poskeys, varkey, _keys = argspec = parser.splitargspec(argspec) | |
483 treeargs = parser.buildargsdict(getlist(exp), funcname, argspec, | |
484 keyvaluenode='keyvalue', keynode='symbol') | |
485 compargs = {} | |
486 if varkey: | |
487 compargs[varkey] = compilelist(treeargs.pop(varkey)) | |
488 compargs.update(compiledict(treeargs)) | |
489 return compargs | |
465 | 490 |
466 def buildkeyvaluepair(exp, content): | 491 def buildkeyvaluepair(exp, content): |
467 raise error.ParseError(_("can't use a key-value pair in this context")) | 492 raise error.ParseError(_("can't use a key-value pair in this context")) |
468 | 493 |
469 # dict of template built-in functions | 494 # dict of template built-in functions |
830 text = evalstring(context, mapping, args[0]) | 855 text = evalstring(context, mapping, args[0]) |
831 style = evalstring(context, mapping, args[1]) | 856 style = evalstring(context, mapping, args[1]) |
832 | 857 |
833 return minirst.format(text, style=style, keep=['verbose']) | 858 return minirst.format(text, style=style, keep=['verbose']) |
834 | 859 |
835 @templatefunc('separate(sep, args)') | 860 @templatefunc('separate(sep, args)', argspec='sep *args') |
836 def separate(context, mapping, args): | 861 def separate(context, mapping, args): |
837 """Add a separator between non-empty arguments.""" | 862 """Add a separator between non-empty arguments.""" |
838 if not args: | 863 if 'sep' not in args: |
839 # i18n: "separate" is a keyword | 864 # i18n: "separate" is a keyword |
840 raise error.ParseError(_("separate expects at least one argument")) | 865 raise error.ParseError(_("separate expects at least one argument")) |
841 | 866 |
842 sep = evalstring(context, mapping, args[0]) | 867 sep = evalstring(context, mapping, args['sep']) |
843 first = True | 868 first = True |
844 for arg in args[1:]: | 869 for arg in args['args']: |
845 argstr = evalstring(context, mapping, arg) | 870 argstr = evalstring(context, mapping, arg) |
846 if not argstr: | 871 if not argstr: |
847 continue | 872 continue |
848 if first: | 873 if first: |
849 first = False | 874 first = False |