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