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