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