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