##// END OF EJS Templates
templatekw: mark _showlist() as deprecated (API)...
Yuya Nishihara -
r37089:d3f7930a default
parent child Browse files
Show More
@@ -1,798 +1,802 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 (
12 12 hex,
13 13 nullid,
14 14 )
15 15
16 16 from . import (
17 17 encoding,
18 18 error,
19 19 hbisect,
20 20 i18n,
21 21 obsutil,
22 22 patch,
23 23 pycompat,
24 24 registrar,
25 25 scmutil,
26 26 templateutil,
27 27 util,
28 28 )
29 29
30 30 _hybrid = templateutil.hybrid
31 31 _mappable = templateutil.mappable
32 32 hybriddict = templateutil.hybriddict
33 33 hybridlist = templateutil.hybridlist
34 34 compatdict = templateutil.compatdict
35 35 compatlist = templateutil.compatlist
36 36 _showcompatlist = templateutil._showcompatlist
37 37
38 38 def _showlist(name, values, templ, mapping, plural=None, separator=' '):
39 ui = mapping.get('ui')
40 if ui:
41 ui.deprecwarn("templatekw._showlist() is deprecated, use "
42 "templateutil._showcompatlist()", '4.6')
39 43 context = templ # this is actually a template context, not a templater
40 44 return _showcompatlist(context, mapping, name, values, plural, separator)
41 45
42 46 def showdict(name, data, mapping, plural=None, key='key', value='value',
43 47 fmt=None, separator=' '):
44 48 ui = mapping.get('ui')
45 49 if ui:
46 50 ui.deprecwarn("templatekw.showdict() is deprecated, use "
47 51 "templateutil.compatdict()", '4.6')
48 52 c = [{key: k, value: v} for k, v in data.iteritems()]
49 53 f = _showlist(name, c, mapping['templ'], mapping, plural, separator)
50 54 return hybriddict(data, key=key, value=value, fmt=fmt, gen=f)
51 55
52 56 def showlist(name, values, mapping, plural=None, element=None, separator=' '):
53 57 ui = mapping.get('ui')
54 58 if ui:
55 59 ui.deprecwarn("templatekw.showlist() is deprecated, use "
56 60 "templateutil.compatlist()", '4.6')
57 61 if not element:
58 62 element = name
59 63 f = _showlist(name, values, mapping['templ'], mapping, plural, separator)
60 64 return hybridlist(values, name=element, gen=f)
61 65
62 66 def getlatesttags(context, mapping, pattern=None):
63 67 '''return date, distance and name for the latest tag of rev'''
64 68 repo = context.resource(mapping, 'repo')
65 69 ctx = context.resource(mapping, 'ctx')
66 70 cache = context.resource(mapping, 'cache')
67 71
68 72 cachename = 'latesttags'
69 73 if pattern is not None:
70 74 cachename += '-' + pattern
71 75 match = util.stringmatcher(pattern)[2]
72 76 else:
73 77 match = util.always
74 78
75 79 if cachename not in cache:
76 80 # Cache mapping from rev to a tuple with tag date, tag
77 81 # distance and tag name
78 82 cache[cachename] = {-1: (0, 0, ['null'])}
79 83 latesttags = cache[cachename]
80 84
81 85 rev = ctx.rev()
82 86 todo = [rev]
83 87 while todo:
84 88 rev = todo.pop()
85 89 if rev in latesttags:
86 90 continue
87 91 ctx = repo[rev]
88 92 tags = [t for t in ctx.tags()
89 93 if (repo.tagtype(t) and repo.tagtype(t) != 'local'
90 94 and match(t))]
91 95 if tags:
92 96 latesttags[rev] = ctx.date()[0], 0, [t for t in sorted(tags)]
93 97 continue
94 98 try:
95 99 ptags = [latesttags[p.rev()] for p in ctx.parents()]
96 100 if len(ptags) > 1:
97 101 if ptags[0][2] == ptags[1][2]:
98 102 # The tuples are laid out so the right one can be found by
99 103 # comparison in this case.
100 104 pdate, pdist, ptag = max(ptags)
101 105 else:
102 106 def key(x):
103 107 changessincetag = len(repo.revs('only(%d, %s)',
104 108 ctx.rev(), x[2][0]))
105 109 # Smallest number of changes since tag wins. Date is
106 110 # used as tiebreaker.
107 111 return [-changessincetag, x[0]]
108 112 pdate, pdist, ptag = max(ptags, key=key)
109 113 else:
110 114 pdate, pdist, ptag = ptags[0]
111 115 except KeyError:
112 116 # Cache miss - recurse
113 117 todo.append(rev)
114 118 todo.extend(p.rev() for p in ctx.parents())
115 119 continue
116 120 latesttags[rev] = pdate, pdist + 1, ptag
117 121 return latesttags[rev]
118 122
119 123 def getrenamedfn(repo, endrev=None):
120 124 rcache = {}
121 125 if endrev is None:
122 126 endrev = len(repo)
123 127
124 128 def getrenamed(fn, rev):
125 129 '''looks up all renames for a file (up to endrev) the first
126 130 time the file is given. It indexes on the changerev and only
127 131 parses the manifest if linkrev != changerev.
128 132 Returns rename info for fn at changerev rev.'''
129 133 if fn not in rcache:
130 134 rcache[fn] = {}
131 135 fl = repo.file(fn)
132 136 for i in fl:
133 137 lr = fl.linkrev(i)
134 138 renamed = fl.renamed(fl.node(i))
135 139 rcache[fn][lr] = renamed
136 140 if lr >= endrev:
137 141 break
138 142 if rev in rcache[fn]:
139 143 return rcache[fn][rev]
140 144
141 145 # If linkrev != rev (i.e. rev not found in rcache) fallback to
142 146 # filectx logic.
143 147 try:
144 148 return repo[rev][fn].renamed()
145 149 except error.LookupError:
146 150 return None
147 151
148 152 return getrenamed
149 153
150 154 def getlogcolumns():
151 155 """Return a dict of log column labels"""
152 156 _ = pycompat.identity # temporarily disable gettext
153 157 # i18n: column positioning for "hg log"
154 158 columns = _('bookmark: %s\n'
155 159 'branch: %s\n'
156 160 'changeset: %s\n'
157 161 'copies: %s\n'
158 162 'date: %s\n'
159 163 'extra: %s=%s\n'
160 164 'files+: %s\n'
161 165 'files-: %s\n'
162 166 'files: %s\n'
163 167 'instability: %s\n'
164 168 'manifest: %s\n'
165 169 'obsolete: %s\n'
166 170 'parent: %s\n'
167 171 'phase: %s\n'
168 172 'summary: %s\n'
169 173 'tag: %s\n'
170 174 'user: %s\n')
171 175 return dict(zip([s.split(':', 1)[0] for s in columns.splitlines()],
172 176 i18n._(columns).splitlines(True)))
173 177
174 178 # default templates internally used for rendering of lists
175 179 defaulttempl = {
176 180 'parent': '{rev}:{node|formatnode} ',
177 181 'manifest': '{rev}:{node|formatnode}',
178 182 'file_copy': '{name} ({source})',
179 183 'envvar': '{key}={value}',
180 184 'extra': '{key}={value|stringescape}'
181 185 }
182 186 # filecopy is preserved for compatibility reasons
183 187 defaulttempl['filecopy'] = defaulttempl['file_copy']
184 188
185 189 # keywords are callables (see registrar.templatekeyword for details)
186 190 keywords = {}
187 191 templatekeyword = registrar.templatekeyword(keywords)
188 192
189 193 @templatekeyword('author', requires={'ctx'})
190 194 def showauthor(context, mapping):
191 195 """String. The unmodified author of the changeset."""
192 196 ctx = context.resource(mapping, 'ctx')
193 197 return ctx.user()
194 198
195 199 @templatekeyword('bisect', requires={'repo', 'ctx'})
196 200 def showbisect(context, mapping):
197 201 """String. The changeset bisection status."""
198 202 repo = context.resource(mapping, 'repo')
199 203 ctx = context.resource(mapping, 'ctx')
200 204 return hbisect.label(repo, ctx.node())
201 205
202 206 @templatekeyword('branch', requires={'ctx'})
203 207 def showbranch(context, mapping):
204 208 """String. The name of the branch on which the changeset was
205 209 committed.
206 210 """
207 211 ctx = context.resource(mapping, 'ctx')
208 212 return ctx.branch()
209 213
210 214 @templatekeyword('branches', requires={'ctx'})
211 215 def showbranches(context, mapping):
212 216 """List of strings. The name of the branch on which the
213 217 changeset was committed. Will be empty if the branch name was
214 218 default. (DEPRECATED)
215 219 """
216 220 ctx = context.resource(mapping, 'ctx')
217 221 branch = ctx.branch()
218 222 if branch != 'default':
219 223 return compatlist(context, mapping, 'branch', [branch],
220 224 plural='branches')
221 225 return compatlist(context, mapping, 'branch', [], plural='branches')
222 226
223 227 @templatekeyword('bookmarks', requires={'repo', 'ctx'})
224 228 def showbookmarks(context, mapping):
225 229 """List of strings. Any bookmarks associated with the
226 230 changeset. Also sets 'active', the name of the active bookmark.
227 231 """
228 232 repo = context.resource(mapping, 'repo')
229 233 ctx = context.resource(mapping, 'ctx')
230 234 bookmarks = ctx.bookmarks()
231 235 active = repo._activebookmark
232 236 makemap = lambda v: {'bookmark': v, 'active': active, 'current': active}
233 237 f = _showcompatlist(context, mapping, 'bookmark', bookmarks)
234 238 return _hybrid(f, bookmarks, makemap, pycompat.identity)
235 239
236 240 @templatekeyword('children', requires={'ctx'})
237 241 def showchildren(context, mapping):
238 242 """List of strings. The children of the changeset."""
239 243 ctx = context.resource(mapping, 'ctx')
240 244 childrevs = ['%d:%s' % (cctx.rev(), cctx) for cctx in ctx.children()]
241 245 return compatlist(context, mapping, 'children', childrevs, element='child')
242 246
243 247 # Deprecated, but kept alive for help generation a purpose.
244 248 @templatekeyword('currentbookmark', requires={'repo', 'ctx'})
245 249 def showcurrentbookmark(context, mapping):
246 250 """String. The active bookmark, if it is associated with the changeset.
247 251 (DEPRECATED)"""
248 252 return showactivebookmark(context, mapping)
249 253
250 254 @templatekeyword('activebookmark', requires={'repo', 'ctx'})
251 255 def showactivebookmark(context, mapping):
252 256 """String. The active bookmark, if it is associated with the changeset."""
253 257 repo = context.resource(mapping, 'repo')
254 258 ctx = context.resource(mapping, 'ctx')
255 259 active = repo._activebookmark
256 260 if active and active in ctx.bookmarks():
257 261 return active
258 262 return ''
259 263
260 264 @templatekeyword('date', requires={'ctx'})
261 265 def showdate(context, mapping):
262 266 """Date information. The date when the changeset was committed."""
263 267 ctx = context.resource(mapping, 'ctx')
264 268 return ctx.date()
265 269
266 270 @templatekeyword('desc', requires={'ctx'})
267 271 def showdescription(context, mapping):
268 272 """String. The text of the changeset description."""
269 273 ctx = context.resource(mapping, 'ctx')
270 274 s = ctx.description()
271 275 if isinstance(s, encoding.localstr):
272 276 # try hard to preserve utf-8 bytes
273 277 return encoding.tolocal(encoding.fromlocal(s).strip())
274 278 else:
275 279 return s.strip()
276 280
277 281 @templatekeyword('diffstat', requires={'ctx'})
278 282 def showdiffstat(context, mapping):
279 283 """String. Statistics of changes with the following format:
280 284 "modified files: +added/-removed lines"
281 285 """
282 286 ctx = context.resource(mapping, 'ctx')
283 287 stats = patch.diffstatdata(util.iterlines(ctx.diff(noprefix=False)))
284 288 maxname, maxtotal, adds, removes, binary = patch.diffstatsum(stats)
285 289 return '%d: +%d/-%d' % (len(stats), adds, removes)
286 290
287 291 @templatekeyword('envvars', requires={'ui'})
288 292 def showenvvars(context, mapping):
289 293 """A dictionary of environment variables. (EXPERIMENTAL)"""
290 294 ui = context.resource(mapping, 'ui')
291 295 env = ui.exportableenviron()
292 296 env = util.sortdict((k, env[k]) for k in sorted(env))
293 297 return compatdict(context, mapping, 'envvar', env, plural='envvars')
294 298
295 299 @templatekeyword('extras', requires={'ctx'})
296 300 def showextras(context, mapping):
297 301 """List of dicts with key, value entries of the 'extras'
298 302 field of this changeset."""
299 303 ctx = context.resource(mapping, 'ctx')
300 304 extras = ctx.extra()
301 305 extras = util.sortdict((k, extras[k]) for k in sorted(extras))
302 306 makemap = lambda k: {'key': k, 'value': extras[k]}
303 307 c = [makemap(k) for k in extras]
304 308 f = _showcompatlist(context, mapping, 'extra', c, plural='extras')
305 309 return _hybrid(f, extras, makemap,
306 310 lambda k: '%s=%s' % (k, util.escapestr(extras[k])))
307 311
308 312 def _showfilesbystat(context, mapping, name, index):
309 313 repo = context.resource(mapping, 'repo')
310 314 ctx = context.resource(mapping, 'ctx')
311 315 revcache = context.resource(mapping, 'revcache')
312 316 if 'files' not in revcache:
313 317 revcache['files'] = repo.status(ctx.p1(), ctx)[:3]
314 318 files = revcache['files'][index]
315 319 return compatlist(context, mapping, name, files, element='file')
316 320
317 321 @templatekeyword('file_adds', requires={'repo', 'ctx', 'revcache'})
318 322 def showfileadds(context, mapping):
319 323 """List of strings. Files added by this changeset."""
320 324 return _showfilesbystat(context, mapping, 'file_add', 1)
321 325
322 326 @templatekeyword('file_copies',
323 327 requires={'repo', 'ctx', 'cache', 'revcache'})
324 328 def showfilecopies(context, mapping):
325 329 """List of strings. Files copied in this changeset with
326 330 their sources.
327 331 """
328 332 repo = context.resource(mapping, 'repo')
329 333 ctx = context.resource(mapping, 'ctx')
330 334 cache = context.resource(mapping, 'cache')
331 335 copies = context.resource(mapping, 'revcache').get('copies')
332 336 if copies is None:
333 337 if 'getrenamed' not in cache:
334 338 cache['getrenamed'] = getrenamedfn(repo)
335 339 copies = []
336 340 getrenamed = cache['getrenamed']
337 341 for fn in ctx.files():
338 342 rename = getrenamed(fn, ctx.rev())
339 343 if rename:
340 344 copies.append((fn, rename[0]))
341 345
342 346 copies = util.sortdict(copies)
343 347 return compatdict(context, mapping, 'file_copy', copies,
344 348 key='name', value='source', fmt='%s (%s)',
345 349 plural='file_copies')
346 350
347 351 # showfilecopiesswitch() displays file copies only if copy records are
348 352 # provided before calling the templater, usually with a --copies
349 353 # command line switch.
350 354 @templatekeyword('file_copies_switch', requires={'revcache'})
351 355 def showfilecopiesswitch(context, mapping):
352 356 """List of strings. Like "file_copies" but displayed
353 357 only if the --copied switch is set.
354 358 """
355 359 copies = context.resource(mapping, 'revcache').get('copies') or []
356 360 copies = util.sortdict(copies)
357 361 return compatdict(context, mapping, 'file_copy', copies,
358 362 key='name', value='source', fmt='%s (%s)',
359 363 plural='file_copies')
360 364
361 365 @templatekeyword('file_dels', requires={'repo', 'ctx', 'revcache'})
362 366 def showfiledels(context, mapping):
363 367 """List of strings. Files removed by this changeset."""
364 368 return _showfilesbystat(context, mapping, 'file_del', 2)
365 369
366 370 @templatekeyword('file_mods', requires={'repo', 'ctx', 'revcache'})
367 371 def showfilemods(context, mapping):
368 372 """List of strings. Files modified by this changeset."""
369 373 return _showfilesbystat(context, mapping, 'file_mod', 0)
370 374
371 375 @templatekeyword('files', requires={'ctx'})
372 376 def showfiles(context, mapping):
373 377 """List of strings. All files modified, added, or removed by this
374 378 changeset.
375 379 """
376 380 ctx = context.resource(mapping, 'ctx')
377 381 return compatlist(context, mapping, 'file', ctx.files())
378 382
379 383 @templatekeyword('graphnode', requires={'repo', 'ctx'})
380 384 def showgraphnode(context, mapping):
381 385 """String. The character representing the changeset node in an ASCII
382 386 revision graph."""
383 387 repo = context.resource(mapping, 'repo')
384 388 ctx = context.resource(mapping, 'ctx')
385 389 return getgraphnode(repo, ctx)
386 390
387 391 def getgraphnode(repo, ctx):
388 392 wpnodes = repo.dirstate.parents()
389 393 if wpnodes[1] == nullid:
390 394 wpnodes = wpnodes[:1]
391 395 if ctx.node() in wpnodes:
392 396 return '@'
393 397 elif ctx.obsolete():
394 398 return 'x'
395 399 elif ctx.isunstable():
396 400 return '*'
397 401 elif ctx.closesbranch():
398 402 return '_'
399 403 else:
400 404 return 'o'
401 405
402 406 @templatekeyword('graphwidth', requires=())
403 407 def showgraphwidth(context, mapping):
404 408 """Integer. The width of the graph drawn by 'log --graph' or zero."""
405 409 # just hosts documentation; should be overridden by template mapping
406 410 return 0
407 411
408 412 @templatekeyword('index', requires=())
409 413 def showindex(context, mapping):
410 414 """Integer. The current iteration of the loop. (0 indexed)"""
411 415 # just hosts documentation; should be overridden by template mapping
412 416 raise error.Abort(_("can't use index in this context"))
413 417
414 418 @templatekeyword('latesttag', requires={'repo', 'ctx', 'cache'})
415 419 def showlatesttag(context, mapping):
416 420 """List of strings. The global tags on the most recent globally
417 421 tagged ancestor of this changeset. If no such tags exist, the list
418 422 consists of the single string "null".
419 423 """
420 424 return showlatesttags(context, mapping, None)
421 425
422 426 def showlatesttags(context, mapping, pattern):
423 427 """helper method for the latesttag keyword and function"""
424 428 latesttags = getlatesttags(context, mapping, pattern)
425 429
426 430 # latesttag[0] is an implementation detail for sorting csets on different
427 431 # branches in a stable manner- it is the date the tagged cset was created,
428 432 # not the date the tag was created. Therefore it isn't made visible here.
429 433 makemap = lambda v: {
430 434 'changes': _showchangessincetag,
431 435 'distance': latesttags[1],
432 436 'latesttag': v, # BC with {latesttag % '{latesttag}'}
433 437 'tag': v
434 438 }
435 439
436 440 tags = latesttags[2]
437 441 f = _showcompatlist(context, mapping, 'latesttag', tags, separator=':')
438 442 return _hybrid(f, tags, makemap, pycompat.identity)
439 443
440 444 @templatekeyword('latesttagdistance', requires={'repo', 'ctx', 'cache'})
441 445 def showlatesttagdistance(context, mapping):
442 446 """Integer. Longest path to the latest tag."""
443 447 return getlatesttags(context, mapping)[1]
444 448
445 449 @templatekeyword('changessincelatesttag', requires={'repo', 'ctx', 'cache'})
446 450 def showchangessincelatesttag(context, mapping):
447 451 """Integer. All ancestors not in the latest tag."""
448 452 mapping = mapping.copy()
449 453 mapping['tag'] = getlatesttags(context, mapping)[2][0]
450 454 return _showchangessincetag(context, mapping)
451 455
452 456 def _showchangessincetag(context, mapping):
453 457 repo = context.resource(mapping, 'repo')
454 458 ctx = context.resource(mapping, 'ctx')
455 459 offset = 0
456 460 revs = [ctx.rev()]
457 461 tag = context.symbol(mapping, 'tag')
458 462
459 463 # The only() revset doesn't currently support wdir()
460 464 if ctx.rev() is None:
461 465 offset = 1
462 466 revs = [p.rev() for p in ctx.parents()]
463 467
464 468 return len(repo.revs('only(%ld, %s)', revs, tag)) + offset
465 469
466 470 # teach templater latesttags.changes is switched to (context, mapping) API
467 471 _showchangessincetag._requires = {'repo', 'ctx'}
468 472
469 473 @templatekeyword('manifest', requires={'repo', 'ctx'})
470 474 def showmanifest(context, mapping):
471 475 repo = context.resource(mapping, 'repo')
472 476 ctx = context.resource(mapping, 'ctx')
473 477 mnode = ctx.manifestnode()
474 478 if mnode is None:
475 479 # just avoid crash, we might want to use the 'ff...' hash in future
476 480 return
477 481 mrev = repo.manifestlog._revlog.rev(mnode)
478 482 mhex = hex(mnode)
479 483 mapping = mapping.copy()
480 484 mapping.update({'rev': mrev, 'node': mhex})
481 485 f = context.process('manifest', mapping)
482 486 # TODO: perhaps 'ctx' should be dropped from mapping because manifest
483 487 # rev and node are completely different from changeset's.
484 488 return _mappable(f, None, f, lambda x: {'rev': mrev, 'node': mhex})
485 489
486 490 @templatekeyword('obsfate', requires={'ui', 'repo', 'ctx'})
487 491 def showobsfate(context, mapping):
488 492 # this function returns a list containing pre-formatted obsfate strings.
489 493 #
490 494 # This function will be replaced by templates fragments when we will have
491 495 # the verbosity templatekw available.
492 496 succsandmarkers = showsuccsandmarkers(context, mapping)
493 497
494 498 ui = context.resource(mapping, 'ui')
495 499 values = []
496 500
497 501 for x in succsandmarkers:
498 502 values.append(obsutil.obsfateprinter(x['successors'], x['markers'], ui))
499 503
500 504 return compatlist(context, mapping, "fate", values)
501 505
502 506 def shownames(context, mapping, namespace):
503 507 """helper method to generate a template keyword for a namespace"""
504 508 repo = context.resource(mapping, 'repo')
505 509 ctx = context.resource(mapping, 'ctx')
506 510 ns = repo.names[namespace]
507 511 names = ns.names(repo, ctx.node())
508 512 return compatlist(context, mapping, ns.templatename, names,
509 513 plural=namespace)
510 514
511 515 @templatekeyword('namespaces', requires={'repo', 'ctx'})
512 516 def shownamespaces(context, mapping):
513 517 """Dict of lists. Names attached to this changeset per
514 518 namespace."""
515 519 repo = context.resource(mapping, 'repo')
516 520 ctx = context.resource(mapping, 'ctx')
517 521
518 522 namespaces = util.sortdict()
519 523 def makensmapfn(ns):
520 524 # 'name' for iterating over namespaces, templatename for local reference
521 525 return lambda v: {'name': v, ns.templatename: v}
522 526
523 527 for k, ns in repo.names.iteritems():
524 528 names = ns.names(repo, ctx.node())
525 529 f = _showcompatlist(context, mapping, 'name', names)
526 530 namespaces[k] = _hybrid(f, names, makensmapfn(ns), pycompat.identity)
527 531
528 532 f = _showcompatlist(context, mapping, 'namespace', list(namespaces))
529 533
530 534 def makemap(ns):
531 535 return {
532 536 'namespace': ns,
533 537 'names': namespaces[ns],
534 538 'builtin': repo.names[ns].builtin,
535 539 'colorname': repo.names[ns].colorname,
536 540 }
537 541
538 542 return _hybrid(f, namespaces, makemap, pycompat.identity)
539 543
540 544 @templatekeyword('node', requires={'ctx'})
541 545 def shownode(context, mapping):
542 546 """String. The changeset identification hash, as a 40 hexadecimal
543 547 digit string.
544 548 """
545 549 ctx = context.resource(mapping, 'ctx')
546 550 return ctx.hex()
547 551
548 552 @templatekeyword('obsolete', requires={'ctx'})
549 553 def showobsolete(context, mapping):
550 554 """String. Whether the changeset is obsolete. (EXPERIMENTAL)"""
551 555 ctx = context.resource(mapping, 'ctx')
552 556 if ctx.obsolete():
553 557 return 'obsolete'
554 558 return ''
555 559
556 560 @templatekeyword('peerurls', requires={'repo'})
557 561 def showpeerurls(context, mapping):
558 562 """A dictionary of repository locations defined in the [paths] section
559 563 of your configuration file."""
560 564 repo = context.resource(mapping, 'repo')
561 565 # see commands.paths() for naming of dictionary keys
562 566 paths = repo.ui.paths
563 567 urls = util.sortdict((k, p.rawloc) for k, p in sorted(paths.iteritems()))
564 568 def makemap(k):
565 569 p = paths[k]
566 570 d = {'name': k, 'url': p.rawloc}
567 571 d.update((o, v) for o, v in sorted(p.suboptions.iteritems()))
568 572 return d
569 573 return _hybrid(None, urls, makemap, lambda k: '%s=%s' % (k, urls[k]))
570 574
571 575 @templatekeyword("predecessors", requires={'repo', 'ctx'})
572 576 def showpredecessors(context, mapping):
573 577 """Returns the list if the closest visible successors. (EXPERIMENTAL)"""
574 578 repo = context.resource(mapping, 'repo')
575 579 ctx = context.resource(mapping, 'ctx')
576 580 predecessors = sorted(obsutil.closestpredecessors(repo, ctx.node()))
577 581 predecessors = map(hex, predecessors)
578 582
579 583 return _hybrid(None, predecessors,
580 584 lambda x: {'ctx': repo[x], 'revcache': {}},
581 585 lambda x: scmutil.formatchangeid(repo[x]))
582 586
583 587 @templatekeyword('reporoot', requires={'repo'})
584 588 def showreporoot(context, mapping):
585 589 """String. The root directory of the current repository."""
586 590 repo = context.resource(mapping, 'repo')
587 591 return repo.root
588 592
589 593 @templatekeyword("successorssets", requires={'repo', 'ctx'})
590 594 def showsuccessorssets(context, mapping):
591 595 """Returns a string of sets of successors for a changectx. Format used
592 596 is: [ctx1, ctx2], [ctx3] if ctx has been splitted into ctx1 and ctx2
593 597 while also diverged into ctx3. (EXPERIMENTAL)"""
594 598 repo = context.resource(mapping, 'repo')
595 599 ctx = context.resource(mapping, 'ctx')
596 600 if not ctx.obsolete():
597 601 return ''
598 602
599 603 ssets = obsutil.successorssets(repo, ctx.node(), closest=True)
600 604 ssets = [[hex(n) for n in ss] for ss in ssets]
601 605
602 606 data = []
603 607 for ss in ssets:
604 608 h = _hybrid(None, ss, lambda x: {'ctx': repo[x], 'revcache': {}},
605 609 lambda x: scmutil.formatchangeid(repo[x]))
606 610 data.append(h)
607 611
608 612 # Format the successorssets
609 613 def render(d):
610 614 t = []
611 615 for i in d.gen():
612 616 t.append(i)
613 617 return "".join(t)
614 618
615 619 def gen(data):
616 620 yield "; ".join(render(d) for d in data)
617 621
618 622 return _hybrid(gen(data), data, lambda x: {'successorset': x},
619 623 pycompat.identity)
620 624
621 625 @templatekeyword("succsandmarkers", requires={'repo', 'ctx'})
622 626 def showsuccsandmarkers(context, mapping):
623 627 """Returns a list of dict for each final successor of ctx. The dict
624 628 contains successors node id in "successors" keys and the list of
625 629 obs-markers from ctx to the set of successors in "markers".
626 630 (EXPERIMENTAL)
627 631 """
628 632 repo = context.resource(mapping, 'repo')
629 633 ctx = context.resource(mapping, 'ctx')
630 634
631 635 values = obsutil.successorsandmarkers(repo, ctx)
632 636
633 637 if values is None:
634 638 values = []
635 639
636 640 # Format successors and markers to avoid exposing binary to templates
637 641 data = []
638 642 for i in values:
639 643 # Format successors
640 644 successors = i['successors']
641 645
642 646 successors = [hex(n) for n in successors]
643 647 successors = _hybrid(None, successors,
644 648 lambda x: {'ctx': repo[x], 'revcache': {}},
645 649 lambda x: scmutil.formatchangeid(repo[x]))
646 650
647 651 # Format markers
648 652 finalmarkers = []
649 653 for m in i['markers']:
650 654 hexprec = hex(m[0])
651 655 hexsucs = tuple(hex(n) for n in m[1])
652 656 hexparents = None
653 657 if m[5] is not None:
654 658 hexparents = tuple(hex(n) for n in m[5])
655 659 newmarker = (hexprec, hexsucs) + m[2:5] + (hexparents,) + m[6:]
656 660 finalmarkers.append(newmarker)
657 661
658 662 data.append({'successors': successors, 'markers': finalmarkers})
659 663
660 664 f = _showcompatlist(context, mapping, 'succsandmarkers', data)
661 665 return _hybrid(f, data, lambda x: x, pycompat.identity)
662 666
663 667 @templatekeyword('p1rev', requires={'ctx'})
664 668 def showp1rev(context, mapping):
665 669 """Integer. The repository-local revision number of the changeset's
666 670 first parent, or -1 if the changeset has no parents."""
667 671 ctx = context.resource(mapping, 'ctx')
668 672 return ctx.p1().rev()
669 673
670 674 @templatekeyword('p2rev', requires={'ctx'})
671 675 def showp2rev(context, mapping):
672 676 """Integer. The repository-local revision number of the changeset's
673 677 second parent, or -1 if the changeset has no second parent."""
674 678 ctx = context.resource(mapping, 'ctx')
675 679 return ctx.p2().rev()
676 680
677 681 @templatekeyword('p1node', requires={'ctx'})
678 682 def showp1node(context, mapping):
679 683 """String. The identification hash of the changeset's first parent,
680 684 as a 40 digit hexadecimal string. If the changeset has no parents, all
681 685 digits are 0."""
682 686 ctx = context.resource(mapping, 'ctx')
683 687 return ctx.p1().hex()
684 688
685 689 @templatekeyword('p2node', requires={'ctx'})
686 690 def showp2node(context, mapping):
687 691 """String. The identification hash of the changeset's second
688 692 parent, as a 40 digit hexadecimal string. If the changeset has no second
689 693 parent, all digits are 0."""
690 694 ctx = context.resource(mapping, 'ctx')
691 695 return ctx.p2().hex()
692 696
693 697 @templatekeyword('parents', requires={'repo', 'ctx'})
694 698 def showparents(context, mapping):
695 699 """List of strings. The parents of the changeset in "rev:node"
696 700 format. If the changeset has only one "natural" parent (the predecessor
697 701 revision) nothing is shown."""
698 702 repo = context.resource(mapping, 'repo')
699 703 ctx = context.resource(mapping, 'ctx')
700 704 pctxs = scmutil.meaningfulparents(repo, ctx)
701 705 prevs = [p.rev() for p in pctxs]
702 706 parents = [[('rev', p.rev()),
703 707 ('node', p.hex()),
704 708 ('phase', p.phasestr())]
705 709 for p in pctxs]
706 710 f = _showcompatlist(context, mapping, 'parent', parents)
707 711 return _hybrid(f, prevs, lambda x: {'ctx': repo[x], 'revcache': {}},
708 712 lambda x: scmutil.formatchangeid(repo[x]), keytype=int)
709 713
710 714 @templatekeyword('phase', requires={'ctx'})
711 715 def showphase(context, mapping):
712 716 """String. The changeset phase name."""
713 717 ctx = context.resource(mapping, 'ctx')
714 718 return ctx.phasestr()
715 719
716 720 @templatekeyword('phaseidx', requires={'ctx'})
717 721 def showphaseidx(context, mapping):
718 722 """Integer. The changeset phase index. (ADVANCED)"""
719 723 ctx = context.resource(mapping, 'ctx')
720 724 return ctx.phase()
721 725
722 726 @templatekeyword('rev', requires={'ctx'})
723 727 def showrev(context, mapping):
724 728 """Integer. The repository-local changeset revision number."""
725 729 ctx = context.resource(mapping, 'ctx')
726 730 return scmutil.intrev(ctx)
727 731
728 732 def showrevslist(context, mapping, name, revs):
729 733 """helper to generate a list of revisions in which a mapped template will
730 734 be evaluated"""
731 735 repo = context.resource(mapping, 'repo')
732 736 f = _showcompatlist(context, mapping, name, ['%d' % r for r in revs])
733 737 return _hybrid(f, revs,
734 738 lambda x: {name: x, 'ctx': repo[x], 'revcache': {}},
735 739 pycompat.identity, keytype=int)
736 740
737 741 @templatekeyword('subrepos', requires={'ctx'})
738 742 def showsubrepos(context, mapping):
739 743 """List of strings. Updated subrepositories in the changeset."""
740 744 ctx = context.resource(mapping, 'ctx')
741 745 substate = ctx.substate
742 746 if not substate:
743 747 return compatlist(context, mapping, 'subrepo', [])
744 748 psubstate = ctx.parents()[0].substate or {}
745 749 subrepos = []
746 750 for sub in substate:
747 751 if sub not in psubstate or substate[sub] != psubstate[sub]:
748 752 subrepos.append(sub) # modified or newly added in ctx
749 753 for sub in psubstate:
750 754 if sub not in substate:
751 755 subrepos.append(sub) # removed in ctx
752 756 return compatlist(context, mapping, 'subrepo', sorted(subrepos))
753 757
754 758 # don't remove "showtags" definition, even though namespaces will put
755 759 # a helper function for "tags" keyword into "keywords" map automatically,
756 760 # because online help text is built without namespaces initialization
757 761 @templatekeyword('tags', requires={'repo', 'ctx'})
758 762 def showtags(context, mapping):
759 763 """List of strings. Any tags associated with the changeset."""
760 764 return shownames(context, mapping, 'tags')
761 765
762 766 @templatekeyword('termwidth', requires={'ui'})
763 767 def showtermwidth(context, mapping):
764 768 """Integer. The width of the current terminal."""
765 769 ui = context.resource(mapping, 'ui')
766 770 return ui.termwidth()
767 771
768 772 @templatekeyword('instabilities', requires={'ctx'})
769 773 def showinstabilities(context, mapping):
770 774 """List of strings. Evolution instabilities affecting the changeset.
771 775 (EXPERIMENTAL)
772 776 """
773 777 ctx = context.resource(mapping, 'ctx')
774 778 return compatlist(context, mapping, 'instability', ctx.instabilities(),
775 779 plural='instabilities')
776 780
777 781 @templatekeyword('verbosity', requires={'ui'})
778 782 def showverbosity(context, mapping):
779 783 """String. The current output verbosity in 'debug', 'quiet', 'verbose',
780 784 or ''."""
781 785 ui = context.resource(mapping, 'ui')
782 786 # see logcmdutil.changesettemplater for priority of these flags
783 787 if ui.debugflag:
784 788 return 'debug'
785 789 elif ui.quiet:
786 790 return 'quiet'
787 791 elif ui.verbose:
788 792 return 'verbose'
789 793 return ''
790 794
791 795 def loadkeyword(ui, extname, registrarobj):
792 796 """Load template keyword from specified registrarobj
793 797 """
794 798 for name, func in registrarobj._table.iteritems():
795 799 keywords[name] = func
796 800
797 801 # tell hggettext to extract docstrings from these functions:
798 802 i18nfunctions = keywords.values()
General Comments 0
You need to be logged in to leave comments. Login now