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