Mercurial > public > mercurial-scm > hg-stable
diff 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 |
line wrap: on
line diff
--- a/mercurial/templater.py Mon Apr 03 20:55:55 2017 +0900 +++ b/mercurial/templater.py Mon Apr 03 21:22:39 2017 +0900 @@ -370,14 +370,15 @@ yield func(context, mapping, data) def buildfilter(exp, context): - arg = compileexp(exp[1], context, methods) n = getsymbol(exp[2]) if n in context._filters: filt = context._filters[n] + arg = compileexp(exp[1], context, methods) return (runfilter, (arg, filt)) if n in funcs: f = funcs[n] - return (f, [arg]) + args = _buildfuncargs(exp[1], context, methods, n, f._argspec) + return (f, args) raise error.ParseError(_("unknown function '%s'") % n) def runfilter(context, mapping, data): @@ -452,17 +453,41 @@ def buildfunc(exp, context): n = getsymbol(exp[1]) - args = [compileexp(x, context, exprmethods) for x in getlist(exp[2])] if n in funcs: f = funcs[n] + args = _buildfuncargs(exp[2], context, exprmethods, n, f._argspec) return (f, args) if n in context._filters: + args = _buildfuncargs(exp[2], context, exprmethods, n, argspec=None) if len(args) != 1: raise error.ParseError(_("filter %s expects one argument") % n) f = context._filters[n] return (runfilter, (args[0], f)) raise error.ParseError(_("unknown function '%s'") % n) +def _buildfuncargs(exp, context, curmethods, funcname, argspec): + """Compile parsed tree of function arguments into list or dict of + (func, data) pairs""" + def compiledict(xs): + return dict((k, compileexp(x, context, curmethods)) + for k, x in xs.iteritems()) + def compilelist(xs): + return [compileexp(x, context, curmethods) for x in xs] + + if not argspec: + # filter or function with no argspec: return list of positional args + return compilelist(getlist(exp)) + + # function with argspec: return dict of named args + _poskeys, varkey, _keys = argspec = parser.splitargspec(argspec) + treeargs = parser.buildargsdict(getlist(exp), funcname, argspec, + keyvaluenode='keyvalue', keynode='symbol') + compargs = {} + if varkey: + compargs[varkey] = compilelist(treeargs.pop(varkey)) + compargs.update(compiledict(treeargs)) + return compargs + def buildkeyvaluepair(exp, content): raise error.ParseError(_("can't use a key-value pair in this context")) @@ -832,16 +857,16 @@ return minirst.format(text, style=style, keep=['verbose']) -@templatefunc('separate(sep, args)') +@templatefunc('separate(sep, args)', argspec='sep *args') def separate(context, mapping, args): """Add a separator between non-empty arguments.""" - if not args: + if 'sep' not in args: # i18n: "separate" is a keyword raise error.ParseError(_("separate expects at least one argument")) - sep = evalstring(context, mapping, args[0]) + sep = evalstring(context, mapping, args['sep']) first = True - for arg in args[1:]: + for arg in args['args']: argstr = evalstring(context, mapping, arg) if not argstr: continue