##// END OF EJS Templates
help: correct signature of separate() template function...
Yuya Nishihara -
r38179:a3b4ccbe stable
parent child Browse files
Show More
@@ -1,690 +1,690 b''
1 # templatefuncs.py - common template functions
1 # templatefuncs.py - common template functions
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import re
10 import re
11
11
12 from .i18n import _
12 from .i18n import _
13 from .node import (
13 from .node import (
14 bin,
14 bin,
15 )
15 )
16 from . import (
16 from . import (
17 color,
17 color,
18 encoding,
18 encoding,
19 error,
19 error,
20 minirst,
20 minirst,
21 obsutil,
21 obsutil,
22 pycompat,
22 pycompat,
23 registrar,
23 registrar,
24 revset as revsetmod,
24 revset as revsetmod,
25 revsetlang,
25 revsetlang,
26 scmutil,
26 scmutil,
27 templatefilters,
27 templatefilters,
28 templatekw,
28 templatekw,
29 templateutil,
29 templateutil,
30 util,
30 util,
31 )
31 )
32 from .utils import (
32 from .utils import (
33 dateutil,
33 dateutil,
34 stringutil,
34 stringutil,
35 )
35 )
36
36
37 evalrawexp = templateutil.evalrawexp
37 evalrawexp = templateutil.evalrawexp
38 evalfuncarg = templateutil.evalfuncarg
38 evalfuncarg = templateutil.evalfuncarg
39 evalboolean = templateutil.evalboolean
39 evalboolean = templateutil.evalboolean
40 evaldate = templateutil.evaldate
40 evaldate = templateutil.evaldate
41 evalinteger = templateutil.evalinteger
41 evalinteger = templateutil.evalinteger
42 evalstring = templateutil.evalstring
42 evalstring = templateutil.evalstring
43 evalstringliteral = templateutil.evalstringliteral
43 evalstringliteral = templateutil.evalstringliteral
44
44
45 # dict of template built-in functions
45 # dict of template built-in functions
46 funcs = {}
46 funcs = {}
47 templatefunc = registrar.templatefunc(funcs)
47 templatefunc = registrar.templatefunc(funcs)
48
48
49 @templatefunc('date(date[, fmt])')
49 @templatefunc('date(date[, fmt])')
50 def date(context, mapping, args):
50 def date(context, mapping, args):
51 """Format a date. See :hg:`help dates` for formatting
51 """Format a date. See :hg:`help dates` for formatting
52 strings. The default is a Unix date format, including the timezone:
52 strings. The default is a Unix date format, including the timezone:
53 "Mon Sep 04 15:13:13 2006 0700"."""
53 "Mon Sep 04 15:13:13 2006 0700"."""
54 if not (1 <= len(args) <= 2):
54 if not (1 <= len(args) <= 2):
55 # i18n: "date" is a keyword
55 # i18n: "date" is a keyword
56 raise error.ParseError(_("date expects one or two arguments"))
56 raise error.ParseError(_("date expects one or two arguments"))
57
57
58 date = evaldate(context, mapping, args[0],
58 date = evaldate(context, mapping, args[0],
59 # i18n: "date" is a keyword
59 # i18n: "date" is a keyword
60 _("date expects a date information"))
60 _("date expects a date information"))
61 fmt = None
61 fmt = None
62 if len(args) == 2:
62 if len(args) == 2:
63 fmt = evalstring(context, mapping, args[1])
63 fmt = evalstring(context, mapping, args[1])
64 if fmt is None:
64 if fmt is None:
65 return dateutil.datestr(date)
65 return dateutil.datestr(date)
66 else:
66 else:
67 return dateutil.datestr(date, fmt)
67 return dateutil.datestr(date, fmt)
68
68
69 @templatefunc('dict([[key=]value...])', argspec='*args **kwargs')
69 @templatefunc('dict([[key=]value...])', argspec='*args **kwargs')
70 def dict_(context, mapping, args):
70 def dict_(context, mapping, args):
71 """Construct a dict from key-value pairs. A key may be omitted if
71 """Construct a dict from key-value pairs. A key may be omitted if
72 a value expression can provide an unambiguous name."""
72 a value expression can provide an unambiguous name."""
73 data = util.sortdict()
73 data = util.sortdict()
74
74
75 for v in args['args']:
75 for v in args['args']:
76 k = templateutil.findsymbolicname(v)
76 k = templateutil.findsymbolicname(v)
77 if not k:
77 if not k:
78 raise error.ParseError(_('dict key cannot be inferred'))
78 raise error.ParseError(_('dict key cannot be inferred'))
79 if k in data or k in args['kwargs']:
79 if k in data or k in args['kwargs']:
80 raise error.ParseError(_("duplicated dict key '%s' inferred") % k)
80 raise error.ParseError(_("duplicated dict key '%s' inferred") % k)
81 data[k] = evalfuncarg(context, mapping, v)
81 data[k] = evalfuncarg(context, mapping, v)
82
82
83 data.update((k, evalfuncarg(context, mapping, v))
83 data.update((k, evalfuncarg(context, mapping, v))
84 for k, v in args['kwargs'].iteritems())
84 for k, v in args['kwargs'].iteritems())
85 return templateutil.hybriddict(data)
85 return templateutil.hybriddict(data)
86
86
87 @templatefunc('diff([includepattern [, excludepattern]])')
87 @templatefunc('diff([includepattern [, excludepattern]])')
88 def diff(context, mapping, args):
88 def diff(context, mapping, args):
89 """Show a diff, optionally
89 """Show a diff, optionally
90 specifying files to include or exclude."""
90 specifying files to include or exclude."""
91 if len(args) > 2:
91 if len(args) > 2:
92 # i18n: "diff" is a keyword
92 # i18n: "diff" is a keyword
93 raise error.ParseError(_("diff expects zero, one, or two arguments"))
93 raise error.ParseError(_("diff expects zero, one, or two arguments"))
94
94
95 def getpatterns(i):
95 def getpatterns(i):
96 if i < len(args):
96 if i < len(args):
97 s = evalstring(context, mapping, args[i]).strip()
97 s = evalstring(context, mapping, args[i]).strip()
98 if s:
98 if s:
99 return [s]
99 return [s]
100 return []
100 return []
101
101
102 ctx = context.resource(mapping, 'ctx')
102 ctx = context.resource(mapping, 'ctx')
103 chunks = ctx.diff(match=ctx.match([], getpatterns(0), getpatterns(1)))
103 chunks = ctx.diff(match=ctx.match([], getpatterns(0), getpatterns(1)))
104
104
105 return ''.join(chunks)
105 return ''.join(chunks)
106
106
107 @templatefunc('extdata(source)', argspec='source')
107 @templatefunc('extdata(source)', argspec='source')
108 def extdata(context, mapping, args):
108 def extdata(context, mapping, args):
109 """Show a text read from the specified extdata source. (EXPERIMENTAL)"""
109 """Show a text read from the specified extdata source. (EXPERIMENTAL)"""
110 if 'source' not in args:
110 if 'source' not in args:
111 # i18n: "extdata" is a keyword
111 # i18n: "extdata" is a keyword
112 raise error.ParseError(_('extdata expects one argument'))
112 raise error.ParseError(_('extdata expects one argument'))
113
113
114 source = evalstring(context, mapping, args['source'])
114 source = evalstring(context, mapping, args['source'])
115 cache = context.resource(mapping, 'cache').setdefault('extdata', {})
115 cache = context.resource(mapping, 'cache').setdefault('extdata', {})
116 ctx = context.resource(mapping, 'ctx')
116 ctx = context.resource(mapping, 'ctx')
117 if source in cache:
117 if source in cache:
118 data = cache[source]
118 data = cache[source]
119 else:
119 else:
120 data = cache[source] = scmutil.extdatasource(ctx.repo(), source)
120 data = cache[source] = scmutil.extdatasource(ctx.repo(), source)
121 return data.get(ctx.rev(), '')
121 return data.get(ctx.rev(), '')
122
122
123 @templatefunc('files(pattern)')
123 @templatefunc('files(pattern)')
124 def files(context, mapping, args):
124 def files(context, mapping, args):
125 """All files of the current changeset matching the pattern. See
125 """All files of the current changeset matching the pattern. See
126 :hg:`help patterns`."""
126 :hg:`help patterns`."""
127 if not len(args) == 1:
127 if not len(args) == 1:
128 # i18n: "files" is a keyword
128 # i18n: "files" is a keyword
129 raise error.ParseError(_("files expects one argument"))
129 raise error.ParseError(_("files expects one argument"))
130
130
131 raw = evalstring(context, mapping, args[0])
131 raw = evalstring(context, mapping, args[0])
132 ctx = context.resource(mapping, 'ctx')
132 ctx = context.resource(mapping, 'ctx')
133 m = ctx.match([raw])
133 m = ctx.match([raw])
134 files = list(ctx.matches(m))
134 files = list(ctx.matches(m))
135 return templateutil.compatlist(context, mapping, "file", files)
135 return templateutil.compatlist(context, mapping, "file", files)
136
136
137 @templatefunc('fill(text[, width[, initialident[, hangindent]]])')
137 @templatefunc('fill(text[, width[, initialident[, hangindent]]])')
138 def fill(context, mapping, args):
138 def fill(context, mapping, args):
139 """Fill many
139 """Fill many
140 paragraphs with optional indentation. See the "fill" filter."""
140 paragraphs with optional indentation. See the "fill" filter."""
141 if not (1 <= len(args) <= 4):
141 if not (1 <= len(args) <= 4):
142 # i18n: "fill" is a keyword
142 # i18n: "fill" is a keyword
143 raise error.ParseError(_("fill expects one to four arguments"))
143 raise error.ParseError(_("fill expects one to four arguments"))
144
144
145 text = evalstring(context, mapping, args[0])
145 text = evalstring(context, mapping, args[0])
146 width = 76
146 width = 76
147 initindent = ''
147 initindent = ''
148 hangindent = ''
148 hangindent = ''
149 if 2 <= len(args) <= 4:
149 if 2 <= len(args) <= 4:
150 width = evalinteger(context, mapping, args[1],
150 width = evalinteger(context, mapping, args[1],
151 # i18n: "fill" is a keyword
151 # i18n: "fill" is a keyword
152 _("fill expects an integer width"))
152 _("fill expects an integer width"))
153 try:
153 try:
154 initindent = evalstring(context, mapping, args[2])
154 initindent = evalstring(context, mapping, args[2])
155 hangindent = evalstring(context, mapping, args[3])
155 hangindent = evalstring(context, mapping, args[3])
156 except IndexError:
156 except IndexError:
157 pass
157 pass
158
158
159 return templatefilters.fill(text, width, initindent, hangindent)
159 return templatefilters.fill(text, width, initindent, hangindent)
160
160
161 @templatefunc('formatnode(node)')
161 @templatefunc('formatnode(node)')
162 def formatnode(context, mapping, args):
162 def formatnode(context, mapping, args):
163 """Obtain the preferred form of a changeset hash. (DEPRECATED)"""
163 """Obtain the preferred form of a changeset hash. (DEPRECATED)"""
164 if len(args) != 1:
164 if len(args) != 1:
165 # i18n: "formatnode" is a keyword
165 # i18n: "formatnode" is a keyword
166 raise error.ParseError(_("formatnode expects one argument"))
166 raise error.ParseError(_("formatnode expects one argument"))
167
167
168 ui = context.resource(mapping, 'ui')
168 ui = context.resource(mapping, 'ui')
169 node = evalstring(context, mapping, args[0])
169 node = evalstring(context, mapping, args[0])
170 if ui.debugflag:
170 if ui.debugflag:
171 return node
171 return node
172 return templatefilters.short(node)
172 return templatefilters.short(node)
173
173
174 @templatefunc('mailmap(author)')
174 @templatefunc('mailmap(author)')
175 def mailmap(context, mapping, args):
175 def mailmap(context, mapping, args):
176 """Return the author, updated according to the value
176 """Return the author, updated according to the value
177 set in the .mailmap file"""
177 set in the .mailmap file"""
178 if len(args) != 1:
178 if len(args) != 1:
179 raise error.ParseError(_("mailmap expects one argument"))
179 raise error.ParseError(_("mailmap expects one argument"))
180
180
181 author = evalstring(context, mapping, args[0])
181 author = evalstring(context, mapping, args[0])
182
182
183 cache = context.resource(mapping, 'cache')
183 cache = context.resource(mapping, 'cache')
184 repo = context.resource(mapping, 'repo')
184 repo = context.resource(mapping, 'repo')
185
185
186 if 'mailmap' not in cache:
186 if 'mailmap' not in cache:
187 data = repo.wvfs.tryread('.mailmap')
187 data = repo.wvfs.tryread('.mailmap')
188 cache['mailmap'] = stringutil.parsemailmap(data)
188 cache['mailmap'] = stringutil.parsemailmap(data)
189
189
190 return stringutil.mapname(cache['mailmap'], author)
190 return stringutil.mapname(cache['mailmap'], author)
191
191
192 @templatefunc('pad(text, width[, fillchar=\' \'[, left=False]])',
192 @templatefunc('pad(text, width[, fillchar=\' \'[, left=False]])',
193 argspec='text width fillchar left')
193 argspec='text width fillchar left')
194 def pad(context, mapping, args):
194 def pad(context, mapping, args):
195 """Pad text with a
195 """Pad text with a
196 fill character."""
196 fill character."""
197 if 'text' not in args or 'width' not in args:
197 if 'text' not in args or 'width' not in args:
198 # i18n: "pad" is a keyword
198 # i18n: "pad" is a keyword
199 raise error.ParseError(_("pad() expects two to four arguments"))
199 raise error.ParseError(_("pad() expects two to four arguments"))
200
200
201 width = evalinteger(context, mapping, args['width'],
201 width = evalinteger(context, mapping, args['width'],
202 # i18n: "pad" is a keyword
202 # i18n: "pad" is a keyword
203 _("pad() expects an integer width"))
203 _("pad() expects an integer width"))
204
204
205 text = evalstring(context, mapping, args['text'])
205 text = evalstring(context, mapping, args['text'])
206
206
207 left = False
207 left = False
208 fillchar = ' '
208 fillchar = ' '
209 if 'fillchar' in args:
209 if 'fillchar' in args:
210 fillchar = evalstring(context, mapping, args['fillchar'])
210 fillchar = evalstring(context, mapping, args['fillchar'])
211 if len(color.stripeffects(fillchar)) != 1:
211 if len(color.stripeffects(fillchar)) != 1:
212 # i18n: "pad" is a keyword
212 # i18n: "pad" is a keyword
213 raise error.ParseError(_("pad() expects a single fill character"))
213 raise error.ParseError(_("pad() expects a single fill character"))
214 if 'left' in args:
214 if 'left' in args:
215 left = evalboolean(context, mapping, args['left'])
215 left = evalboolean(context, mapping, args['left'])
216
216
217 fillwidth = width - encoding.colwidth(color.stripeffects(text))
217 fillwidth = width - encoding.colwidth(color.stripeffects(text))
218 if fillwidth <= 0:
218 if fillwidth <= 0:
219 return text
219 return text
220 if left:
220 if left:
221 return fillchar * fillwidth + text
221 return fillchar * fillwidth + text
222 else:
222 else:
223 return text + fillchar * fillwidth
223 return text + fillchar * fillwidth
224
224
225 @templatefunc('indent(text, indentchars[, firstline])')
225 @templatefunc('indent(text, indentchars[, firstline])')
226 def indent(context, mapping, args):
226 def indent(context, mapping, args):
227 """Indents all non-empty lines
227 """Indents all non-empty lines
228 with the characters given in the indentchars string. An optional
228 with the characters given in the indentchars string. An optional
229 third parameter will override the indent for the first line only
229 third parameter will override the indent for the first line only
230 if present."""
230 if present."""
231 if not (2 <= len(args) <= 3):
231 if not (2 <= len(args) <= 3):
232 # i18n: "indent" is a keyword
232 # i18n: "indent" is a keyword
233 raise error.ParseError(_("indent() expects two or three arguments"))
233 raise error.ParseError(_("indent() expects two or three arguments"))
234
234
235 text = evalstring(context, mapping, args[0])
235 text = evalstring(context, mapping, args[0])
236 indent = evalstring(context, mapping, args[1])
236 indent = evalstring(context, mapping, args[1])
237
237
238 if len(args) == 3:
238 if len(args) == 3:
239 firstline = evalstring(context, mapping, args[2])
239 firstline = evalstring(context, mapping, args[2])
240 else:
240 else:
241 firstline = indent
241 firstline = indent
242
242
243 # the indent function doesn't indent the first line, so we do it here
243 # the indent function doesn't indent the first line, so we do it here
244 return templatefilters.indent(firstline + text, indent)
244 return templatefilters.indent(firstline + text, indent)
245
245
246 @templatefunc('get(dict, key)')
246 @templatefunc('get(dict, key)')
247 def get(context, mapping, args):
247 def get(context, mapping, args):
248 """Get an attribute/key from an object. Some keywords
248 """Get an attribute/key from an object. Some keywords
249 are complex types. This function allows you to obtain the value of an
249 are complex types. This function allows you to obtain the value of an
250 attribute on these types."""
250 attribute on these types."""
251 if len(args) != 2:
251 if len(args) != 2:
252 # i18n: "get" is a keyword
252 # i18n: "get" is a keyword
253 raise error.ParseError(_("get() expects two arguments"))
253 raise error.ParseError(_("get() expects two arguments"))
254
254
255 dictarg = evalfuncarg(context, mapping, args[0])
255 dictarg = evalfuncarg(context, mapping, args[0])
256 if not util.safehasattr(dictarg, 'get'):
256 if not util.safehasattr(dictarg, 'get'):
257 # i18n: "get" is a keyword
257 # i18n: "get" is a keyword
258 raise error.ParseError(_("get() expects a dict as first argument"))
258 raise error.ParseError(_("get() expects a dict as first argument"))
259
259
260 key = evalfuncarg(context, mapping, args[1])
260 key = evalfuncarg(context, mapping, args[1])
261 return templateutil.getdictitem(dictarg, key)
261 return templateutil.getdictitem(dictarg, key)
262
262
263 @templatefunc('if(expr, then[, else])')
263 @templatefunc('if(expr, then[, else])')
264 def if_(context, mapping, args):
264 def if_(context, mapping, args):
265 """Conditionally execute based on the result of
265 """Conditionally execute based on the result of
266 an expression."""
266 an expression."""
267 if not (2 <= len(args) <= 3):
267 if not (2 <= len(args) <= 3):
268 # i18n: "if" is a keyword
268 # i18n: "if" is a keyword
269 raise error.ParseError(_("if expects two or three arguments"))
269 raise error.ParseError(_("if expects two or three arguments"))
270
270
271 test = evalboolean(context, mapping, args[0])
271 test = evalboolean(context, mapping, args[0])
272 if test:
272 if test:
273 return evalrawexp(context, mapping, args[1])
273 return evalrawexp(context, mapping, args[1])
274 elif len(args) == 3:
274 elif len(args) == 3:
275 return evalrawexp(context, mapping, args[2])
275 return evalrawexp(context, mapping, args[2])
276
276
277 @templatefunc('ifcontains(needle, haystack, then[, else])')
277 @templatefunc('ifcontains(needle, haystack, then[, else])')
278 def ifcontains(context, mapping, args):
278 def ifcontains(context, mapping, args):
279 """Conditionally execute based
279 """Conditionally execute based
280 on whether the item "needle" is in "haystack"."""
280 on whether the item "needle" is in "haystack"."""
281 if not (3 <= len(args) <= 4):
281 if not (3 <= len(args) <= 4):
282 # i18n: "ifcontains" is a keyword
282 # i18n: "ifcontains" is a keyword
283 raise error.ParseError(_("ifcontains expects three or four arguments"))
283 raise error.ParseError(_("ifcontains expects three or four arguments"))
284
284
285 haystack = evalfuncarg(context, mapping, args[1])
285 haystack = evalfuncarg(context, mapping, args[1])
286 keytype = getattr(haystack, 'keytype', None)
286 keytype = getattr(haystack, 'keytype', None)
287 try:
287 try:
288 needle = evalrawexp(context, mapping, args[0])
288 needle = evalrawexp(context, mapping, args[0])
289 needle = templateutil.unwrapastype(context, mapping, needle,
289 needle = templateutil.unwrapastype(context, mapping, needle,
290 keytype or bytes)
290 keytype or bytes)
291 found = (needle in haystack)
291 found = (needle in haystack)
292 except error.ParseError:
292 except error.ParseError:
293 found = False
293 found = False
294
294
295 if found:
295 if found:
296 return evalrawexp(context, mapping, args[2])
296 return evalrawexp(context, mapping, args[2])
297 elif len(args) == 4:
297 elif len(args) == 4:
298 return evalrawexp(context, mapping, args[3])
298 return evalrawexp(context, mapping, args[3])
299
299
300 @templatefunc('ifeq(expr1, expr2, then[, else])')
300 @templatefunc('ifeq(expr1, expr2, then[, else])')
301 def ifeq(context, mapping, args):
301 def ifeq(context, mapping, args):
302 """Conditionally execute based on
302 """Conditionally execute based on
303 whether 2 items are equivalent."""
303 whether 2 items are equivalent."""
304 if not (3 <= len(args) <= 4):
304 if not (3 <= len(args) <= 4):
305 # i18n: "ifeq" is a keyword
305 # i18n: "ifeq" is a keyword
306 raise error.ParseError(_("ifeq expects three or four arguments"))
306 raise error.ParseError(_("ifeq expects three or four arguments"))
307
307
308 test = evalstring(context, mapping, args[0])
308 test = evalstring(context, mapping, args[0])
309 match = evalstring(context, mapping, args[1])
309 match = evalstring(context, mapping, args[1])
310 if test == match:
310 if test == match:
311 return evalrawexp(context, mapping, args[2])
311 return evalrawexp(context, mapping, args[2])
312 elif len(args) == 4:
312 elif len(args) == 4:
313 return evalrawexp(context, mapping, args[3])
313 return evalrawexp(context, mapping, args[3])
314
314
315 @templatefunc('join(list, sep)')
315 @templatefunc('join(list, sep)')
316 def join(context, mapping, args):
316 def join(context, mapping, args):
317 """Join items in a list with a delimiter."""
317 """Join items in a list with a delimiter."""
318 if not (1 <= len(args) <= 2):
318 if not (1 <= len(args) <= 2):
319 # i18n: "join" is a keyword
319 # i18n: "join" is a keyword
320 raise error.ParseError(_("join expects one or two arguments"))
320 raise error.ParseError(_("join expects one or two arguments"))
321
321
322 joinset = evalrawexp(context, mapping, args[0])
322 joinset = evalrawexp(context, mapping, args[0])
323 joiner = " "
323 joiner = " "
324 if len(args) > 1:
324 if len(args) > 1:
325 joiner = evalstring(context, mapping, args[1])
325 joiner = evalstring(context, mapping, args[1])
326 if isinstance(joinset, templateutil.wrapped):
326 if isinstance(joinset, templateutil.wrapped):
327 return joinset.join(context, mapping, joiner)
327 return joinset.join(context, mapping, joiner)
328 # TODO: perhaps a generator should be stringify()-ed here, but we can't
328 # TODO: perhaps a generator should be stringify()-ed here, but we can't
329 # because hgweb abuses it as a keyword that returns a list of dicts.
329 # because hgweb abuses it as a keyword that returns a list of dicts.
330 joinset = templateutil.unwrapvalue(context, mapping, joinset)
330 joinset = templateutil.unwrapvalue(context, mapping, joinset)
331 return templateutil.joinitems(pycompat.maybebytestr(joinset), joiner)
331 return templateutil.joinitems(pycompat.maybebytestr(joinset), joiner)
332
332
333 @templatefunc('label(label, expr)')
333 @templatefunc('label(label, expr)')
334 def label(context, mapping, args):
334 def label(context, mapping, args):
335 """Apply a label to generated content. Content with
335 """Apply a label to generated content. Content with
336 a label applied can result in additional post-processing, such as
336 a label applied can result in additional post-processing, such as
337 automatic colorization."""
337 automatic colorization."""
338 if len(args) != 2:
338 if len(args) != 2:
339 # i18n: "label" is a keyword
339 # i18n: "label" is a keyword
340 raise error.ParseError(_("label expects two arguments"))
340 raise error.ParseError(_("label expects two arguments"))
341
341
342 ui = context.resource(mapping, 'ui')
342 ui = context.resource(mapping, 'ui')
343 thing = evalstring(context, mapping, args[1])
343 thing = evalstring(context, mapping, args[1])
344 # preserve unknown symbol as literal so effects like 'red', 'bold',
344 # preserve unknown symbol as literal so effects like 'red', 'bold',
345 # etc. don't need to be quoted
345 # etc. don't need to be quoted
346 label = evalstringliteral(context, mapping, args[0])
346 label = evalstringliteral(context, mapping, args[0])
347
347
348 return ui.label(thing, label)
348 return ui.label(thing, label)
349
349
350 @templatefunc('latesttag([pattern])')
350 @templatefunc('latesttag([pattern])')
351 def latesttag(context, mapping, args):
351 def latesttag(context, mapping, args):
352 """The global tags matching the given pattern on the
352 """The global tags matching the given pattern on the
353 most recent globally tagged ancestor of this changeset.
353 most recent globally tagged ancestor of this changeset.
354 If no such tags exist, the "{tag}" template resolves to
354 If no such tags exist, the "{tag}" template resolves to
355 the string "null"."""
355 the string "null"."""
356 if len(args) > 1:
356 if len(args) > 1:
357 # i18n: "latesttag" is a keyword
357 # i18n: "latesttag" is a keyword
358 raise error.ParseError(_("latesttag expects at most one argument"))
358 raise error.ParseError(_("latesttag expects at most one argument"))
359
359
360 pattern = None
360 pattern = None
361 if len(args) == 1:
361 if len(args) == 1:
362 pattern = evalstring(context, mapping, args[0])
362 pattern = evalstring(context, mapping, args[0])
363 return templatekw.showlatesttags(context, mapping, pattern)
363 return templatekw.showlatesttags(context, mapping, pattern)
364
364
365 @templatefunc('localdate(date[, tz])')
365 @templatefunc('localdate(date[, tz])')
366 def localdate(context, mapping, args):
366 def localdate(context, mapping, args):
367 """Converts a date to the specified timezone.
367 """Converts a date to the specified timezone.
368 The default is local date."""
368 The default is local date."""
369 if not (1 <= len(args) <= 2):
369 if not (1 <= len(args) <= 2):
370 # i18n: "localdate" is a keyword
370 # i18n: "localdate" is a keyword
371 raise error.ParseError(_("localdate expects one or two arguments"))
371 raise error.ParseError(_("localdate expects one or two arguments"))
372
372
373 date = evaldate(context, mapping, args[0],
373 date = evaldate(context, mapping, args[0],
374 # i18n: "localdate" is a keyword
374 # i18n: "localdate" is a keyword
375 _("localdate expects a date information"))
375 _("localdate expects a date information"))
376 if len(args) >= 2:
376 if len(args) >= 2:
377 tzoffset = None
377 tzoffset = None
378 tz = evalfuncarg(context, mapping, args[1])
378 tz = evalfuncarg(context, mapping, args[1])
379 if isinstance(tz, bytes):
379 if isinstance(tz, bytes):
380 tzoffset, remainder = dateutil.parsetimezone(tz)
380 tzoffset, remainder = dateutil.parsetimezone(tz)
381 if remainder:
381 if remainder:
382 tzoffset = None
382 tzoffset = None
383 if tzoffset is None:
383 if tzoffset is None:
384 try:
384 try:
385 tzoffset = int(tz)
385 tzoffset = int(tz)
386 except (TypeError, ValueError):
386 except (TypeError, ValueError):
387 # i18n: "localdate" is a keyword
387 # i18n: "localdate" is a keyword
388 raise error.ParseError(_("localdate expects a timezone"))
388 raise error.ParseError(_("localdate expects a timezone"))
389 else:
389 else:
390 tzoffset = dateutil.makedate()[1]
390 tzoffset = dateutil.makedate()[1]
391 return (date[0], tzoffset)
391 return (date[0], tzoffset)
392
392
393 @templatefunc('max(iterable)')
393 @templatefunc('max(iterable)')
394 def max_(context, mapping, args, **kwargs):
394 def max_(context, mapping, args, **kwargs):
395 """Return the max of an iterable"""
395 """Return the max of an iterable"""
396 if len(args) != 1:
396 if len(args) != 1:
397 # i18n: "max" is a keyword
397 # i18n: "max" is a keyword
398 raise error.ParseError(_("max expects one argument"))
398 raise error.ParseError(_("max expects one argument"))
399
399
400 iterable = evalfuncarg(context, mapping, args[0])
400 iterable = evalfuncarg(context, mapping, args[0])
401 try:
401 try:
402 x = max(pycompat.maybebytestr(iterable))
402 x = max(pycompat.maybebytestr(iterable))
403 except (TypeError, ValueError):
403 except (TypeError, ValueError):
404 # i18n: "max" is a keyword
404 # i18n: "max" is a keyword
405 raise error.ParseError(_("max first argument should be an iterable"))
405 raise error.ParseError(_("max first argument should be an iterable"))
406 return templateutil.wraphybridvalue(iterable, x, x)
406 return templateutil.wraphybridvalue(iterable, x, x)
407
407
408 @templatefunc('min(iterable)')
408 @templatefunc('min(iterable)')
409 def min_(context, mapping, args, **kwargs):
409 def min_(context, mapping, args, **kwargs):
410 """Return the min of an iterable"""
410 """Return the min of an iterable"""
411 if len(args) != 1:
411 if len(args) != 1:
412 # i18n: "min" is a keyword
412 # i18n: "min" is a keyword
413 raise error.ParseError(_("min expects one argument"))
413 raise error.ParseError(_("min expects one argument"))
414
414
415 iterable = evalfuncarg(context, mapping, args[0])
415 iterable = evalfuncarg(context, mapping, args[0])
416 try:
416 try:
417 x = min(pycompat.maybebytestr(iterable))
417 x = min(pycompat.maybebytestr(iterable))
418 except (TypeError, ValueError):
418 except (TypeError, ValueError):
419 # i18n: "min" is a keyword
419 # i18n: "min" is a keyword
420 raise error.ParseError(_("min first argument should be an iterable"))
420 raise error.ParseError(_("min first argument should be an iterable"))
421 return templateutil.wraphybridvalue(iterable, x, x)
421 return templateutil.wraphybridvalue(iterable, x, x)
422
422
423 @templatefunc('mod(a, b)')
423 @templatefunc('mod(a, b)')
424 def mod(context, mapping, args):
424 def mod(context, mapping, args):
425 """Calculate a mod b such that a / b + a mod b == a"""
425 """Calculate a mod b such that a / b + a mod b == a"""
426 if not len(args) == 2:
426 if not len(args) == 2:
427 # i18n: "mod" is a keyword
427 # i18n: "mod" is a keyword
428 raise error.ParseError(_("mod expects two arguments"))
428 raise error.ParseError(_("mod expects two arguments"))
429
429
430 func = lambda a, b: a % b
430 func = lambda a, b: a % b
431 return templateutil.runarithmetic(context, mapping,
431 return templateutil.runarithmetic(context, mapping,
432 (func, args[0], args[1]))
432 (func, args[0], args[1]))
433
433
434 @templatefunc('obsfateoperations(markers)')
434 @templatefunc('obsfateoperations(markers)')
435 def obsfateoperations(context, mapping, args):
435 def obsfateoperations(context, mapping, args):
436 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
436 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
437 if len(args) != 1:
437 if len(args) != 1:
438 # i18n: "obsfateoperations" is a keyword
438 # i18n: "obsfateoperations" is a keyword
439 raise error.ParseError(_("obsfateoperations expects one argument"))
439 raise error.ParseError(_("obsfateoperations expects one argument"))
440
440
441 markers = evalfuncarg(context, mapping, args[0])
441 markers = evalfuncarg(context, mapping, args[0])
442
442
443 try:
443 try:
444 data = obsutil.markersoperations(markers)
444 data = obsutil.markersoperations(markers)
445 return templateutil.hybridlist(data, name='operation')
445 return templateutil.hybridlist(data, name='operation')
446 except (TypeError, KeyError):
446 except (TypeError, KeyError):
447 # i18n: "obsfateoperations" is a keyword
447 # i18n: "obsfateoperations" is a keyword
448 errmsg = _("obsfateoperations first argument should be an iterable")
448 errmsg = _("obsfateoperations first argument should be an iterable")
449 raise error.ParseError(errmsg)
449 raise error.ParseError(errmsg)
450
450
451 @templatefunc('obsfatedate(markers)')
451 @templatefunc('obsfatedate(markers)')
452 def obsfatedate(context, mapping, args):
452 def obsfatedate(context, mapping, args):
453 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
453 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
454 if len(args) != 1:
454 if len(args) != 1:
455 # i18n: "obsfatedate" is a keyword
455 # i18n: "obsfatedate" is a keyword
456 raise error.ParseError(_("obsfatedate expects one argument"))
456 raise error.ParseError(_("obsfatedate expects one argument"))
457
457
458 markers = evalfuncarg(context, mapping, args[0])
458 markers = evalfuncarg(context, mapping, args[0])
459
459
460 try:
460 try:
461 data = obsutil.markersdates(markers)
461 data = obsutil.markersdates(markers)
462 return templateutil.hybridlist(data, name='date', fmt='%d %d')
462 return templateutil.hybridlist(data, name='date', fmt='%d %d')
463 except (TypeError, KeyError):
463 except (TypeError, KeyError):
464 # i18n: "obsfatedate" is a keyword
464 # i18n: "obsfatedate" is a keyword
465 errmsg = _("obsfatedate first argument should be an iterable")
465 errmsg = _("obsfatedate first argument should be an iterable")
466 raise error.ParseError(errmsg)
466 raise error.ParseError(errmsg)
467
467
468 @templatefunc('obsfateusers(markers)')
468 @templatefunc('obsfateusers(markers)')
469 def obsfateusers(context, mapping, args):
469 def obsfateusers(context, mapping, args):
470 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
470 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
471 if len(args) != 1:
471 if len(args) != 1:
472 # i18n: "obsfateusers" is a keyword
472 # i18n: "obsfateusers" is a keyword
473 raise error.ParseError(_("obsfateusers expects one argument"))
473 raise error.ParseError(_("obsfateusers expects one argument"))
474
474
475 markers = evalfuncarg(context, mapping, args[0])
475 markers = evalfuncarg(context, mapping, args[0])
476
476
477 try:
477 try:
478 data = obsutil.markersusers(markers)
478 data = obsutil.markersusers(markers)
479 return templateutil.hybridlist(data, name='user')
479 return templateutil.hybridlist(data, name='user')
480 except (TypeError, KeyError, ValueError):
480 except (TypeError, KeyError, ValueError):
481 # i18n: "obsfateusers" is a keyword
481 # i18n: "obsfateusers" is a keyword
482 msg = _("obsfateusers first argument should be an iterable of "
482 msg = _("obsfateusers first argument should be an iterable of "
483 "obsmakers")
483 "obsmakers")
484 raise error.ParseError(msg)
484 raise error.ParseError(msg)
485
485
486 @templatefunc('obsfateverb(successors, markers)')
486 @templatefunc('obsfateverb(successors, markers)')
487 def obsfateverb(context, mapping, args):
487 def obsfateverb(context, mapping, args):
488 """Compute obsfate related information based on successors (EXPERIMENTAL)"""
488 """Compute obsfate related information based on successors (EXPERIMENTAL)"""
489 if len(args) != 2:
489 if len(args) != 2:
490 # i18n: "obsfateverb" is a keyword
490 # i18n: "obsfateverb" is a keyword
491 raise error.ParseError(_("obsfateverb expects two arguments"))
491 raise error.ParseError(_("obsfateverb expects two arguments"))
492
492
493 successors = evalfuncarg(context, mapping, args[0])
493 successors = evalfuncarg(context, mapping, args[0])
494 markers = evalfuncarg(context, mapping, args[1])
494 markers = evalfuncarg(context, mapping, args[1])
495
495
496 try:
496 try:
497 return obsutil.obsfateverb(successors, markers)
497 return obsutil.obsfateverb(successors, markers)
498 except TypeError:
498 except TypeError:
499 # i18n: "obsfateverb" is a keyword
499 # i18n: "obsfateverb" is a keyword
500 errmsg = _("obsfateverb first argument should be countable")
500 errmsg = _("obsfateverb first argument should be countable")
501 raise error.ParseError(errmsg)
501 raise error.ParseError(errmsg)
502
502
503 @templatefunc('relpath(path)')
503 @templatefunc('relpath(path)')
504 def relpath(context, mapping, args):
504 def relpath(context, mapping, args):
505 """Convert a repository-absolute path into a filesystem path relative to
505 """Convert a repository-absolute path into a filesystem path relative to
506 the current working directory."""
506 the current working directory."""
507 if len(args) != 1:
507 if len(args) != 1:
508 # i18n: "relpath" is a keyword
508 # i18n: "relpath" is a keyword
509 raise error.ParseError(_("relpath expects one argument"))
509 raise error.ParseError(_("relpath expects one argument"))
510
510
511 repo = context.resource(mapping, 'ctx').repo()
511 repo = context.resource(mapping, 'ctx').repo()
512 path = evalstring(context, mapping, args[0])
512 path = evalstring(context, mapping, args[0])
513 return repo.pathto(path)
513 return repo.pathto(path)
514
514
515 @templatefunc('revset(query[, formatargs...])')
515 @templatefunc('revset(query[, formatargs...])')
516 def revset(context, mapping, args):
516 def revset(context, mapping, args):
517 """Execute a revision set query. See
517 """Execute a revision set query. See
518 :hg:`help revset`."""
518 :hg:`help revset`."""
519 if not len(args) > 0:
519 if not len(args) > 0:
520 # i18n: "revset" is a keyword
520 # i18n: "revset" is a keyword
521 raise error.ParseError(_("revset expects one or more arguments"))
521 raise error.ParseError(_("revset expects one or more arguments"))
522
522
523 raw = evalstring(context, mapping, args[0])
523 raw = evalstring(context, mapping, args[0])
524 ctx = context.resource(mapping, 'ctx')
524 ctx = context.resource(mapping, 'ctx')
525 repo = ctx.repo()
525 repo = ctx.repo()
526
526
527 def query(expr):
527 def query(expr):
528 m = revsetmod.match(repo.ui, expr, lookup=revsetmod.lookupfn(repo))
528 m = revsetmod.match(repo.ui, expr, lookup=revsetmod.lookupfn(repo))
529 return m(repo)
529 return m(repo)
530
530
531 if len(args) > 1:
531 if len(args) > 1:
532 formatargs = [evalfuncarg(context, mapping, a) for a in args[1:]]
532 formatargs = [evalfuncarg(context, mapping, a) for a in args[1:]]
533 revs = query(revsetlang.formatspec(raw, *formatargs))
533 revs = query(revsetlang.formatspec(raw, *formatargs))
534 revs = list(revs)
534 revs = list(revs)
535 else:
535 else:
536 cache = context.resource(mapping, 'cache')
536 cache = context.resource(mapping, 'cache')
537 revsetcache = cache.setdefault("revsetcache", {})
537 revsetcache = cache.setdefault("revsetcache", {})
538 if raw in revsetcache:
538 if raw in revsetcache:
539 revs = revsetcache[raw]
539 revs = revsetcache[raw]
540 else:
540 else:
541 revs = query(raw)
541 revs = query(raw)
542 revs = list(revs)
542 revs = list(revs)
543 revsetcache[raw] = revs
543 revsetcache[raw] = revs
544 return templatekw.showrevslist(context, mapping, "revision", revs)
544 return templatekw.showrevslist(context, mapping, "revision", revs)
545
545
546 @templatefunc('rstdoc(text, style)')
546 @templatefunc('rstdoc(text, style)')
547 def rstdoc(context, mapping, args):
547 def rstdoc(context, mapping, args):
548 """Format reStructuredText."""
548 """Format reStructuredText."""
549 if len(args) != 2:
549 if len(args) != 2:
550 # i18n: "rstdoc" is a keyword
550 # i18n: "rstdoc" is a keyword
551 raise error.ParseError(_("rstdoc expects two arguments"))
551 raise error.ParseError(_("rstdoc expects two arguments"))
552
552
553 text = evalstring(context, mapping, args[0])
553 text = evalstring(context, mapping, args[0])
554 style = evalstring(context, mapping, args[1])
554 style = evalstring(context, mapping, args[1])
555
555
556 return minirst.format(text, style=style, keep=['verbose'])
556 return minirst.format(text, style=style, keep=['verbose'])
557
557
558 @templatefunc('separate(sep, args)', argspec='sep *args')
558 @templatefunc('separate(sep, args...)', argspec='sep *args')
559 def separate(context, mapping, args):
559 def separate(context, mapping, args):
560 """Add a separator between non-empty arguments."""
560 """Add a separator between non-empty arguments."""
561 if 'sep' not in args:
561 if 'sep' not in args:
562 # i18n: "separate" is a keyword
562 # i18n: "separate" is a keyword
563 raise error.ParseError(_("separate expects at least one argument"))
563 raise error.ParseError(_("separate expects at least one argument"))
564
564
565 sep = evalstring(context, mapping, args['sep'])
565 sep = evalstring(context, mapping, args['sep'])
566 first = True
566 first = True
567 for arg in args['args']:
567 for arg in args['args']:
568 argstr = evalstring(context, mapping, arg)
568 argstr = evalstring(context, mapping, arg)
569 if not argstr:
569 if not argstr:
570 continue
570 continue
571 if first:
571 if first:
572 first = False
572 first = False
573 else:
573 else:
574 yield sep
574 yield sep
575 yield argstr
575 yield argstr
576
576
577 @templatefunc('shortest(node, minlength=4)')
577 @templatefunc('shortest(node, minlength=4)')
578 def shortest(context, mapping, args):
578 def shortest(context, mapping, args):
579 """Obtain the shortest representation of
579 """Obtain the shortest representation of
580 a node."""
580 a node."""
581 if not (1 <= len(args) <= 2):
581 if not (1 <= len(args) <= 2):
582 # i18n: "shortest" is a keyword
582 # i18n: "shortest" is a keyword
583 raise error.ParseError(_("shortest() expects one or two arguments"))
583 raise error.ParseError(_("shortest() expects one or two arguments"))
584
584
585 hexnode = evalstring(context, mapping, args[0])
585 hexnode = evalstring(context, mapping, args[0])
586
586
587 minlength = 4
587 minlength = 4
588 if len(args) > 1:
588 if len(args) > 1:
589 minlength = evalinteger(context, mapping, args[1],
589 minlength = evalinteger(context, mapping, args[1],
590 # i18n: "shortest" is a keyword
590 # i18n: "shortest" is a keyword
591 _("shortest() expects an integer minlength"))
591 _("shortest() expects an integer minlength"))
592
592
593 repo = context.resource(mapping, 'ctx')._repo
593 repo = context.resource(mapping, 'ctx')._repo
594 if len(hexnode) > 40:
594 if len(hexnode) > 40:
595 return hexnode
595 return hexnode
596 elif len(hexnode) == 40:
596 elif len(hexnode) == 40:
597 try:
597 try:
598 node = bin(hexnode)
598 node = bin(hexnode)
599 except TypeError:
599 except TypeError:
600 return hexnode
600 return hexnode
601 else:
601 else:
602 try:
602 try:
603 node = scmutil.resolvehexnodeidprefix(repo, hexnode)
603 node = scmutil.resolvehexnodeidprefix(repo, hexnode)
604 except (error.LookupError, error.WdirUnsupported):
604 except (error.LookupError, error.WdirUnsupported):
605 return hexnode
605 return hexnode
606 if not node:
606 if not node:
607 return hexnode
607 return hexnode
608 return scmutil.shortesthexnodeidprefix(repo, node, minlength)
608 return scmutil.shortesthexnodeidprefix(repo, node, minlength)
609
609
610 @templatefunc('strip(text[, chars])')
610 @templatefunc('strip(text[, chars])')
611 def strip(context, mapping, args):
611 def strip(context, mapping, args):
612 """Strip characters from a string. By default,
612 """Strip characters from a string. By default,
613 strips all leading and trailing whitespace."""
613 strips all leading and trailing whitespace."""
614 if not (1 <= len(args) <= 2):
614 if not (1 <= len(args) <= 2):
615 # i18n: "strip" is a keyword
615 # i18n: "strip" is a keyword
616 raise error.ParseError(_("strip expects one or two arguments"))
616 raise error.ParseError(_("strip expects one or two arguments"))
617
617
618 text = evalstring(context, mapping, args[0])
618 text = evalstring(context, mapping, args[0])
619 if len(args) == 2:
619 if len(args) == 2:
620 chars = evalstring(context, mapping, args[1])
620 chars = evalstring(context, mapping, args[1])
621 return text.strip(chars)
621 return text.strip(chars)
622 return text.strip()
622 return text.strip()
623
623
624 @templatefunc('sub(pattern, replacement, expression)')
624 @templatefunc('sub(pattern, replacement, expression)')
625 def sub(context, mapping, args):
625 def sub(context, mapping, args):
626 """Perform text substitution
626 """Perform text substitution
627 using regular expressions."""
627 using regular expressions."""
628 if len(args) != 3:
628 if len(args) != 3:
629 # i18n: "sub" is a keyword
629 # i18n: "sub" is a keyword
630 raise error.ParseError(_("sub expects three arguments"))
630 raise error.ParseError(_("sub expects three arguments"))
631
631
632 pat = evalstring(context, mapping, args[0])
632 pat = evalstring(context, mapping, args[0])
633 rpl = evalstring(context, mapping, args[1])
633 rpl = evalstring(context, mapping, args[1])
634 src = evalstring(context, mapping, args[2])
634 src = evalstring(context, mapping, args[2])
635 try:
635 try:
636 patre = re.compile(pat)
636 patre = re.compile(pat)
637 except re.error:
637 except re.error:
638 # i18n: "sub" is a keyword
638 # i18n: "sub" is a keyword
639 raise error.ParseError(_("sub got an invalid pattern: %s") % pat)
639 raise error.ParseError(_("sub got an invalid pattern: %s") % pat)
640 try:
640 try:
641 yield patre.sub(rpl, src)
641 yield patre.sub(rpl, src)
642 except re.error:
642 except re.error:
643 # i18n: "sub" is a keyword
643 # i18n: "sub" is a keyword
644 raise error.ParseError(_("sub got an invalid replacement: %s") % rpl)
644 raise error.ParseError(_("sub got an invalid replacement: %s") % rpl)
645
645
646 @templatefunc('startswith(pattern, text)')
646 @templatefunc('startswith(pattern, text)')
647 def startswith(context, mapping, args):
647 def startswith(context, mapping, args):
648 """Returns the value from the "text" argument
648 """Returns the value from the "text" argument
649 if it begins with the content from the "pattern" argument."""
649 if it begins with the content from the "pattern" argument."""
650 if len(args) != 2:
650 if len(args) != 2:
651 # i18n: "startswith" is a keyword
651 # i18n: "startswith" is a keyword
652 raise error.ParseError(_("startswith expects two arguments"))
652 raise error.ParseError(_("startswith expects two arguments"))
653
653
654 patn = evalstring(context, mapping, args[0])
654 patn = evalstring(context, mapping, args[0])
655 text = evalstring(context, mapping, args[1])
655 text = evalstring(context, mapping, args[1])
656 if text.startswith(patn):
656 if text.startswith(patn):
657 return text
657 return text
658 return ''
658 return ''
659
659
660 @templatefunc('word(number, text[, separator])')
660 @templatefunc('word(number, text[, separator])')
661 def word(context, mapping, args):
661 def word(context, mapping, args):
662 """Return the nth word from a string."""
662 """Return the nth word from a string."""
663 if not (2 <= len(args) <= 3):
663 if not (2 <= len(args) <= 3):
664 # i18n: "word" is a keyword
664 # i18n: "word" is a keyword
665 raise error.ParseError(_("word expects two or three arguments, got %d")
665 raise error.ParseError(_("word expects two or three arguments, got %d")
666 % len(args))
666 % len(args))
667
667
668 num = evalinteger(context, mapping, args[0],
668 num = evalinteger(context, mapping, args[0],
669 # i18n: "word" is a keyword
669 # i18n: "word" is a keyword
670 _("word expects an integer index"))
670 _("word expects an integer index"))
671 text = evalstring(context, mapping, args[1])
671 text = evalstring(context, mapping, args[1])
672 if len(args) == 3:
672 if len(args) == 3:
673 splitter = evalstring(context, mapping, args[2])
673 splitter = evalstring(context, mapping, args[2])
674 else:
674 else:
675 splitter = None
675 splitter = None
676
676
677 tokens = text.split(splitter)
677 tokens = text.split(splitter)
678 if num >= len(tokens) or num < -len(tokens):
678 if num >= len(tokens) or num < -len(tokens):
679 return ''
679 return ''
680 else:
680 else:
681 return tokens[num]
681 return tokens[num]
682
682
683 def loadfunction(ui, extname, registrarobj):
683 def loadfunction(ui, extname, registrarobj):
684 """Load template function from specified registrarobj
684 """Load template function from specified registrarobj
685 """
685 """
686 for name, func in registrarobj._table.iteritems():
686 for name, func in registrarobj._table.iteritems():
687 funcs[name] = func
687 funcs[name] = func
688
688
689 # tell hggettext to extract docstrings from these functions:
689 # tell hggettext to extract docstrings from these functions:
690 i18nfunctions = funcs.values()
690 i18nfunctions = funcs.values()
General Comments 0
You need to be logged in to leave comments. Login now