##// END OF EJS Templates
templatekw: correct typo in activebookmark documentation
Matt Harbison -
r25663:edd2d20a default
parent child Browse files
Show More
@@ -1,471 +1,471
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 node import hex
9 9 import patch, util, error
10 10 import hbisect
11 11
12 12 # This helper class allows us to handle both:
13 13 # "{files}" (legacy command-line-specific list hack) and
14 14 # "{files % '{file}\n'}" (hgweb-style with inlining and function support)
15 15 # and to access raw values:
16 16 # "{ifcontains(file, files, ...)}", "{ifcontains(key, extras, ...)}"
17 17 # "{get(extras, key)}"
18 18
19 19 class _hybrid(object):
20 20 def __init__(self, gen, values, makemap, joinfmt=None):
21 21 self.gen = gen
22 22 self.values = values
23 23 self._makemap = makemap
24 24 if joinfmt:
25 25 self.joinfmt = joinfmt
26 26 else:
27 27 self.joinfmt = lambda x: x.values()[0]
28 28 def __iter__(self):
29 29 return self.gen
30 30 def __call__(self):
31 31 makemap = self._makemap
32 32 for x in self.values:
33 33 yield makemap(x)
34 34 def __contains__(self, x):
35 35 return x in self.values
36 36 def __len__(self):
37 37 return len(self.values)
38 38 def __getattr__(self, name):
39 39 if name != 'get':
40 40 raise AttributeError(name)
41 41 return getattr(self.values, name)
42 42
43 43 def showlist(name, values, plural=None, element=None, **args):
44 44 if not element:
45 45 element = name
46 46 f = _showlist(name, values, plural, **args)
47 47 return _hybrid(f, values, lambda x: {element: x})
48 48
49 49 def _showlist(name, values, plural=None, **args):
50 50 '''expand set of values.
51 51 name is name of key in template map.
52 52 values is list of strings or dicts.
53 53 plural is plural of name, if not simply name + 's'.
54 54
55 55 expansion works like this, given name 'foo'.
56 56
57 57 if values is empty, expand 'no_foos'.
58 58
59 59 if 'foo' not in template map, return values as a string,
60 60 joined by space.
61 61
62 62 expand 'start_foos'.
63 63
64 64 for each value, expand 'foo'. if 'last_foo' in template
65 65 map, expand it instead of 'foo' for last key.
66 66
67 67 expand 'end_foos'.
68 68 '''
69 69 templ = args['templ']
70 70 if plural:
71 71 names = plural
72 72 else: names = name + 's'
73 73 if not values:
74 74 noname = 'no_' + names
75 75 if noname in templ:
76 76 yield templ(noname, **args)
77 77 return
78 78 if name not in templ:
79 79 if isinstance(values[0], str):
80 80 yield ' '.join(values)
81 81 else:
82 82 for v in values:
83 83 yield dict(v, **args)
84 84 return
85 85 startname = 'start_' + names
86 86 if startname in templ:
87 87 yield templ(startname, **args)
88 88 vargs = args.copy()
89 89 def one(v, tag=name):
90 90 try:
91 91 vargs.update(v)
92 92 except (AttributeError, ValueError):
93 93 try:
94 94 for a, b in v:
95 95 vargs[a] = b
96 96 except ValueError:
97 97 vargs[name] = v
98 98 return templ(tag, **vargs)
99 99 lastname = 'last_' + name
100 100 if lastname in templ:
101 101 last = values.pop()
102 102 else:
103 103 last = None
104 104 for v in values:
105 105 yield one(v)
106 106 if last is not None:
107 107 yield one(last, tag=lastname)
108 108 endname = 'end_' + names
109 109 if endname in templ:
110 110 yield templ(endname, **args)
111 111
112 112 def getfiles(repo, ctx, revcache):
113 113 if 'files' not in revcache:
114 114 revcache['files'] = repo.status(ctx.p1(), ctx)[:3]
115 115 return revcache['files']
116 116
117 117 def getlatesttags(repo, ctx, cache):
118 118 '''return date, distance and name for the latest tag of rev'''
119 119
120 120 if 'latesttags' not in cache:
121 121 # Cache mapping from rev to a tuple with tag date, tag
122 122 # distance and tag name
123 123 cache['latesttags'] = {-1: (0, 0, 'null')}
124 124 latesttags = cache['latesttags']
125 125
126 126 rev = ctx.rev()
127 127 todo = [rev]
128 128 while todo:
129 129 rev = todo.pop()
130 130 if rev in latesttags:
131 131 continue
132 132 ctx = repo[rev]
133 133 tags = [t for t in ctx.tags()
134 134 if (repo.tagtype(t) and repo.tagtype(t) != 'local')]
135 135 if tags:
136 136 latesttags[rev] = ctx.date()[0], 0, ':'.join(sorted(tags))
137 137 continue
138 138 try:
139 139 # The tuples are laid out so the right one can be found by
140 140 # comparison.
141 141 pdate, pdist, ptag = max(
142 142 latesttags[p.rev()] for p in ctx.parents())
143 143 except KeyError:
144 144 # Cache miss - recurse
145 145 todo.append(rev)
146 146 todo.extend(p.rev() for p in ctx.parents())
147 147 continue
148 148 latesttags[rev] = pdate, pdist + 1, ptag
149 149 return latesttags[rev]
150 150
151 151 def getrenamedfn(repo, endrev=None):
152 152 rcache = {}
153 153 if endrev is None:
154 154 endrev = len(repo)
155 155
156 156 def getrenamed(fn, rev):
157 157 '''looks up all renames for a file (up to endrev) the first
158 158 time the file is given. It indexes on the changerev and only
159 159 parses the manifest if linkrev != changerev.
160 160 Returns rename info for fn at changerev rev.'''
161 161 if fn not in rcache:
162 162 rcache[fn] = {}
163 163 fl = repo.file(fn)
164 164 for i in fl:
165 165 lr = fl.linkrev(i)
166 166 renamed = fl.renamed(fl.node(i))
167 167 rcache[fn][lr] = renamed
168 168 if lr >= endrev:
169 169 break
170 170 if rev in rcache[fn]:
171 171 return rcache[fn][rev]
172 172
173 173 # If linkrev != rev (i.e. rev not found in rcache) fallback to
174 174 # filectx logic.
175 175 try:
176 176 return repo[rev][fn].renamed()
177 177 except error.LookupError:
178 178 return None
179 179
180 180 return getrenamed
181 181
182 182
183 183 def showauthor(repo, ctx, templ, **args):
184 184 """:author: String. The unmodified author of the changeset."""
185 185 return ctx.user()
186 186
187 187 def showbisect(repo, ctx, templ, **args):
188 188 """:bisect: String. The changeset bisection status."""
189 189 return hbisect.label(repo, ctx.node())
190 190
191 191 def showbranch(**args):
192 192 """:branch: String. The name of the branch on which the changeset was
193 193 committed.
194 194 """
195 195 return args['ctx'].branch()
196 196
197 197 def showbranches(**args):
198 198 """:branches: List of strings. The name of the branch on which the
199 199 changeset was committed. Will be empty if the branch name was
200 200 default.
201 201 """
202 202 branch = args['ctx'].branch()
203 203 if branch != 'default':
204 204 return showlist('branch', [branch], plural='branches', **args)
205 205 return showlist('branch', [], plural='branches', **args)
206 206
207 207 def showbookmarks(**args):
208 208 """:bookmarks: List of strings. Any bookmarks associated with the
209 209 changeset. Also sets 'active', the name of the active bookmark.
210 210 """
211 211 repo = args['ctx']._repo
212 212 bookmarks = args['ctx'].bookmarks()
213 213 active = repo._activebookmark
214 214 makemap = lambda v: {'bookmark': v, 'active': active, 'current': active}
215 215 f = _showlist('bookmark', bookmarks, **args)
216 216 return _hybrid(f, bookmarks, makemap, lambda x: x['bookmark'])
217 217
218 218 def showchildren(**args):
219 219 """:children: List of strings. The children of the changeset."""
220 220 ctx = args['ctx']
221 221 childrevs = ['%d:%s' % (cctx, cctx) for cctx in ctx.children()]
222 222 return showlist('children', childrevs, element='child', **args)
223 223
224 224 # Deprecated, but kept alive for help generation a purpose.
225 225 def showcurrentbookmark(**args):
226 226 """:currentbookmark: String. The active bookmark, if it is
227 227 associated with the changeset (DEPRECATED)"""
228 228 return showactivebookmark(**args)
229 229
230 230 def showactivebookmark(**args):
231 """:activetbookmark: String. The active bookmark, if it is
231 """:activebookmark: String. The active bookmark, if it is
232 232 associated with the changeset"""
233 233 active = args['repo']._activebookmark
234 234 if active and active in args['ctx'].bookmarks():
235 235 return active
236 236 return ''
237 237
238 238 def showdate(repo, ctx, templ, **args):
239 239 """:date: Date information. The date when the changeset was committed."""
240 240 return ctx.date()
241 241
242 242 def showdescription(repo, ctx, templ, **args):
243 243 """:desc: String. The text of the changeset description."""
244 244 return ctx.description().strip()
245 245
246 246 def showdiffstat(repo, ctx, templ, **args):
247 247 """:diffstat: String. Statistics of changes with the following format:
248 248 "modified files: +added/-removed lines"
249 249 """
250 250 stats = patch.diffstatdata(util.iterlines(ctx.diff()))
251 251 maxname, maxtotal, adds, removes, binary = patch.diffstatsum(stats)
252 252 return '%s: +%s/-%s' % (len(stats), adds, removes)
253 253
254 254 def showextras(**args):
255 255 """:extras: List of dicts with key, value entries of the 'extras'
256 256 field of this changeset."""
257 257 extras = args['ctx'].extra()
258 258 extras = util.sortdict((k, extras[k]) for k in sorted(extras))
259 259 makemap = lambda k: {'key': k, 'value': extras[k]}
260 260 c = [makemap(k) for k in extras]
261 261 f = _showlist('extra', c, plural='extras', **args)
262 262 return _hybrid(f, extras, makemap,
263 263 lambda x: '%s=%s' % (x['key'], x['value']))
264 264
265 265 def showfileadds(**args):
266 266 """:file_adds: List of strings. Files added by this changeset."""
267 267 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
268 268 return showlist('file_add', getfiles(repo, ctx, revcache)[1],
269 269 element='file', **args)
270 270
271 271 def showfilecopies(**args):
272 272 """:file_copies: List of strings. Files copied in this changeset with
273 273 their sources.
274 274 """
275 275 cache, ctx = args['cache'], args['ctx']
276 276 copies = args['revcache'].get('copies')
277 277 if copies is None:
278 278 if 'getrenamed' not in cache:
279 279 cache['getrenamed'] = getrenamedfn(args['repo'])
280 280 copies = []
281 281 getrenamed = cache['getrenamed']
282 282 for fn in ctx.files():
283 283 rename = getrenamed(fn, ctx.rev())
284 284 if rename:
285 285 copies.append((fn, rename[0]))
286 286
287 287 copies = util.sortdict(copies)
288 288 makemap = lambda k: {'name': k, 'source': copies[k]}
289 289 c = [makemap(k) for k in copies]
290 290 f = _showlist('file_copy', c, plural='file_copies', **args)
291 291 return _hybrid(f, copies, makemap,
292 292 lambda x: '%s (%s)' % (x['name'], x['source']))
293 293
294 294 # showfilecopiesswitch() displays file copies only if copy records are
295 295 # provided before calling the templater, usually with a --copies
296 296 # command line switch.
297 297 def showfilecopiesswitch(**args):
298 298 """:file_copies_switch: List of strings. Like "file_copies" but displayed
299 299 only if the --copied switch is set.
300 300 """
301 301 copies = args['revcache'].get('copies') or []
302 302 copies = util.sortdict(copies)
303 303 makemap = lambda k: {'name': k, 'source': copies[k]}
304 304 c = [makemap(k) for k in copies]
305 305 f = _showlist('file_copy', c, plural='file_copies', **args)
306 306 return _hybrid(f, copies, makemap,
307 307 lambda x: '%s (%s)' % (x['name'], x['source']))
308 308
309 309 def showfiledels(**args):
310 310 """:file_dels: List of strings. Files removed by this changeset."""
311 311 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
312 312 return showlist('file_del', getfiles(repo, ctx, revcache)[2],
313 313 element='file', **args)
314 314
315 315 def showfilemods(**args):
316 316 """:file_mods: List of strings. Files modified by this changeset."""
317 317 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
318 318 return showlist('file_mod', getfiles(repo, ctx, revcache)[0],
319 319 element='file', **args)
320 320
321 321 def showfiles(**args):
322 322 """:files: List of strings. All files modified, added, or removed by this
323 323 changeset.
324 324 """
325 325 return showlist('file', args['ctx'].files(), **args)
326 326
327 327 def showlatesttag(repo, ctx, templ, cache, **args):
328 328 """:latesttag: String. Most recent global tag in the ancestors of this
329 329 changeset.
330 330 """
331 331 return getlatesttags(repo, ctx, cache)[2]
332 332
333 333 def showlatesttagdistance(repo, ctx, templ, cache, **args):
334 334 """:latesttagdistance: Integer. Longest path to the latest tag."""
335 335 return getlatesttags(repo, ctx, cache)[1]
336 336
337 337 def showmanifest(**args):
338 338 repo, ctx, templ = args['repo'], args['ctx'], args['templ']
339 339 mnode = ctx.manifestnode()
340 340 args = args.copy()
341 341 args.update({'rev': repo.manifest.rev(mnode), 'node': hex(mnode)})
342 342 return templ('manifest', **args)
343 343
344 344 def shownode(repo, ctx, templ, **args):
345 345 """:node: String. The changeset identification hash, as a 40 hexadecimal
346 346 digit string.
347 347 """
348 348 return ctx.hex()
349 349
350 350 def showp1rev(repo, ctx, templ, **args):
351 351 """:p1rev: Integer. The repository-local revision number of the changeset's
352 352 first parent, or -1 if the changeset has no parents."""
353 353 return ctx.p1().rev()
354 354
355 355 def showp2rev(repo, ctx, templ, **args):
356 356 """:p2rev: Integer. The repository-local revision number of the changeset's
357 357 second parent, or -1 if the changeset has no second parent."""
358 358 return ctx.p2().rev()
359 359
360 360 def showp1node(repo, ctx, templ, **args):
361 361 """:p1node: String. The identification hash of the changeset's first parent,
362 362 as a 40 digit hexadecimal string. If the changeset has no parents, all
363 363 digits are 0."""
364 364 return ctx.p1().hex()
365 365
366 366 def showp2node(repo, ctx, templ, **args):
367 367 """:p2node: String. The identification hash of the changeset's second
368 368 parent, as a 40 digit hexadecimal string. If the changeset has no second
369 369 parent, all digits are 0."""
370 370 return ctx.p2().hex()
371 371
372 372 def showphase(repo, ctx, templ, **args):
373 373 """:phase: String. The changeset phase name."""
374 374 return ctx.phasestr()
375 375
376 376 def showphaseidx(repo, ctx, templ, **args):
377 377 """:phaseidx: Integer. The changeset phase index."""
378 378 return ctx.phase()
379 379
380 380 def showrev(repo, ctx, templ, **args):
381 381 """:rev: Integer. The repository-local changeset revision number."""
382 382 return ctx.rev()
383 383
384 384 def showsubrepos(**args):
385 385 """:subrepos: List of strings. Updated subrepositories in the changeset."""
386 386 ctx = args['ctx']
387 387 substate = ctx.substate
388 388 if not substate:
389 389 return showlist('subrepo', [], **args)
390 390 psubstate = ctx.parents()[0].substate or {}
391 391 subrepos = []
392 392 for sub in substate:
393 393 if sub not in psubstate or substate[sub] != psubstate[sub]:
394 394 subrepos.append(sub) # modified or newly added in ctx
395 395 for sub in psubstate:
396 396 if sub not in substate:
397 397 subrepos.append(sub) # removed in ctx
398 398 return showlist('subrepo', sorted(subrepos), **args)
399 399
400 400 def shownames(namespace, **args):
401 401 """helper method to generate a template keyword for a namespace"""
402 402 ctx = args['ctx']
403 403 repo = ctx.repo()
404 404 ns = repo.names[namespace]
405 405 names = ns.names(repo, ctx.node())
406 406 return showlist(ns.templatename, names, plural=namespace, **args)
407 407
408 408 # don't remove "showtags" definition, even though namespaces will put
409 409 # a helper function for "tags" keyword into "keywords" map automatically,
410 410 # because online help text is built without namespaces initialization
411 411 def showtags(**args):
412 412 """:tags: List of strings. Any tags associated with the changeset."""
413 413 return shownames('tags', **args)
414 414
415 415 # keywords are callables like:
416 416 # fn(repo, ctx, templ, cache, revcache, **args)
417 417 # with:
418 418 # repo - current repository instance
419 419 # ctx - the changectx being displayed
420 420 # templ - the templater instance
421 421 # cache - a cache dictionary for the whole templater run
422 422 # revcache - a cache dictionary for the current revision
423 423 keywords = {
424 424 'activebookmark': showactivebookmark,
425 425 'author': showauthor,
426 426 'bisect': showbisect,
427 427 'branch': showbranch,
428 428 'branches': showbranches,
429 429 'bookmarks': showbookmarks,
430 430 'children': showchildren,
431 431 # currentbookmark is deprecated
432 432 'currentbookmark': showcurrentbookmark,
433 433 'date': showdate,
434 434 'desc': showdescription,
435 435 'diffstat': showdiffstat,
436 436 'extras': showextras,
437 437 'file_adds': showfileadds,
438 438 'file_copies': showfilecopies,
439 439 'file_copies_switch': showfilecopiesswitch,
440 440 'file_dels': showfiledels,
441 441 'file_mods': showfilemods,
442 442 'files': showfiles,
443 443 'latesttag': showlatesttag,
444 444 'latesttagdistance': showlatesttagdistance,
445 445 'manifest': showmanifest,
446 446 'node': shownode,
447 447 'p1rev': showp1rev,
448 448 'p1node': showp1node,
449 449 'p2rev': showp2rev,
450 450 'p2node': showp2node,
451 451 'phase': showphase,
452 452 'phaseidx': showphaseidx,
453 453 'rev': showrev,
454 454 'subrepos': showsubrepos,
455 455 'tags': showtags,
456 456 }
457 457
458 458 def _showparents(**args):
459 459 """:parents: List of strings. The parents of the changeset in "rev:node"
460 460 format. If the changeset has only one "natural" parent (the predecessor
461 461 revision) nothing is shown."""
462 462 pass
463 463
464 464 dockeywords = {
465 465 'parents': _showparents,
466 466 }
467 467 dockeywords.update(keywords)
468 468 del dockeywords['branches']
469 469
470 470 # tell hggettext to extract docstrings from these functions:
471 471 i18nfunctions = dockeywords.values()
General Comments 0
You need to be logged in to leave comments. Login now