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