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