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