##// END OF EJS Templates
templatekw: add default implementation of _hybrid.gen...
Yuya Nishihara -
r31923:68c910fa default
parent child Browse files
Show More
@@ -1,658 +1,668
1 1 # templatekw.py - common changeset template keywords
2 2 #
3 3 # Copyright 2005-2009 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 from .i18n import _
11 11 from .node import hex, nullid
12 12 from . import (
13 13 encoding,
14 14 error,
15 15 hbisect,
16 16 patch,
17 17 registrar,
18 18 scmutil,
19 19 util,
20 20 )
21 21
22 22 class _hybrid(object):
23 23 """Wrapper for list or dict to support legacy template
24 24
25 25 This class allows us to handle both:
26 26 - "{files}" (legacy command-line-specific list hack) and
27 27 - "{files % '{file}\n'}" (hgweb-style with inlining and function support)
28 28 and to access raw values:
29 29 - "{ifcontains(file, files, ...)}", "{ifcontains(key, extras, ...)}"
30 30 - "{get(extras, key)}"
31 31 - "{files|json}"
32 32 """
33 33
34 34 def __init__(self, gen, values, makemap, joinfmt):
35 self.gen = gen
35 if gen is not None:
36 self.gen = gen
36 37 self._values = values
37 38 self._makemap = makemap
38 39 self.joinfmt = joinfmt
40 @util.propertycache
41 def gen(self):
42 return self._defaultgen()
43 def _defaultgen(self):
44 """Generator to stringify this as {join(self, ' ')}"""
45 for i, d in enumerate(self.itermaps()):
46 if i > 0:
47 yield ' '
48 yield self.joinfmt(d)
39 49 def itermaps(self):
40 50 makemap = self._makemap
41 51 for x in self._values:
42 52 yield makemap(x)
43 53 def __contains__(self, x):
44 54 return x in self._values
45 55 def __len__(self):
46 56 return len(self._values)
47 57 def __iter__(self):
48 58 return iter(self._values)
49 59 def __getattr__(self, name):
50 60 if name not in ('get', 'items', 'iteritems', 'iterkeys', 'itervalues',
51 61 'keys', 'values'):
52 62 raise AttributeError(name)
53 63 return getattr(self._values, name)
54 64
55 65 def unwraphybrid(thing):
56 66 """Return an object which can be stringified possibly by using a legacy
57 67 template"""
58 68 if not util.safehasattr(thing, 'gen'):
59 69 return thing
60 70 return thing.gen
61 71
62 72 def showlist(name, values, plural=None, element=None, separator=' ', **args):
63 73 if not element:
64 74 element = name
65 75 f = _showlist(name, values, plural, separator, **args)
66 76 return _hybrid(f, values, lambda x: {element: x}, lambda d: d[element])
67 77
68 78 def _showlist(name, values, plural=None, separator=' ', **args):
69 79 '''expand set of values.
70 80 name is name of key in template map.
71 81 values is list of strings or dicts.
72 82 plural is plural of name, if not simply name + 's'.
73 83 separator is used to join values as a string
74 84
75 85 expansion works like this, given name 'foo'.
76 86
77 87 if values is empty, expand 'no_foos'.
78 88
79 89 if 'foo' not in template map, return values as a string,
80 90 joined by 'separator'.
81 91
82 92 expand 'start_foos'.
83 93
84 94 for each value, expand 'foo'. if 'last_foo' in template
85 95 map, expand it instead of 'foo' for last key.
86 96
87 97 expand 'end_foos'.
88 98 '''
89 99 templ = args['templ']
90 100 if plural:
91 101 names = plural
92 102 else: names = name + 's'
93 103 if not values:
94 104 noname = 'no_' + names
95 105 if noname in templ:
96 106 yield templ(noname, **args)
97 107 return
98 108 if name not in templ:
99 109 if isinstance(values[0], str):
100 110 yield separator.join(values)
101 111 else:
102 112 for v in values:
103 113 yield dict(v, **args)
104 114 return
105 115 startname = 'start_' + names
106 116 if startname in templ:
107 117 yield templ(startname, **args)
108 118 vargs = args.copy()
109 119 def one(v, tag=name):
110 120 try:
111 121 vargs.update(v)
112 122 except (AttributeError, ValueError):
113 123 try:
114 124 for a, b in v:
115 125 vargs[a] = b
116 126 except ValueError:
117 127 vargs[name] = v
118 128 return templ(tag, **vargs)
119 129 lastname = 'last_' + name
120 130 if lastname in templ:
121 131 last = values.pop()
122 132 else:
123 133 last = None
124 134 for v in values:
125 135 yield one(v)
126 136 if last is not None:
127 137 yield one(last, tag=lastname)
128 138 endname = 'end_' + names
129 139 if endname in templ:
130 140 yield templ(endname, **args)
131 141
132 142 def _formatrevnode(ctx):
133 143 """Format changeset as '{rev}:{node|formatnode}', which is the default
134 144 template provided by cmdutil.changeset_templater"""
135 145 repo = ctx.repo()
136 146 if repo.ui.debugflag:
137 147 hexnode = ctx.hex()
138 148 else:
139 149 hexnode = ctx.hex()[:12]
140 150 return '%d:%s' % (scmutil.intrev(ctx.rev()), hexnode)
141 151
142 152 def getfiles(repo, ctx, revcache):
143 153 if 'files' not in revcache:
144 154 revcache['files'] = repo.status(ctx.p1(), ctx)[:3]
145 155 return revcache['files']
146 156
147 157 def getlatesttags(repo, ctx, cache, pattern=None):
148 158 '''return date, distance and name for the latest tag of rev'''
149 159
150 160 cachename = 'latesttags'
151 161 if pattern is not None:
152 162 cachename += '-' + pattern
153 163 match = util.stringmatcher(pattern)[2]
154 164 else:
155 165 match = util.always
156 166
157 167 if cachename not in cache:
158 168 # Cache mapping from rev to a tuple with tag date, tag
159 169 # distance and tag name
160 170 cache[cachename] = {-1: (0, 0, ['null'])}
161 171 latesttags = cache[cachename]
162 172
163 173 rev = ctx.rev()
164 174 todo = [rev]
165 175 while todo:
166 176 rev = todo.pop()
167 177 if rev in latesttags:
168 178 continue
169 179 ctx = repo[rev]
170 180 tags = [t for t in ctx.tags()
171 181 if (repo.tagtype(t) and repo.tagtype(t) != 'local'
172 182 and match(t))]
173 183 if tags:
174 184 latesttags[rev] = ctx.date()[0], 0, [t for t in sorted(tags)]
175 185 continue
176 186 try:
177 187 # The tuples are laid out so the right one can be found by
178 188 # comparison.
179 189 pdate, pdist, ptag = max(
180 190 latesttags[p.rev()] for p in ctx.parents())
181 191 except KeyError:
182 192 # Cache miss - recurse
183 193 todo.append(rev)
184 194 todo.extend(p.rev() for p in ctx.parents())
185 195 continue
186 196 latesttags[rev] = pdate, pdist + 1, ptag
187 197 return latesttags[rev]
188 198
189 199 def getrenamedfn(repo, endrev=None):
190 200 rcache = {}
191 201 if endrev is None:
192 202 endrev = len(repo)
193 203
194 204 def getrenamed(fn, rev):
195 205 '''looks up all renames for a file (up to endrev) the first
196 206 time the file is given. It indexes on the changerev and only
197 207 parses the manifest if linkrev != changerev.
198 208 Returns rename info for fn at changerev rev.'''
199 209 if fn not in rcache:
200 210 rcache[fn] = {}
201 211 fl = repo.file(fn)
202 212 for i in fl:
203 213 lr = fl.linkrev(i)
204 214 renamed = fl.renamed(fl.node(i))
205 215 rcache[fn][lr] = renamed
206 216 if lr >= endrev:
207 217 break
208 218 if rev in rcache[fn]:
209 219 return rcache[fn][rev]
210 220
211 221 # If linkrev != rev (i.e. rev not found in rcache) fallback to
212 222 # filectx logic.
213 223 try:
214 224 return repo[rev][fn].renamed()
215 225 except error.LookupError:
216 226 return None
217 227
218 228 return getrenamed
219 229
220 230 # default templates internally used for rendering of lists
221 231 defaulttempl = {
222 232 'parent': '{rev}:{node|formatnode} ',
223 233 'manifest': '{rev}:{node|formatnode}',
224 234 'file_copy': '{name} ({source})',
225 235 'envvar': '{key}={value}',
226 236 'extra': '{key}={value|stringescape}'
227 237 }
228 238 # filecopy is preserved for compatibility reasons
229 239 defaulttempl['filecopy'] = defaulttempl['file_copy']
230 240
231 241 # keywords are callables like:
232 242 # fn(repo, ctx, templ, cache, revcache, **args)
233 243 # with:
234 244 # repo - current repository instance
235 245 # ctx - the changectx being displayed
236 246 # templ - the templater instance
237 247 # cache - a cache dictionary for the whole templater run
238 248 # revcache - a cache dictionary for the current revision
239 249 keywords = {}
240 250
241 251 templatekeyword = registrar.templatekeyword(keywords)
242 252
243 253 @templatekeyword('author')
244 254 def showauthor(repo, ctx, templ, **args):
245 255 """String. The unmodified author of the changeset."""
246 256 return ctx.user()
247 257
248 258 @templatekeyword('bisect')
249 259 def showbisect(repo, ctx, templ, **args):
250 260 """String. The changeset bisection status."""
251 261 return hbisect.label(repo, ctx.node())
252 262
253 263 @templatekeyword('branch')
254 264 def showbranch(**args):
255 265 """String. The name of the branch on which the changeset was
256 266 committed.
257 267 """
258 268 return args['ctx'].branch()
259 269
260 270 @templatekeyword('branches')
261 271 def showbranches(**args):
262 272 """List of strings. The name of the branch on which the
263 273 changeset was committed. Will be empty if the branch name was
264 274 default. (DEPRECATED)
265 275 """
266 276 branch = args['ctx'].branch()
267 277 if branch != 'default':
268 278 return showlist('branch', [branch], plural='branches', **args)
269 279 return showlist('branch', [], plural='branches', **args)
270 280
271 281 @templatekeyword('bookmarks')
272 282 def showbookmarks(**args):
273 283 """List of strings. Any bookmarks associated with the
274 284 changeset. Also sets 'active', the name of the active bookmark.
275 285 """
276 286 repo = args['ctx']._repo
277 287 bookmarks = args['ctx'].bookmarks()
278 288 active = repo._activebookmark
279 289 makemap = lambda v: {'bookmark': v, 'active': active, 'current': active}
280 290 f = _showlist('bookmark', bookmarks, **args)
281 291 return _hybrid(f, bookmarks, makemap, lambda x: x['bookmark'])
282 292
283 293 @templatekeyword('children')
284 294 def showchildren(**args):
285 295 """List of strings. The children of the changeset."""
286 296 ctx = args['ctx']
287 297 childrevs = ['%d:%s' % (cctx, cctx) for cctx in ctx.children()]
288 298 return showlist('children', childrevs, element='child', **args)
289 299
290 300 # Deprecated, but kept alive for help generation a purpose.
291 301 @templatekeyword('currentbookmark')
292 302 def showcurrentbookmark(**args):
293 303 """String. The active bookmark, if it is
294 304 associated with the changeset (DEPRECATED)"""
295 305 return showactivebookmark(**args)
296 306
297 307 @templatekeyword('activebookmark')
298 308 def showactivebookmark(**args):
299 309 """String. The active bookmark, if it is
300 310 associated with the changeset"""
301 311 active = args['repo']._activebookmark
302 312 if active and active in args['ctx'].bookmarks():
303 313 return active
304 314 return ''
305 315
306 316 @templatekeyword('date')
307 317 def showdate(repo, ctx, templ, **args):
308 318 """Date information. The date when the changeset was committed."""
309 319 return ctx.date()
310 320
311 321 @templatekeyword('desc')
312 322 def showdescription(repo, ctx, templ, **args):
313 323 """String. The text of the changeset description."""
314 324 s = ctx.description()
315 325 if isinstance(s, encoding.localstr):
316 326 # try hard to preserve utf-8 bytes
317 327 return encoding.tolocal(encoding.fromlocal(s).strip())
318 328 else:
319 329 return s.strip()
320 330
321 331 @templatekeyword('diffstat')
322 332 def showdiffstat(repo, ctx, templ, **args):
323 333 """String. Statistics of changes with the following format:
324 334 "modified files: +added/-removed lines"
325 335 """
326 336 stats = patch.diffstatdata(util.iterlines(ctx.diff(noprefix=False)))
327 337 maxname, maxtotal, adds, removes, binary = patch.diffstatsum(stats)
328 338 return '%s: +%s/-%s' % (len(stats), adds, removes)
329 339
330 340 @templatekeyword('envvars')
331 341 def showenvvars(repo, **args):
332 342 """A dictionary of environment variables. (EXPERIMENTAL)"""
333 343
334 344 env = repo.ui.exportableenviron()
335 345 env = util.sortdict((k, env[k]) for k in sorted(env))
336 346 makemap = lambda k: {'key': k, 'value': env[k]}
337 347 c = [makemap(k) for k in env]
338 348 f = _showlist('envvar', c, plural='envvars', **args)
339 349 return _hybrid(f, env, makemap,
340 350 lambda x: '%s=%s' % (x['key'], x['value']))
341 351
342 352 @templatekeyword('extras')
343 353 def showextras(**args):
344 354 """List of dicts with key, value entries of the 'extras'
345 355 field of this changeset."""
346 356 extras = args['ctx'].extra()
347 357 extras = util.sortdict((k, extras[k]) for k in sorted(extras))
348 358 makemap = lambda k: {'key': k, 'value': extras[k]}
349 359 c = [makemap(k) for k in extras]
350 360 f = _showlist('extra', c, plural='extras', **args)
351 361 return _hybrid(f, extras, makemap,
352 362 lambda x: '%s=%s' % (x['key'], util.escapestr(x['value'])))
353 363
354 364 @templatekeyword('file_adds')
355 365 def showfileadds(**args):
356 366 """List of strings. Files added by this changeset."""
357 367 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
358 368 return showlist('file_add', getfiles(repo, ctx, revcache)[1],
359 369 element='file', **args)
360 370
361 371 @templatekeyword('file_copies')
362 372 def showfilecopies(**args):
363 373 """List of strings. Files copied in this changeset with
364 374 their sources.
365 375 """
366 376 cache, ctx = args['cache'], args['ctx']
367 377 copies = args['revcache'].get('copies')
368 378 if copies is None:
369 379 if 'getrenamed' not in cache:
370 380 cache['getrenamed'] = getrenamedfn(args['repo'])
371 381 copies = []
372 382 getrenamed = cache['getrenamed']
373 383 for fn in ctx.files():
374 384 rename = getrenamed(fn, ctx.rev())
375 385 if rename:
376 386 copies.append((fn, rename[0]))
377 387
378 388 copies = util.sortdict(copies)
379 389 makemap = lambda k: {'name': k, 'source': copies[k]}
380 390 c = [makemap(k) for k in copies]
381 391 f = _showlist('file_copy', c, plural='file_copies', **args)
382 392 return _hybrid(f, copies, makemap,
383 393 lambda x: '%s (%s)' % (x['name'], x['source']))
384 394
385 395 # showfilecopiesswitch() displays file copies only if copy records are
386 396 # provided before calling the templater, usually with a --copies
387 397 # command line switch.
388 398 @templatekeyword('file_copies_switch')
389 399 def showfilecopiesswitch(**args):
390 400 """List of strings. Like "file_copies" but displayed
391 401 only if the --copied switch is set.
392 402 """
393 403 copies = args['revcache'].get('copies') or []
394 404 copies = util.sortdict(copies)
395 405 makemap = lambda k: {'name': k, 'source': copies[k]}
396 406 c = [makemap(k) for k in copies]
397 407 f = _showlist('file_copy', c, plural='file_copies', **args)
398 408 return _hybrid(f, copies, makemap,
399 409 lambda x: '%s (%s)' % (x['name'], x['source']))
400 410
401 411 @templatekeyword('file_dels')
402 412 def showfiledels(**args):
403 413 """List of strings. Files removed by this changeset."""
404 414 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
405 415 return showlist('file_del', getfiles(repo, ctx, revcache)[2],
406 416 element='file', **args)
407 417
408 418 @templatekeyword('file_mods')
409 419 def showfilemods(**args):
410 420 """List of strings. Files modified by this changeset."""
411 421 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
412 422 return showlist('file_mod', getfiles(repo, ctx, revcache)[0],
413 423 element='file', **args)
414 424
415 425 @templatekeyword('files')
416 426 def showfiles(**args):
417 427 """List of strings. All files modified, added, or removed by this
418 428 changeset.
419 429 """
420 430 return showlist('file', args['ctx'].files(), **args)
421 431
422 432 @templatekeyword('graphnode')
423 433 def showgraphnode(repo, ctx, **args):
424 434 """String. The character representing the changeset node in
425 435 an ASCII revision graph"""
426 436 wpnodes = repo.dirstate.parents()
427 437 if wpnodes[1] == nullid:
428 438 wpnodes = wpnodes[:1]
429 439 if ctx.node() in wpnodes:
430 440 return '@'
431 441 elif ctx.obsolete():
432 442 return 'x'
433 443 elif ctx.closesbranch():
434 444 return '_'
435 445 else:
436 446 return 'o'
437 447
438 448 @templatekeyword('index')
439 449 def showindex(**args):
440 450 """Integer. The current iteration of the loop. (0 indexed)"""
441 451 # just hosts documentation; should be overridden by template mapping
442 452 raise error.Abort(_("can't use index in this context"))
443 453
444 454 @templatekeyword('latesttag')
445 455 def showlatesttag(**args):
446 456 """List of strings. The global tags on the most recent globally
447 457 tagged ancestor of this changeset. If no such tags exist, the list
448 458 consists of the single string "null".
449 459 """
450 460 return showlatesttags(None, **args)
451 461
452 462 def showlatesttags(pattern, **args):
453 463 """helper method for the latesttag keyword and function"""
454 464 repo, ctx = args['repo'], args['ctx']
455 465 cache = args['cache']
456 466 latesttags = getlatesttags(repo, ctx, cache, pattern)
457 467
458 468 # latesttag[0] is an implementation detail for sorting csets on different
459 469 # branches in a stable manner- it is the date the tagged cset was created,
460 470 # not the date the tag was created. Therefore it isn't made visible here.
461 471 makemap = lambda v: {
462 472 'changes': _showchangessincetag,
463 473 'distance': latesttags[1],
464 474 'latesttag': v, # BC with {latesttag % '{latesttag}'}
465 475 'tag': v
466 476 }
467 477
468 478 tags = latesttags[2]
469 479 f = _showlist('latesttag', tags, separator=':', **args)
470 480 return _hybrid(f, tags, makemap, lambda x: x['latesttag'])
471 481
472 482 @templatekeyword('latesttagdistance')
473 483 def showlatesttagdistance(repo, ctx, templ, cache, **args):
474 484 """Integer. Longest path to the latest tag."""
475 485 return getlatesttags(repo, ctx, cache)[1]
476 486
477 487 @templatekeyword('changessincelatesttag')
478 488 def showchangessincelatesttag(repo, ctx, templ, cache, **args):
479 489 """Integer. All ancestors not in the latest tag."""
480 490 latesttag = getlatesttags(repo, ctx, cache)[2][0]
481 491
482 492 return _showchangessincetag(repo, ctx, tag=latesttag, **args)
483 493
484 494 def _showchangessincetag(repo, ctx, **args):
485 495 offset = 0
486 496 revs = [ctx.rev()]
487 497 tag = args['tag']
488 498
489 499 # The only() revset doesn't currently support wdir()
490 500 if ctx.rev() is None:
491 501 offset = 1
492 502 revs = [p.rev() for p in ctx.parents()]
493 503
494 504 return len(repo.revs('only(%ld, %s)', revs, tag)) + offset
495 505
496 506 @templatekeyword('manifest')
497 507 def showmanifest(**args):
498 508 repo, ctx, templ = args['repo'], args['ctx'], args['templ']
499 509 mnode = ctx.manifestnode()
500 510 if mnode is None:
501 511 # just avoid crash, we might want to use the 'ff...' hash in future
502 512 return
503 513 args = args.copy()
504 514 args.update({'rev': repo.manifestlog._revlog.rev(mnode),
505 515 'node': hex(mnode)})
506 516 return templ('manifest', **args)
507 517
508 518 def shownames(namespace, **args):
509 519 """helper method to generate a template keyword for a namespace"""
510 520 ctx = args['ctx']
511 521 repo = ctx.repo()
512 522 ns = repo.names[namespace]
513 523 names = ns.names(repo, ctx.node())
514 524 return showlist(ns.templatename, names, plural=namespace, **args)
515 525
516 526 @templatekeyword('namespaces')
517 527 def shownamespaces(**args):
518 528 """Dict of lists. Names attached to this changeset per
519 529 namespace."""
520 530 ctx = args['ctx']
521 531 repo = ctx.repo()
522 532 namespaces = util.sortdict((k, showlist('name', ns.names(repo, ctx.node()),
523 533 **args))
524 534 for k, ns in repo.names.iteritems())
525 535 f = _showlist('namespace', list(namespaces), **args)
526 536 return _hybrid(f, namespaces,
527 537 lambda k: {'namespace': k, 'names': namespaces[k]},
528 538 lambda x: x['namespace'])
529 539
530 540 @templatekeyword('node')
531 541 def shownode(repo, ctx, templ, **args):
532 542 """String. The changeset identification hash, as a 40 hexadecimal
533 543 digit string.
534 544 """
535 545 return ctx.hex()
536 546
537 547 @templatekeyword('obsolete')
538 548 def showobsolete(repo, ctx, templ, **args):
539 549 """String. Whether the changeset is obsolete.
540 550 """
541 551 if ctx.obsolete():
542 552 return 'obsolete'
543 553 return ''
544 554
545 555 @templatekeyword('p1rev')
546 556 def showp1rev(repo, ctx, templ, **args):
547 557 """Integer. The repository-local revision number of the changeset's
548 558 first parent, or -1 if the changeset has no parents."""
549 559 return ctx.p1().rev()
550 560
551 561 @templatekeyword('p2rev')
552 562 def showp2rev(repo, ctx, templ, **args):
553 563 """Integer. The repository-local revision number of the changeset's
554 564 second parent, or -1 if the changeset has no second parent."""
555 565 return ctx.p2().rev()
556 566
557 567 @templatekeyword('p1node')
558 568 def showp1node(repo, ctx, templ, **args):
559 569 """String. The identification hash of the changeset's first parent,
560 570 as a 40 digit hexadecimal string. If the changeset has no parents, all
561 571 digits are 0."""
562 572 return ctx.p1().hex()
563 573
564 574 @templatekeyword('p2node')
565 575 def showp2node(repo, ctx, templ, **args):
566 576 """String. The identification hash of the changeset's second
567 577 parent, as a 40 digit hexadecimal string. If the changeset has no second
568 578 parent, all digits are 0."""
569 579 return ctx.p2().hex()
570 580
571 581 @templatekeyword('parents')
572 582 def showparents(**args):
573 583 """List of strings. The parents of the changeset in "rev:node"
574 584 format. If the changeset has only one "natural" parent (the predecessor
575 585 revision) nothing is shown."""
576 586 repo = args['repo']
577 587 ctx = args['ctx']
578 588 pctxs = scmutil.meaningfulparents(repo, ctx)
579 589 prevs = [str(p.rev()) for p in pctxs] # ifcontains() needs a list of str
580 590 parents = [[('rev', p.rev()),
581 591 ('node', p.hex()),
582 592 ('phase', p.phasestr())]
583 593 for p in pctxs]
584 594 f = _showlist('parent', parents, **args)
585 595 return _hybrid(f, prevs, lambda x: {'ctx': repo[int(x)], 'revcache': {}},
586 596 lambda d: _formatrevnode(d['ctx']))
587 597
588 598 @templatekeyword('phase')
589 599 def showphase(repo, ctx, templ, **args):
590 600 """String. The changeset phase name."""
591 601 return ctx.phasestr()
592 602
593 603 @templatekeyword('phaseidx')
594 604 def showphaseidx(repo, ctx, templ, **args):
595 605 """Integer. The changeset phase index."""
596 606 return ctx.phase()
597 607
598 608 @templatekeyword('rev')
599 609 def showrev(repo, ctx, templ, **args):
600 610 """Integer. The repository-local changeset revision number."""
601 611 return scmutil.intrev(ctx.rev())
602 612
603 613 def showrevslist(name, revs, **args):
604 614 """helper to generate a list of revisions in which a mapped template will
605 615 be evaluated"""
606 616 repo = args['ctx'].repo()
607 617 revs = [str(r) for r in revs] # ifcontains() needs a list of str
608 618 f = _showlist(name, revs, **args)
609 619 return _hybrid(f, revs,
610 620 lambda x: {name: x, 'ctx': repo[int(x)], 'revcache': {}},
611 621 lambda d: d[name])
612 622
613 623 @templatekeyword('subrepos')
614 624 def showsubrepos(**args):
615 625 """List of strings. Updated subrepositories in the changeset."""
616 626 ctx = args['ctx']
617 627 substate = ctx.substate
618 628 if not substate:
619 629 return showlist('subrepo', [], **args)
620 630 psubstate = ctx.parents()[0].substate or {}
621 631 subrepos = []
622 632 for sub in substate:
623 633 if sub not in psubstate or substate[sub] != psubstate[sub]:
624 634 subrepos.append(sub) # modified or newly added in ctx
625 635 for sub in psubstate:
626 636 if sub not in substate:
627 637 subrepos.append(sub) # removed in ctx
628 638 return showlist('subrepo', sorted(subrepos), **args)
629 639
630 640 # don't remove "showtags" definition, even though namespaces will put
631 641 # a helper function for "tags" keyword into "keywords" map automatically,
632 642 # because online help text is built without namespaces initialization
633 643 @templatekeyword('tags')
634 644 def showtags(**args):
635 645 """List of strings. Any tags associated with the changeset."""
636 646 return shownames('tags', **args)
637 647
638 648 def loadkeyword(ui, extname, registrarobj):
639 649 """Load template keyword from specified registrarobj
640 650 """
641 651 for name, func in registrarobj._table.iteritems():
642 652 keywords[name] = func
643 653
644 654 @templatekeyword('termwidth')
645 655 def termwidth(repo, ctx, templ, **args):
646 656 """Integer. The width of the current terminal."""
647 657 return repo.ui.termwidth()
648 658
649 659 @templatekeyword('troubles')
650 660 def showtroubles(**args):
651 661 """List of strings. Evolution troubles affecting the changeset.
652 662
653 663 (EXPERIMENTAL)
654 664 """
655 665 return showlist('trouble', args['ctx'].troubles(), **args)
656 666
657 667 # tell hggettext to extract docstrings from these functions:
658 668 i18nfunctions = keywords.values()
General Comments 0
You need to be logged in to leave comments. Login now