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