comparison mercurial/templatekw.py @ 43076:2372284d9457

formatting: blacken the codebase This is using my patch to black (https://github.com/psf/black/pull/826) so we don't un-wrap collection literals. Done with: hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S # skip-blame mass-reformatting only # no-check-commit reformats foo_bar functions Differential Revision: https://phab.mercurial-scm.org/D6971
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:45:02 -0400
parents 88ba0ff94605
children 687b865b95ad
comparison
equal deleted inserted replaced
43075:57875cf423c9 43076:2372284d9457
27 registrar, 27 registrar,
28 scmutil, 28 scmutil,
29 templateutil, 29 templateutil,
30 util, 30 util,
31 ) 31 )
32 from .utils import ( 32 from .utils import stringutil
33 stringutil,
34 )
35 33
36 _hybrid = templateutil.hybrid 34 _hybrid = templateutil.hybrid
37 hybriddict = templateutil.hybriddict 35 hybriddict = templateutil.hybriddict
38 hybridlist = templateutil.hybridlist 36 hybridlist = templateutil.hybridlist
39 compatdict = templateutil.compatdict 37 compatdict = templateutil.compatdict
40 compatlist = templateutil.compatlist 38 compatlist = templateutil.compatlist
41 _showcompatlist = templateutil._showcompatlist 39 _showcompatlist = templateutil._showcompatlist
40
42 41
43 def getlatesttags(context, mapping, pattern=None): 42 def getlatesttags(context, mapping, pattern=None):
44 '''return date, distance and name for the latest tag of rev''' 43 '''return date, distance and name for the latest tag of rev'''
45 repo = context.resource(mapping, 'repo') 44 repo = context.resource(mapping, 'repo')
46 ctx = context.resource(mapping, 'ctx') 45 ctx = context.resource(mapping, 'ctx')
64 while todo: 63 while todo:
65 rev = todo.pop() 64 rev = todo.pop()
66 if rev in latesttags: 65 if rev in latesttags:
67 continue 66 continue
68 ctx = repo[rev] 67 ctx = repo[rev]
69 tags = [t for t in ctx.tags() 68 tags = [
70 if (repo.tagtype(t) and repo.tagtype(t) != 'local' 69 t
71 and match(t))] 70 for t in ctx.tags()
71 if (repo.tagtype(t) and repo.tagtype(t) != 'local' and match(t))
72 ]
72 if tags: 73 if tags:
73 latesttags[rev] = ctx.date()[0], 0, [t for t in sorted(tags)] 74 latesttags[rev] = ctx.date()[0], 0, [t for t in sorted(tags)]
74 continue 75 continue
75 try: 76 try:
76 ptags = [latesttags[p.rev()] for p in ctx.parents()] 77 ptags = [latesttags[p.rev()] for p in ctx.parents()]
78 if ptags[0][2] == ptags[1][2]: 79 if ptags[0][2] == ptags[1][2]:
79 # The tuples are laid out so the right one can be found by 80 # The tuples are laid out so the right one can be found by
80 # comparison in this case. 81 # comparison in this case.
81 pdate, pdist, ptag = max(ptags) 82 pdate, pdist, ptag = max(ptags)
82 else: 83 else:
84
83 def key(x): 85 def key(x):
84 tag = x[2][0] 86 tag = x[2][0]
85 if ctx.rev() is None: 87 if ctx.rev() is None:
86 # only() doesn't support wdir 88 # only() doesn't support wdir
87 prevs = [c.rev() for c in ctx.parents()] 89 prevs = [c.rev() for c in ctx.parents()]
91 changes = repo.revs('only(%d, %s)', ctx.rev(), tag) 93 changes = repo.revs('only(%d, %s)', ctx.rev(), tag)
92 changessincetag = len(changes) 94 changessincetag = len(changes)
93 # Smallest number of changes since tag wins. Date is 95 # Smallest number of changes since tag wins. Date is
94 # used as tiebreaker. 96 # used as tiebreaker.
95 return [-changessincetag, x[0]] 97 return [-changessincetag, x[0]]
98
96 pdate, pdist, ptag = max(ptags, key=key) 99 pdate, pdist, ptag = max(ptags, key=key)
97 else: 100 else:
98 pdate, pdist, ptag = ptags[0] 101 pdate, pdist, ptag = ptags[0]
99 except KeyError: 102 except KeyError:
100 # Cache miss - recurse 103 # Cache miss - recurse
102 todo.extend(p.rev() for p in ctx.parents()) 105 todo.extend(p.rev() for p in ctx.parents())
103 continue 106 continue
104 latesttags[rev] = pdate, pdist + 1, ptag 107 latesttags[rev] = pdate, pdist + 1, ptag
105 return latesttags[rev] 108 return latesttags[rev]
106 109
110
107 def getlogcolumns(): 111 def getlogcolumns():
108 """Return a dict of log column labels""" 112 """Return a dict of log column labels"""
109 _ = pycompat.identity # temporarily disable gettext 113 _ = pycompat.identity # temporarily disable gettext
110 # i18n: column positioning for "hg log" 114 # i18n: column positioning for "hg log"
111 columns = _('bookmark: %s\n' 115 columns = _(
112 'branch: %s\n' 116 'bookmark: %s\n'
113 'changeset: %s\n' 117 'branch: %s\n'
114 'copies: %s\n' 118 'changeset: %s\n'
115 'date: %s\n' 119 'copies: %s\n'
116 'extra: %s=%s\n' 120 'date: %s\n'
117 'files+: %s\n' 121 'extra: %s=%s\n'
118 'files-: %s\n' 122 'files+: %s\n'
119 'files: %s\n' 123 'files-: %s\n'
120 'instability: %s\n' 124 'files: %s\n'
121 'manifest: %s\n' 125 'instability: %s\n'
122 'obsolete: %s\n' 126 'manifest: %s\n'
123 'parent: %s\n' 127 'obsolete: %s\n'
124 'phase: %s\n' 128 'parent: %s\n'
125 'summary: %s\n' 129 'phase: %s\n'
126 'tag: %s\n' 130 'summary: %s\n'
127 'user: %s\n') 131 'tag: %s\n'
128 return dict(zip([s.split(':', 1)[0] for s in columns.splitlines()], 132 'user: %s\n'
129 i18n._(columns).splitlines(True))) 133 )
134 return dict(
135 zip(
136 [s.split(':', 1)[0] for s in columns.splitlines()],
137 i18n._(columns).splitlines(True),
138 )
139 )
140
130 141
131 # basic internal templates 142 # basic internal templates
132 _changeidtmpl = '{rev}:{node|formatnode}' 143 _changeidtmpl = '{rev}:{node|formatnode}'
133 144
134 # default templates internally used for rendering of lists 145 # default templates internally used for rendering of lists
135 defaulttempl = { 146 defaulttempl = {
136 'parent': _changeidtmpl + ' ', 147 'parent': _changeidtmpl + ' ',
137 'manifest': _changeidtmpl, 148 'manifest': _changeidtmpl,
138 'file_copy': '{name} ({source})', 149 'file_copy': '{name} ({source})',
139 'envvar': '{key}={value}', 150 'envvar': '{key}={value}',
140 'extra': '{key}={value|stringescape}' 151 'extra': '{key}={value|stringescape}',
141 } 152 }
142 # filecopy is preserved for compatibility reasons 153 # filecopy is preserved for compatibility reasons
143 defaulttempl['filecopy'] = defaulttempl['file_copy'] 154 defaulttempl['filecopy'] = defaulttempl['file_copy']
144 155
145 # keywords are callables (see registrar.templatekeyword for details) 156 # keywords are callables (see registrar.templatekeyword for details)
146 keywords = {} 157 keywords = {}
147 templatekeyword = registrar.templatekeyword(keywords) 158 templatekeyword = registrar.templatekeyword(keywords)
159
148 160
149 @templatekeyword('author', requires={'ctx'}) 161 @templatekeyword('author', requires={'ctx'})
150 def showauthor(context, mapping): 162 def showauthor(context, mapping):
151 """Alias for ``{user}``""" 163 """Alias for ``{user}``"""
152 return showuser(context, mapping) 164 return showuser(context, mapping)
153 165
166
154 @templatekeyword('bisect', requires={'repo', 'ctx'}) 167 @templatekeyword('bisect', requires={'repo', 'ctx'})
155 def showbisect(context, mapping): 168 def showbisect(context, mapping):
156 """String. The changeset bisection status.""" 169 """String. The changeset bisection status."""
157 repo = context.resource(mapping, 'repo') 170 repo = context.resource(mapping, 'repo')
158 ctx = context.resource(mapping, 'ctx') 171 ctx = context.resource(mapping, 'ctx')
159 return hbisect.label(repo, ctx.node()) 172 return hbisect.label(repo, ctx.node())
173
160 174
161 @templatekeyword('branch', requires={'ctx'}) 175 @templatekeyword('branch', requires={'ctx'})
162 def showbranch(context, mapping): 176 def showbranch(context, mapping):
163 """String. The name of the branch on which the changeset was 177 """String. The name of the branch on which the changeset was
164 committed. 178 committed.
165 """ 179 """
166 ctx = context.resource(mapping, 'ctx') 180 ctx = context.resource(mapping, 'ctx')
167 return ctx.branch() 181 return ctx.branch()
182
168 183
169 @templatekeyword('branches', requires={'ctx'}) 184 @templatekeyword('branches', requires={'ctx'})
170 def showbranches(context, mapping): 185 def showbranches(context, mapping):
171 """List of strings. The name of the branch on which the 186 """List of strings. The name of the branch on which the
172 changeset was committed. Will be empty if the branch name was 187 changeset was committed. Will be empty if the branch name was
173 default. (DEPRECATED) 188 default. (DEPRECATED)
174 """ 189 """
175 ctx = context.resource(mapping, 'ctx') 190 ctx = context.resource(mapping, 'ctx')
176 branch = ctx.branch() 191 branch = ctx.branch()
177 if branch != 'default': 192 if branch != 'default':
178 return compatlist(context, mapping, 'branch', [branch], 193 return compatlist(
179 plural='branches') 194 context, mapping, 'branch', [branch], plural='branches'
195 )
180 return compatlist(context, mapping, 'branch', [], plural='branches') 196 return compatlist(context, mapping, 'branch', [], plural='branches')
197
181 198
182 @templatekeyword('bookmarks', requires={'repo', 'ctx'}) 199 @templatekeyword('bookmarks', requires={'repo', 'ctx'})
183 def showbookmarks(context, mapping): 200 def showbookmarks(context, mapping):
184 """List of strings. Any bookmarks associated with the 201 """List of strings. Any bookmarks associated with the
185 changeset. Also sets 'active', the name of the active bookmark. 202 changeset. Also sets 'active', the name of the active bookmark.
190 active = repo._activebookmark 207 active = repo._activebookmark
191 makemap = lambda v: {'bookmark': v, 'active': active, 'current': active} 208 makemap = lambda v: {'bookmark': v, 'active': active, 'current': active}
192 f = _showcompatlist(context, mapping, 'bookmark', bookmarks) 209 f = _showcompatlist(context, mapping, 'bookmark', bookmarks)
193 return _hybrid(f, bookmarks, makemap, pycompat.identity) 210 return _hybrid(f, bookmarks, makemap, pycompat.identity)
194 211
212
195 @templatekeyword('children', requires={'ctx'}) 213 @templatekeyword('children', requires={'ctx'})
196 def showchildren(context, mapping): 214 def showchildren(context, mapping):
197 """List of strings. The children of the changeset.""" 215 """List of strings. The children of the changeset."""
198 ctx = context.resource(mapping, 'ctx') 216 ctx = context.resource(mapping, 'ctx')
199 childrevs = ['%d:%s' % (cctx.rev(), cctx) for cctx in ctx.children()] 217 childrevs = ['%d:%s' % (cctx.rev(), cctx) for cctx in ctx.children()]
200 return compatlist(context, mapping, 'children', childrevs, element='child') 218 return compatlist(context, mapping, 'children', childrevs, element='child')
219
201 220
202 # Deprecated, but kept alive for help generation a purpose. 221 # Deprecated, but kept alive for help generation a purpose.
203 @templatekeyword('currentbookmark', requires={'repo', 'ctx'}) 222 @templatekeyword('currentbookmark', requires={'repo', 'ctx'})
204 def showcurrentbookmark(context, mapping): 223 def showcurrentbookmark(context, mapping):
205 """String. The active bookmark, if it is associated with the changeset. 224 """String. The active bookmark, if it is associated with the changeset.
206 (DEPRECATED)""" 225 (DEPRECATED)"""
207 return showactivebookmark(context, mapping) 226 return showactivebookmark(context, mapping)
208 227
228
209 @templatekeyword('activebookmark', requires={'repo', 'ctx'}) 229 @templatekeyword('activebookmark', requires={'repo', 'ctx'})
210 def showactivebookmark(context, mapping): 230 def showactivebookmark(context, mapping):
211 """String. The active bookmark, if it is associated with the changeset.""" 231 """String. The active bookmark, if it is associated with the changeset."""
212 repo = context.resource(mapping, 'repo') 232 repo = context.resource(mapping, 'repo')
213 ctx = context.resource(mapping, 'ctx') 233 ctx = context.resource(mapping, 'ctx')
214 active = repo._activebookmark 234 active = repo._activebookmark
215 if active and active in ctx.bookmarks(): 235 if active and active in ctx.bookmarks():
216 return active 236 return active
217 return '' 237 return ''
218 238
239
219 @templatekeyword('date', requires={'ctx'}) 240 @templatekeyword('date', requires={'ctx'})
220 def showdate(context, mapping): 241 def showdate(context, mapping):
221 """Date information. The date when the changeset was committed.""" 242 """Date information. The date when the changeset was committed."""
222 ctx = context.resource(mapping, 'ctx') 243 ctx = context.resource(mapping, 'ctx')
223 # the default string format is '<float(unixtime)><tzoffset>' because 244 # the default string format is '<float(unixtime)><tzoffset>' because
224 # python-hglib splits date at decimal separator. 245 # python-hglib splits date at decimal separator.
225 return templateutil.date(ctx.date(), showfmt='%d.0%d') 246 return templateutil.date(ctx.date(), showfmt='%d.0%d')
247
226 248
227 @templatekeyword('desc', requires={'ctx'}) 249 @templatekeyword('desc', requires={'ctx'})
228 def showdescription(context, mapping): 250 def showdescription(context, mapping):
229 """String. The text of the changeset description.""" 251 """String. The text of the changeset description."""
230 ctx = context.resource(mapping, 'ctx') 252 ctx = context.resource(mapping, 'ctx')
235 elif isinstance(s, encoding.safelocalstr): 257 elif isinstance(s, encoding.safelocalstr):
236 return encoding.safelocalstr(s.strip()) 258 return encoding.safelocalstr(s.strip())
237 else: 259 else:
238 return s.strip() 260 return s.strip()
239 261
262
240 @templatekeyword('diffstat', requires={'ui', 'ctx'}) 263 @templatekeyword('diffstat', requires={'ui', 'ctx'})
241 def showdiffstat(context, mapping): 264 def showdiffstat(context, mapping):
242 """String. Statistics of changes with the following format: 265 """String. Statistics of changes with the following format:
243 "modified files: +added/-removed lines" 266 "modified files: +added/-removed lines"
244 """ 267 """
247 diffopts = diffutil.diffallopts(ui, {'noprefix': False}) 270 diffopts = diffutil.diffallopts(ui, {'noprefix': False})
248 diff = ctx.diff(opts=diffopts) 271 diff = ctx.diff(opts=diffopts)
249 stats = patch.diffstatdata(util.iterlines(diff)) 272 stats = patch.diffstatdata(util.iterlines(diff))
250 maxname, maxtotal, adds, removes, binary = patch.diffstatsum(stats) 273 maxname, maxtotal, adds, removes, binary = patch.diffstatsum(stats)
251 return '%d: +%d/-%d' % (len(stats), adds, removes) 274 return '%d: +%d/-%d' % (len(stats), adds, removes)
275
252 276
253 @templatekeyword('envvars', requires={'ui'}) 277 @templatekeyword('envvars', requires={'ui'})
254 def showenvvars(context, mapping): 278 def showenvvars(context, mapping):
255 """A dictionary of environment variables. (EXPERIMENTAL)""" 279 """A dictionary of environment variables. (EXPERIMENTAL)"""
256 ui = context.resource(mapping, 'ui') 280 ui = context.resource(mapping, 'ui')
257 env = ui.exportableenviron() 281 env = ui.exportableenviron()
258 env = util.sortdict((k, env[k]) for k in sorted(env)) 282 env = util.sortdict((k, env[k]) for k in sorted(env))
259 return compatdict(context, mapping, 'envvar', env, plural='envvars') 283 return compatdict(context, mapping, 'envvar', env, plural='envvars')
260 284
285
261 @templatekeyword('extras', requires={'ctx'}) 286 @templatekeyword('extras', requires={'ctx'})
262 def showextras(context, mapping): 287 def showextras(context, mapping):
263 """List of dicts with key, value entries of the 'extras' 288 """List of dicts with key, value entries of the 'extras'
264 field of this changeset.""" 289 field of this changeset."""
265 ctx = context.resource(mapping, 'ctx') 290 ctx = context.resource(mapping, 'ctx')
266 extras = ctx.extra() 291 extras = ctx.extra()
267 extras = util.sortdict((k, extras[k]) for k in sorted(extras)) 292 extras = util.sortdict((k, extras[k]) for k in sorted(extras))
268 makemap = lambda k: {'key': k, 'value': extras[k]} 293 makemap = lambda k: {'key': k, 'value': extras[k]}
269 c = [makemap(k) for k in extras] 294 c = [makemap(k) for k in extras]
270 f = _showcompatlist(context, mapping, 'extra', c, plural='extras') 295 f = _showcompatlist(context, mapping, 'extra', c, plural='extras')
271 return _hybrid(f, extras, makemap, 296 return _hybrid(
272 lambda k: '%s=%s' % (k, stringutil.escapestr(extras[k]))) 297 f,
298 extras,
299 makemap,
300 lambda k: '%s=%s' % (k, stringutil.escapestr(extras[k])),
301 )
302
273 303
274 def _getfilestatus(context, mapping, listall=False): 304 def _getfilestatus(context, mapping, listall=False):
275 ctx = context.resource(mapping, 'ctx') 305 ctx = context.resource(mapping, 'ctx')
276 revcache = context.resource(mapping, 'revcache') 306 revcache = context.resource(mapping, 'revcache')
277 if 'filestatus' not in revcache or revcache['filestatusall'] < listall: 307 if 'filestatus' not in revcache or revcache['filestatusall'] < listall:
278 stat = ctx.p1().status(ctx, listignored=listall, listclean=listall, 308 stat = ctx.p1().status(
279 listunknown=listall) 309 ctx, listignored=listall, listclean=listall, listunknown=listall
310 )
280 revcache['filestatus'] = stat 311 revcache['filestatus'] = stat
281 revcache['filestatusall'] = listall 312 revcache['filestatusall'] = listall
282 return revcache['filestatus'] 313 return revcache['filestatus']
314
283 315
284 def _getfilestatusmap(context, mapping, listall=False): 316 def _getfilestatusmap(context, mapping, listall=False):
285 revcache = context.resource(mapping, 'revcache') 317 revcache = context.resource(mapping, 'revcache')
286 if 'filestatusmap' not in revcache or revcache['filestatusall'] < listall: 318 if 'filestatusmap' not in revcache or revcache['filestatusall'] < listall:
287 stat = _getfilestatus(context, mapping, listall=listall) 319 stat = _getfilestatus(context, mapping, listall=listall)
288 revcache['filestatusmap'] = statmap = {} 320 revcache['filestatusmap'] = statmap = {}
289 for char, files in zip(pycompat.iterbytestr('MAR!?IC'), stat): 321 for char, files in zip(pycompat.iterbytestr('MAR!?IC'), stat):
290 statmap.update((f, char) for f in files) 322 statmap.update((f, char) for f in files)
291 return revcache['filestatusmap'] # {path: statchar} 323 return revcache['filestatusmap'] # {path: statchar}
292 324
293 @templatekeyword('file_copies', 325
294 requires={'repo', 'ctx', 'cache', 'revcache'}) 326 @templatekeyword('file_copies', requires={'repo', 'ctx', 'cache', 'revcache'})
295 def showfilecopies(context, mapping): 327 def showfilecopies(context, mapping):
296 """List of strings. Files copied in this changeset with 328 """List of strings. Files copied in this changeset with
297 their sources. 329 their sources.
298 """ 330 """
299 repo = context.resource(mapping, 'repo') 331 repo = context.resource(mapping, 'repo')
303 if copies is None: 335 if copies is None:
304 if 'getcopies' not in cache: 336 if 'getcopies' not in cache:
305 cache['getcopies'] = scmutil.getcopiesfn(repo) 337 cache['getcopies'] = scmutil.getcopiesfn(repo)
306 getcopies = cache['getcopies'] 338 getcopies = cache['getcopies']
307 copies = getcopies(ctx) 339 copies = getcopies(ctx)
308 return templateutil.compatfilecopiesdict(context, mapping, 'file_copy', 340 return templateutil.compatfilecopiesdict(
309 copies) 341 context, mapping, 'file_copy', copies
342 )
343
310 344
311 # showfilecopiesswitch() displays file copies only if copy records are 345 # showfilecopiesswitch() displays file copies only if copy records are
312 # provided before calling the templater, usually with a --copies 346 # provided before calling the templater, usually with a --copies
313 # command line switch. 347 # command line switch.
314 @templatekeyword('file_copies_switch', requires={'revcache'}) 348 @templatekeyword('file_copies_switch', requires={'revcache'})
315 def showfilecopiesswitch(context, mapping): 349 def showfilecopiesswitch(context, mapping):
316 """List of strings. Like "file_copies" but displayed 350 """List of strings. Like "file_copies" but displayed
317 only if the --copied switch is set. 351 only if the --copied switch is set.
318 """ 352 """
319 copies = context.resource(mapping, 'revcache').get('copies') or [] 353 copies = context.resource(mapping, 'revcache').get('copies') or []
320 return templateutil.compatfilecopiesdict(context, mapping, 'file_copy', 354 return templateutil.compatfilecopiesdict(
321 copies) 355 context, mapping, 'file_copy', copies
356 )
357
322 358
323 @templatekeyword('file_adds', requires={'ctx', 'revcache'}) 359 @templatekeyword('file_adds', requires={'ctx', 'revcache'})
324 def showfileadds(context, mapping): 360 def showfileadds(context, mapping):
325 """List of strings. Files added by this changeset.""" 361 """List of strings. Files added by this changeset."""
326 ctx = context.resource(mapping, 'ctx') 362 ctx = context.resource(mapping, 'ctx')
327 return templateutil.compatfileslist(context, mapping, 'file_add', 363 return templateutil.compatfileslist(
328 ctx.filesadded()) 364 context, mapping, 'file_add', ctx.filesadded()
365 )
366
329 367
330 @templatekeyword('file_dels', requires={'ctx', 'revcache'}) 368 @templatekeyword('file_dels', requires={'ctx', 'revcache'})
331 def showfiledels(context, mapping): 369 def showfiledels(context, mapping):
332 """List of strings. Files removed by this changeset.""" 370 """List of strings. Files removed by this changeset."""
333 ctx = context.resource(mapping, 'ctx') 371 ctx = context.resource(mapping, 'ctx')
334 return templateutil.compatfileslist(context, mapping, 'file_del', 372 return templateutil.compatfileslist(
335 ctx.filesremoved()) 373 context, mapping, 'file_del', ctx.filesremoved()
374 )
375
336 376
337 @templatekeyword('file_mods', requires={'ctx', 'revcache'}) 377 @templatekeyword('file_mods', requires={'ctx', 'revcache'})
338 def showfilemods(context, mapping): 378 def showfilemods(context, mapping):
339 """List of strings. Files modified by this changeset.""" 379 """List of strings. Files modified by this changeset."""
340 ctx = context.resource(mapping, 'ctx') 380 ctx = context.resource(mapping, 'ctx')
341 return templateutil.compatfileslist(context, mapping, 'file_mod', 381 return templateutil.compatfileslist(
342 ctx.filesmodified()) 382 context, mapping, 'file_mod', ctx.filesmodified()
383 )
384
343 385
344 @templatekeyword('files', requires={'ctx'}) 386 @templatekeyword('files', requires={'ctx'})
345 def showfiles(context, mapping): 387 def showfiles(context, mapping):
346 """List of strings. All files modified, added, or removed by this 388 """List of strings. All files modified, added, or removed by this
347 changeset. 389 changeset.
348 """ 390 """
349 ctx = context.resource(mapping, 'ctx') 391 ctx = context.resource(mapping, 'ctx')
350 return templateutil.compatfileslist(context, mapping, 'file', ctx.files()) 392 return templateutil.compatfileslist(context, mapping, 'file', ctx.files())
351 393
394
352 @templatekeyword('graphnode', requires={'repo', 'ctx'}) 395 @templatekeyword('graphnode', requires={'repo', 'ctx'})
353 def showgraphnode(context, mapping): 396 def showgraphnode(context, mapping):
354 """String. The character representing the changeset node in an ASCII 397 """String. The character representing the changeset node in an ASCII
355 revision graph.""" 398 revision graph."""
356 repo = context.resource(mapping, 'repo') 399 repo = context.resource(mapping, 'repo')
357 ctx = context.resource(mapping, 'ctx') 400 ctx = context.resource(mapping, 'ctx')
358 return getgraphnode(repo, ctx) 401 return getgraphnode(repo, ctx)
359 402
403
360 def getgraphnode(repo, ctx): 404 def getgraphnode(repo, ctx):
361 return getgraphnodecurrent(repo, ctx) or getgraphnodesymbol(ctx) 405 return getgraphnodecurrent(repo, ctx) or getgraphnodesymbol(ctx)
406
362 407
363 def getgraphnodecurrent(repo, ctx): 408 def getgraphnodecurrent(repo, ctx):
364 wpnodes = repo.dirstate.parents() 409 wpnodes = repo.dirstate.parents()
365 if wpnodes[1] == nullid: 410 if wpnodes[1] == nullid:
366 wpnodes = wpnodes[:1] 411 wpnodes = wpnodes[:1]
367 if ctx.node() in wpnodes: 412 if ctx.node() in wpnodes:
368 return '@' 413 return '@'
369 else: 414 else:
370 return '' 415 return ''
416
371 417
372 def getgraphnodesymbol(ctx): 418 def getgraphnodesymbol(ctx):
373 if ctx.obsolete(): 419 if ctx.obsolete():
374 return 'x' 420 return 'x'
375 elif ctx.isunstable(): 421 elif ctx.isunstable():
377 elif ctx.closesbranch(): 423 elif ctx.closesbranch():
378 return '_' 424 return '_'
379 else: 425 else:
380 return 'o' 426 return 'o'
381 427
428
382 @templatekeyword('graphwidth', requires=()) 429 @templatekeyword('graphwidth', requires=())
383 def showgraphwidth(context, mapping): 430 def showgraphwidth(context, mapping):
384 """Integer. The width of the graph drawn by 'log --graph' or zero.""" 431 """Integer. The width of the graph drawn by 'log --graph' or zero."""
385 # just hosts documentation; should be overridden by template mapping 432 # just hosts documentation; should be overridden by template mapping
386 return 0 433 return 0
387 434
435
388 @templatekeyword('index', requires=()) 436 @templatekeyword('index', requires=())
389 def showindex(context, mapping): 437 def showindex(context, mapping):
390 """Integer. The current iteration of the loop. (0 indexed)""" 438 """Integer. The current iteration of the loop. (0 indexed)"""
391 # just hosts documentation; should be overridden by template mapping 439 # just hosts documentation; should be overridden by template mapping
392 raise error.Abort(_("can't use index in this context")) 440 raise error.Abort(_("can't use index in this context"))
393 441
442
394 @templatekeyword('latesttag', requires={'repo', 'ctx', 'cache'}) 443 @templatekeyword('latesttag', requires={'repo', 'ctx', 'cache'})
395 def showlatesttag(context, mapping): 444 def showlatesttag(context, mapping):
396 """List of strings. The global tags on the most recent globally 445 """List of strings. The global tags on the most recent globally
397 tagged ancestor of this changeset. If no such tags exist, the list 446 tagged ancestor of this changeset. If no such tags exist, the list
398 consists of the single string "null". 447 consists of the single string "null".
399 """ 448 """
400 return showlatesttags(context, mapping, None) 449 return showlatesttags(context, mapping, None)
450
401 451
402 def showlatesttags(context, mapping, pattern): 452 def showlatesttags(context, mapping, pattern):
403 """helper method for the latesttag keyword and function""" 453 """helper method for the latesttag keyword and function"""
404 latesttags = getlatesttags(context, mapping, pattern) 454 latesttags = getlatesttags(context, mapping, pattern)
405 455
407 # branches in a stable manner- it is the date the tagged cset was created, 457 # branches in a stable manner- it is the date the tagged cset was created,
408 # not the date the tag was created. Therefore it isn't made visible here. 458 # not the date the tag was created. Therefore it isn't made visible here.
409 makemap = lambda v: { 459 makemap = lambda v: {
410 'changes': _showchangessincetag, 460 'changes': _showchangessincetag,
411 'distance': latesttags[1], 461 'distance': latesttags[1],
412 'latesttag': v, # BC with {latesttag % '{latesttag}'} 462 'latesttag': v, # BC with {latesttag % '{latesttag}'}
413 'tag': v 463 'tag': v,
414 } 464 }
415 465
416 tags = latesttags[2] 466 tags = latesttags[2]
417 f = _showcompatlist(context, mapping, 'latesttag', tags, separator=':') 467 f = _showcompatlist(context, mapping, 'latesttag', tags, separator=':')
418 return _hybrid(f, tags, makemap, pycompat.identity) 468 return _hybrid(f, tags, makemap, pycompat.identity)
469
419 470
420 @templatekeyword('latesttagdistance', requires={'repo', 'ctx', 'cache'}) 471 @templatekeyword('latesttagdistance', requires={'repo', 'ctx', 'cache'})
421 def showlatesttagdistance(context, mapping): 472 def showlatesttagdistance(context, mapping):
422 """Integer. Longest path to the latest tag.""" 473 """Integer. Longest path to the latest tag."""
423 return getlatesttags(context, mapping)[1] 474 return getlatesttags(context, mapping)[1]
475
424 476
425 @templatekeyword('changessincelatesttag', requires={'repo', 'ctx', 'cache'}) 477 @templatekeyword('changessincelatesttag', requires={'repo', 'ctx', 'cache'})
426 def showchangessincelatesttag(context, mapping): 478 def showchangessincelatesttag(context, mapping):
427 """Integer. All ancestors not in the latest tag.""" 479 """Integer. All ancestors not in the latest tag."""
428 tag = getlatesttags(context, mapping)[2][0] 480 tag = getlatesttags(context, mapping)[2][0]
429 mapping = context.overlaymap(mapping, {'tag': tag}) 481 mapping = context.overlaymap(mapping, {'tag': tag})
430 return _showchangessincetag(context, mapping) 482 return _showchangessincetag(context, mapping)
431 483
484
432 def _showchangessincetag(context, mapping): 485 def _showchangessincetag(context, mapping):
433 repo = context.resource(mapping, 'repo') 486 repo = context.resource(mapping, 'repo')
434 ctx = context.resource(mapping, 'ctx') 487 ctx = context.resource(mapping, 'ctx')
435 offset = 0 488 offset = 0
436 revs = [ctx.rev()] 489 revs = [ctx.rev()]
441 offset = 1 494 offset = 1
442 revs = [p.rev() for p in ctx.parents()] 495 revs = [p.rev() for p in ctx.parents()]
443 496
444 return len(repo.revs('only(%ld, %s)', revs, tag)) + offset 497 return len(repo.revs('only(%ld, %s)', revs, tag)) + offset
445 498
499
446 # teach templater latesttags.changes is switched to (context, mapping) API 500 # teach templater latesttags.changes is switched to (context, mapping) API
447 _showchangessincetag._requires = {'repo', 'ctx'} 501 _showchangessincetag._requires = {'repo', 'ctx'}
502
448 503
449 @templatekeyword('manifest', requires={'repo', 'ctx'}) 504 @templatekeyword('manifest', requires={'repo', 'ctx'})
450 def showmanifest(context, mapping): 505 def showmanifest(context, mapping):
451 repo = context.resource(mapping, 'repo') 506 repo = context.resource(mapping, 'repo')
452 ctx = context.resource(mapping, 'ctx') 507 ctx = context.resource(mapping, 'ctx')
457 else: 512 else:
458 mrev = repo.manifestlog.rev(mnode) 513 mrev = repo.manifestlog.rev(mnode)
459 mhex = hex(mnode) 514 mhex = hex(mnode)
460 mapping = context.overlaymap(mapping, {'rev': mrev, 'node': mhex}) 515 mapping = context.overlaymap(mapping, {'rev': mrev, 'node': mhex})
461 f = context.process('manifest', mapping) 516 f = context.process('manifest', mapping)
462 return templateutil.hybriditem(f, None, f, 517 return templateutil.hybriditem(
463 lambda x: {'rev': mrev, 'node': mhex}) 518 f, None, f, lambda x: {'rev': mrev, 'node': mhex}
519 )
520
464 521
465 @templatekeyword('obsfate', requires={'ui', 'repo', 'ctx'}) 522 @templatekeyword('obsfate', requires={'ui', 'repo', 'ctx'})
466 def showobsfate(context, mapping): 523 def showobsfate(context, mapping):
467 # this function returns a list containing pre-formatted obsfate strings. 524 # this function returns a list containing pre-formatted obsfate strings.
468 # 525 #
473 ui = context.resource(mapping, 'ui') 530 ui = context.resource(mapping, 'ui')
474 repo = context.resource(mapping, 'repo') 531 repo = context.resource(mapping, 'repo')
475 values = [] 532 values = []
476 533
477 for x in succsandmarkers.tovalue(context, mapping): 534 for x in succsandmarkers.tovalue(context, mapping):
478 v = obsutil.obsfateprinter(ui, repo, x['successors'], x['markers'], 535 v = obsutil.obsfateprinter(
479 scmutil.formatchangeid) 536 ui, repo, x['successors'], x['markers'], scmutil.formatchangeid
537 )
480 values.append(v) 538 values.append(v)
481 539
482 return compatlist(context, mapping, "fate", values) 540 return compatlist(context, mapping, "fate", values)
541
483 542
484 def shownames(context, mapping, namespace): 543 def shownames(context, mapping, namespace):
485 """helper method to generate a template keyword for a namespace""" 544 """helper method to generate a template keyword for a namespace"""
486 repo = context.resource(mapping, 'repo') 545 repo = context.resource(mapping, 'repo')
487 ctx = context.resource(mapping, 'ctx') 546 ctx = context.resource(mapping, 'ctx')
488 ns = repo.names[namespace] 547 ns = repo.names[namespace]
489 names = ns.names(repo, ctx.node()) 548 names = ns.names(repo, ctx.node())
490 return compatlist(context, mapping, ns.templatename, names, 549 return compatlist(
491 plural=namespace) 550 context, mapping, ns.templatename, names, plural=namespace
551 )
552
492 553
493 @templatekeyword('namespaces', requires={'repo', 'ctx'}) 554 @templatekeyword('namespaces', requires={'repo', 'ctx'})
494 def shownamespaces(context, mapping): 555 def shownamespaces(context, mapping):
495 """Dict of lists. Names attached to this changeset per 556 """Dict of lists. Names attached to this changeset per
496 namespace.""" 557 namespace."""
497 repo = context.resource(mapping, 'repo') 558 repo = context.resource(mapping, 'repo')
498 ctx = context.resource(mapping, 'ctx') 559 ctx = context.resource(mapping, 'ctx')
499 560
500 namespaces = util.sortdict() 561 namespaces = util.sortdict()
562
501 def makensmapfn(ns): 563 def makensmapfn(ns):
502 # 'name' for iterating over namespaces, templatename for local reference 564 # 'name' for iterating over namespaces, templatename for local reference
503 return lambda v: {'name': v, ns.templatename: v} 565 return lambda v: {'name': v, ns.templatename: v}
504 566
505 for k, ns in repo.names.iteritems(): 567 for k, ns in repo.names.iteritems():
517 'colorname': repo.names[ns].colorname, 579 'colorname': repo.names[ns].colorname,
518 } 580 }
519 581
520 return _hybrid(f, namespaces, makemap, pycompat.identity) 582 return _hybrid(f, namespaces, makemap, pycompat.identity)
521 583
584
522 @templatekeyword('negrev', requires={'repo', 'ctx'}) 585 @templatekeyword('negrev', requires={'repo', 'ctx'})
523 def shownegrev(context, mapping): 586 def shownegrev(context, mapping):
524 """Integer. The repository-local changeset negative revision number, 587 """Integer. The repository-local changeset negative revision number,
525 which counts in the opposite direction.""" 588 which counts in the opposite direction."""
526 ctx = context.resource(mapping, 'ctx') 589 ctx = context.resource(mapping, 'ctx')
528 if rev is None or rev < 0: # wdir() or nullrev? 591 if rev is None or rev < 0: # wdir() or nullrev?
529 return None 592 return None
530 repo = context.resource(mapping, 'repo') 593 repo = context.resource(mapping, 'repo')
531 return rev - len(repo) 594 return rev - len(repo)
532 595
596
533 @templatekeyword('node', requires={'ctx'}) 597 @templatekeyword('node', requires={'ctx'})
534 def shownode(context, mapping): 598 def shownode(context, mapping):
535 """String. The changeset identification hash, as a 40 hexadecimal 599 """String. The changeset identification hash, as a 40 hexadecimal
536 digit string. 600 digit string.
537 """ 601 """
538 ctx = context.resource(mapping, 'ctx') 602 ctx = context.resource(mapping, 'ctx')
539 return ctx.hex() 603 return ctx.hex()
540 604
605
541 @templatekeyword('obsolete', requires={'ctx'}) 606 @templatekeyword('obsolete', requires={'ctx'})
542 def showobsolete(context, mapping): 607 def showobsolete(context, mapping):
543 """String. Whether the changeset is obsolete. (EXPERIMENTAL)""" 608 """String. Whether the changeset is obsolete. (EXPERIMENTAL)"""
544 ctx = context.resource(mapping, 'ctx') 609 ctx = context.resource(mapping, 'ctx')
545 if ctx.obsolete(): 610 if ctx.obsolete():
546 return 'obsolete' 611 return 'obsolete'
547 return '' 612 return ''
613
548 614
549 @templatekeyword('path', requires={'fctx'}) 615 @templatekeyword('path', requires={'fctx'})
550 def showpath(context, mapping): 616 def showpath(context, mapping):
551 """String. Repository-absolute path of the current file. (EXPERIMENTAL)""" 617 """String. Repository-absolute path of the current file. (EXPERIMENTAL)"""
552 fctx = context.resource(mapping, 'fctx') 618 fctx = context.resource(mapping, 'fctx')
553 return fctx.path() 619 return fctx.path()
554 620
621
555 @templatekeyword('peerurls', requires={'repo'}) 622 @templatekeyword('peerurls', requires={'repo'})
556 def showpeerurls(context, mapping): 623 def showpeerurls(context, mapping):
557 """A dictionary of repository locations defined in the [paths] section 624 """A dictionary of repository locations defined in the [paths] section
558 of your configuration file.""" 625 of your configuration file."""
559 repo = context.resource(mapping, 'repo') 626 repo = context.resource(mapping, 'repo')
560 # see commands.paths() for naming of dictionary keys 627 # see commands.paths() for naming of dictionary keys
561 paths = repo.ui.paths 628 paths = repo.ui.paths
562 urls = util.sortdict((k, p.rawloc) for k, p in sorted(paths.iteritems())) 629 urls = util.sortdict((k, p.rawloc) for k, p in sorted(paths.iteritems()))
630
563 def makemap(k): 631 def makemap(k):
564 p = paths[k] 632 p = paths[k]
565 d = {'name': k, 'url': p.rawloc} 633 d = {'name': k, 'url': p.rawloc}
566 d.update((o, v) for o, v in sorted(p.suboptions.iteritems())) 634 d.update((o, v) for o, v in sorted(p.suboptions.iteritems()))
567 return d 635 return d
636
568 return _hybrid(None, urls, makemap, lambda k: '%s=%s' % (k, urls[k])) 637 return _hybrid(None, urls, makemap, lambda k: '%s=%s' % (k, urls[k]))
638
569 639
570 @templatekeyword("predecessors", requires={'repo', 'ctx'}) 640 @templatekeyword("predecessors", requires={'repo', 'ctx'})
571 def showpredecessors(context, mapping): 641 def showpredecessors(context, mapping):
572 """Returns the list of the closest visible predecessors. (EXPERIMENTAL)""" 642 """Returns the list of the closest visible predecessors. (EXPERIMENTAL)"""
573 repo = context.resource(mapping, 'repo') 643 repo = context.resource(mapping, 'repo')
574 ctx = context.resource(mapping, 'ctx') 644 ctx = context.resource(mapping, 'ctx')
575 predecessors = sorted(obsutil.closestpredecessors(repo, ctx.node())) 645 predecessors = sorted(obsutil.closestpredecessors(repo, ctx.node()))
576 predecessors = pycompat.maplist(hex, predecessors) 646 predecessors = pycompat.maplist(hex, predecessors)
577 647
578 return _hybrid(None, predecessors, 648 return _hybrid(
579 lambda x: {'ctx': repo[x]}, 649 None,
580 lambda x: scmutil.formatchangeid(repo[x])) 650 predecessors,
651 lambda x: {'ctx': repo[x]},
652 lambda x: scmutil.formatchangeid(repo[x]),
653 )
654
581 655
582 @templatekeyword('reporoot', requires={'repo'}) 656 @templatekeyword('reporoot', requires={'repo'})
583 def showreporoot(context, mapping): 657 def showreporoot(context, mapping):
584 """String. The root directory of the current repository.""" 658 """String. The root directory of the current repository."""
585 repo = context.resource(mapping, 'repo') 659 repo = context.resource(mapping, 'repo')
586 return repo.root 660 return repo.root
661
587 662
588 @templatekeyword('size', requires={'fctx'}) 663 @templatekeyword('size', requires={'fctx'})
589 def showsize(context, mapping): 664 def showsize(context, mapping):
590 """Integer. Size of the current file in bytes. (EXPERIMENTAL)""" 665 """Integer. Size of the current file in bytes. (EXPERIMENTAL)"""
591 fctx = context.resource(mapping, 'fctx') 666 fctx = context.resource(mapping, 'fctx')
592 return fctx.size() 667 return fctx.size()
668
593 669
594 # requires 'fctx' to denote {status} depends on (ctx, path) pair 670 # requires 'fctx' to denote {status} depends on (ctx, path) pair
595 @templatekeyword('status', requires={'ctx', 'fctx', 'revcache'}) 671 @templatekeyword('status', requires={'ctx', 'fctx', 'revcache'})
596 def showstatus(context, mapping): 672 def showstatus(context, mapping):
597 """String. Status code of the current file. (EXPERIMENTAL)""" 673 """String. Status code of the current file. (EXPERIMENTAL)"""
602 statmap = _getfilestatusmap(context, mapping) 678 statmap = _getfilestatusmap(context, mapping)
603 if path not in statmap: 679 if path not in statmap:
604 statmap = _getfilestatusmap(context, mapping, listall=True) 680 statmap = _getfilestatusmap(context, mapping, listall=True)
605 return statmap.get(path) 681 return statmap.get(path)
606 682
683
607 @templatekeyword("successorssets", requires={'repo', 'ctx'}) 684 @templatekeyword("successorssets", requires={'repo', 'ctx'})
608 def showsuccessorssets(context, mapping): 685 def showsuccessorssets(context, mapping):
609 """Returns a string of sets of successors for a changectx. Format used 686 """Returns a string of sets of successors for a changectx. Format used
610 is: [ctx1, ctx2], [ctx3] if ctx has been split into ctx1 and ctx2 687 is: [ctx1, ctx2], [ctx3] if ctx has been split into ctx1 and ctx2
611 while also diverged into ctx3. (EXPERIMENTAL)""" 688 while also diverged into ctx3. (EXPERIMENTAL)"""
617 ssets = obsutil.successorssets(repo, ctx.node(), closest=True) 694 ssets = obsutil.successorssets(repo, ctx.node(), closest=True)
618 ssets = [[hex(n) for n in ss] for ss in ssets] 695 ssets = [[hex(n) for n in ss] for ss in ssets]
619 696
620 data = [] 697 data = []
621 for ss in ssets: 698 for ss in ssets:
622 h = _hybrid(None, ss, lambda x: {'ctx': repo[x]}, 699 h = _hybrid(
623 lambda x: scmutil.formatchangeid(repo[x])) 700 None,
701 ss,
702 lambda x: {'ctx': repo[x]},
703 lambda x: scmutil.formatchangeid(repo[x]),
704 )
624 data.append(h) 705 data.append(h)
625 706
626 # Format the successorssets 707 # Format the successorssets
627 def render(d): 708 def render(d):
628 return templateutil.stringify(context, mapping, d) 709 return templateutil.stringify(context, mapping, d)
629 710
630 def gen(data): 711 def gen(data):
631 yield "; ".join(render(d) for d in data) 712 yield "; ".join(render(d) for d in data)
632 713
633 return _hybrid(gen(data), data, lambda x: {'successorset': x}, 714 return _hybrid(
634 pycompat.identity) 715 gen(data), data, lambda x: {'successorset': x}, pycompat.identity
716 )
717
635 718
636 @templatekeyword("succsandmarkers", requires={'repo', 'ctx'}) 719 @templatekeyword("succsandmarkers", requires={'repo', 'ctx'})
637 def showsuccsandmarkers(context, mapping): 720 def showsuccsandmarkers(context, mapping):
638 """Returns a list of dict for each final successor of ctx. The dict 721 """Returns a list of dict for each final successor of ctx. The dict
639 contains successors node id in "successors" keys and the list of 722 contains successors node id in "successors" keys and the list of
653 for i in values: 736 for i in values:
654 # Format successors 737 # Format successors
655 successors = i['successors'] 738 successors = i['successors']
656 739
657 successors = [hex(n) for n in successors] 740 successors = [hex(n) for n in successors]
658 successors = _hybrid(None, successors, 741 successors = _hybrid(
659 lambda x: {'ctx': repo[x]}, 742 None,
660 lambda x: scmutil.formatchangeid(repo[x])) 743 successors,
744 lambda x: {'ctx': repo[x]},
745 lambda x: scmutil.formatchangeid(repo[x]),
746 )
661 747
662 # Format markers 748 # Format markers
663 finalmarkers = [] 749 finalmarkers = []
664 for m in i['markers']: 750 for m in i['markers']:
665 hexprec = hex(m[0]) 751 hexprec = hex(m[0])
672 758
673 data.append({'successors': successors, 'markers': finalmarkers}) 759 data.append({'successors': successors, 'markers': finalmarkers})
674 760
675 return templateutil.mappinglist(data) 761 return templateutil.mappinglist(data)
676 762
763
677 @templatekeyword('p1', requires={'ctx'}) 764 @templatekeyword('p1', requires={'ctx'})
678 def showp1(context, mapping): 765 def showp1(context, mapping):
679 """Changeset. The changeset's first parent. ``{p1.rev}`` for the revision 766 """Changeset. The changeset's first parent. ``{p1.rev}`` for the revision
680 number, and ``{p1.node}`` for the identification hash.""" 767 number, and ``{p1.node}`` for the identification hash."""
681 ctx = context.resource(mapping, 'ctx') 768 ctx = context.resource(mapping, 'ctx')
682 return templateutil.mappingdict({'ctx': ctx.p1()}, tmpl=_changeidtmpl) 769 return templateutil.mappingdict({'ctx': ctx.p1()}, tmpl=_changeidtmpl)
683 770
771
684 @templatekeyword('p2', requires={'ctx'}) 772 @templatekeyword('p2', requires={'ctx'})
685 def showp2(context, mapping): 773 def showp2(context, mapping):
686 """Changeset. The changeset's second parent. ``{p2.rev}`` for the revision 774 """Changeset. The changeset's second parent. ``{p2.rev}`` for the revision
687 number, and ``{p2.node}`` for the identification hash.""" 775 number, and ``{p2.node}`` for the identification hash."""
688 ctx = context.resource(mapping, 'ctx') 776 ctx = context.resource(mapping, 'ctx')
689 return templateutil.mappingdict({'ctx': ctx.p2()}, tmpl=_changeidtmpl) 777 return templateutil.mappingdict({'ctx': ctx.p2()}, tmpl=_changeidtmpl)
690 778
779
691 @templatekeyword('p1rev', requires={'ctx'}) 780 @templatekeyword('p1rev', requires={'ctx'})
692 def showp1rev(context, mapping): 781 def showp1rev(context, mapping):
693 """Integer. The repository-local revision number of the changeset's 782 """Integer. The repository-local revision number of the changeset's
694 first parent, or -1 if the changeset has no parents. (DEPRECATED)""" 783 first parent, or -1 if the changeset has no parents. (DEPRECATED)"""
695 ctx = context.resource(mapping, 'ctx') 784 ctx = context.resource(mapping, 'ctx')
696 return ctx.p1().rev() 785 return ctx.p1().rev()
697 786
787
698 @templatekeyword('p2rev', requires={'ctx'}) 788 @templatekeyword('p2rev', requires={'ctx'})
699 def showp2rev(context, mapping): 789 def showp2rev(context, mapping):
700 """Integer. The repository-local revision number of the changeset's 790 """Integer. The repository-local revision number of the changeset's
701 second parent, or -1 if the changeset has no second parent. (DEPRECATED)""" 791 second parent, or -1 if the changeset has no second parent. (DEPRECATED)"""
702 ctx = context.resource(mapping, 'ctx') 792 ctx = context.resource(mapping, 'ctx')
703 return ctx.p2().rev() 793 return ctx.p2().rev()
794
704 795
705 @templatekeyword('p1node', requires={'ctx'}) 796 @templatekeyword('p1node', requires={'ctx'})
706 def showp1node(context, mapping): 797 def showp1node(context, mapping):
707 """String. The identification hash of the changeset's first parent, 798 """String. The identification hash of the changeset's first parent,
708 as a 40 digit hexadecimal string. If the changeset has no parents, all 799 as a 40 digit hexadecimal string. If the changeset has no parents, all
709 digits are 0. (DEPRECATED)""" 800 digits are 0. (DEPRECATED)"""
710 ctx = context.resource(mapping, 'ctx') 801 ctx = context.resource(mapping, 'ctx')
711 return ctx.p1().hex() 802 return ctx.p1().hex()
712 803
804
713 @templatekeyword('p2node', requires={'ctx'}) 805 @templatekeyword('p2node', requires={'ctx'})
714 def showp2node(context, mapping): 806 def showp2node(context, mapping):
715 """String. The identification hash of the changeset's second 807 """String. The identification hash of the changeset's second
716 parent, as a 40 digit hexadecimal string. If the changeset has no second 808 parent, as a 40 digit hexadecimal string. If the changeset has no second
717 parent, all digits are 0. (DEPRECATED)""" 809 parent, all digits are 0. (DEPRECATED)"""
718 ctx = context.resource(mapping, 'ctx') 810 ctx = context.resource(mapping, 'ctx')
719 return ctx.p2().hex() 811 return ctx.p2().hex()
720 812
813
721 @templatekeyword('parents', requires={'repo', 'ctx'}) 814 @templatekeyword('parents', requires={'repo', 'ctx'})
722 def showparents(context, mapping): 815 def showparents(context, mapping):
723 """List of strings. The parents of the changeset in "rev:node" 816 """List of strings. The parents of the changeset in "rev:node"
724 format. If the changeset has only one "natural" parent (the predecessor 817 format. If the changeset has only one "natural" parent (the predecessor
725 revision) nothing is shown.""" 818 revision) nothing is shown."""
726 repo = context.resource(mapping, 'repo') 819 repo = context.resource(mapping, 'repo')
727 ctx = context.resource(mapping, 'ctx') 820 ctx = context.resource(mapping, 'ctx')
728 pctxs = scmutil.meaningfulparents(repo, ctx) 821 pctxs = scmutil.meaningfulparents(repo, ctx)
729 prevs = [p.rev() for p in pctxs] 822 prevs = [p.rev() for p in pctxs]
730 parents = [[('rev', p.rev()), 823 parents = [
731 ('node', p.hex()), 824 [('rev', p.rev()), ('node', p.hex()), ('phase', p.phasestr())]
732 ('phase', p.phasestr())] 825 for p in pctxs
733 for p in pctxs] 826 ]
734 f = _showcompatlist(context, mapping, 'parent', parents) 827 f = _showcompatlist(context, mapping, 'parent', parents)
735 return _hybrid(f, prevs, lambda x: {'ctx': repo[x]}, 828 return _hybrid(
736 lambda x: scmutil.formatchangeid(repo[x]), keytype=int) 829 f,
830 prevs,
831 lambda x: {'ctx': repo[x]},
832 lambda x: scmutil.formatchangeid(repo[x]),
833 keytype=int,
834 )
835
737 836
738 @templatekeyword('phase', requires={'ctx'}) 837 @templatekeyword('phase', requires={'ctx'})
739 def showphase(context, mapping): 838 def showphase(context, mapping):
740 """String. The changeset phase name.""" 839 """String. The changeset phase name."""
741 ctx = context.resource(mapping, 'ctx') 840 ctx = context.resource(mapping, 'ctx')
742 return ctx.phasestr() 841 return ctx.phasestr()
743 842
843
744 @templatekeyword('phaseidx', requires={'ctx'}) 844 @templatekeyword('phaseidx', requires={'ctx'})
745 def showphaseidx(context, mapping): 845 def showphaseidx(context, mapping):
746 """Integer. The changeset phase index. (ADVANCED)""" 846 """Integer. The changeset phase index. (ADVANCED)"""
747 ctx = context.resource(mapping, 'ctx') 847 ctx = context.resource(mapping, 'ctx')
748 return ctx.phase() 848 return ctx.phase()
749 849
850
750 @templatekeyword('rev', requires={'ctx'}) 851 @templatekeyword('rev', requires={'ctx'})
751 def showrev(context, mapping): 852 def showrev(context, mapping):
752 """Integer. The repository-local changeset revision number.""" 853 """Integer. The repository-local changeset revision number."""
753 ctx = context.resource(mapping, 'ctx') 854 ctx = context.resource(mapping, 'ctx')
754 return scmutil.intrev(ctx) 855 return scmutil.intrev(ctx)
856
755 857
756 def showrevslist(context, mapping, name, revs): 858 def showrevslist(context, mapping, name, revs):
757 """helper to generate a list of revisions in which a mapped template will 859 """helper to generate a list of revisions in which a mapped template will
758 be evaluated""" 860 be evaluated"""
759 repo = context.resource(mapping, 'repo') 861 repo = context.resource(mapping, 'repo')
760 # revs may be a smartset; don't compute it until f() has to be evaluated 862 # revs may be a smartset; don't compute it until f() has to be evaluated
761 def f(): 863 def f():
762 srevs = ['%d' % r for r in revs] 864 srevs = ['%d' % r for r in revs]
763 return _showcompatlist(context, mapping, name, srevs) 865 return _showcompatlist(context, mapping, name, srevs)
764 return _hybrid(f, revs, 866
765 lambda x: {name: x, 'ctx': repo[x]}, 867 return _hybrid(
766 pycompat.identity, keytype=int) 868 f,
869 revs,
870 lambda x: {name: x, 'ctx': repo[x]},
871 pycompat.identity,
872 keytype=int,
873 )
874
767 875
768 @templatekeyword('subrepos', requires={'ctx'}) 876 @templatekeyword('subrepos', requires={'ctx'})
769 def showsubrepos(context, mapping): 877 def showsubrepos(context, mapping):
770 """List of strings. Updated subrepositories in the changeset.""" 878 """List of strings. Updated subrepositories in the changeset."""
771 ctx = context.resource(mapping, 'ctx') 879 ctx = context.resource(mapping, 'ctx')
774 return compatlist(context, mapping, 'subrepo', []) 882 return compatlist(context, mapping, 'subrepo', [])
775 psubstate = ctx.p1().substate or {} 883 psubstate = ctx.p1().substate or {}
776 subrepos = [] 884 subrepos = []
777 for sub in substate: 885 for sub in substate:
778 if sub not in psubstate or substate[sub] != psubstate[sub]: 886 if sub not in psubstate or substate[sub] != psubstate[sub]:
779 subrepos.append(sub) # modified or newly added in ctx 887 subrepos.append(sub) # modified or newly added in ctx
780 for sub in psubstate: 888 for sub in psubstate:
781 if sub not in substate: 889 if sub not in substate:
782 subrepos.append(sub) # removed in ctx 890 subrepos.append(sub) # removed in ctx
783 return compatlist(context, mapping, 'subrepo', sorted(subrepos)) 891 return compatlist(context, mapping, 'subrepo', sorted(subrepos))
892
784 893
785 # don't remove "showtags" definition, even though namespaces will put 894 # don't remove "showtags" definition, even though namespaces will put
786 # a helper function for "tags" keyword into "keywords" map automatically, 895 # a helper function for "tags" keyword into "keywords" map automatically,
787 # because online help text is built without namespaces initialization 896 # because online help text is built without namespaces initialization
788 @templatekeyword('tags', requires={'repo', 'ctx'}) 897 @templatekeyword('tags', requires={'repo', 'ctx'})
789 def showtags(context, mapping): 898 def showtags(context, mapping):
790 """List of strings. Any tags associated with the changeset.""" 899 """List of strings. Any tags associated with the changeset."""
791 return shownames(context, mapping, 'tags') 900 return shownames(context, mapping, 'tags')
792 901
902
793 @templatekeyword('termwidth', requires={'ui'}) 903 @templatekeyword('termwidth', requires={'ui'})
794 def showtermwidth(context, mapping): 904 def showtermwidth(context, mapping):
795 """Integer. The width of the current terminal.""" 905 """Integer. The width of the current terminal."""
796 ui = context.resource(mapping, 'ui') 906 ui = context.resource(mapping, 'ui')
797 return ui.termwidth() 907 return ui.termwidth()
798 908
909
799 @templatekeyword('user', requires={'ctx'}) 910 @templatekeyword('user', requires={'ctx'})
800 def showuser(context, mapping): 911 def showuser(context, mapping):
801 """String. The unmodified author of the changeset.""" 912 """String. The unmodified author of the changeset."""
802 ctx = context.resource(mapping, 'ctx') 913 ctx = context.resource(mapping, 'ctx')
803 return ctx.user() 914 return ctx.user()
915
804 916
805 @templatekeyword('instabilities', requires={'ctx'}) 917 @templatekeyword('instabilities', requires={'ctx'})
806 def showinstabilities(context, mapping): 918 def showinstabilities(context, mapping):
807 """List of strings. Evolution instabilities affecting the changeset. 919 """List of strings. Evolution instabilities affecting the changeset.
808 (EXPERIMENTAL) 920 (EXPERIMENTAL)
809 """ 921 """
810 ctx = context.resource(mapping, 'ctx') 922 ctx = context.resource(mapping, 'ctx')
811 return compatlist(context, mapping, 'instability', ctx.instabilities(), 923 return compatlist(
812 plural='instabilities') 924 context,
925 mapping,
926 'instability',
927 ctx.instabilities(),
928 plural='instabilities',
929 )
930
813 931
814 @templatekeyword('verbosity', requires={'ui'}) 932 @templatekeyword('verbosity', requires={'ui'})
815 def showverbosity(context, mapping): 933 def showverbosity(context, mapping):
816 """String. The current output verbosity in 'debug', 'quiet', 'verbose', 934 """String. The current output verbosity in 'debug', 'quiet', 'verbose',
817 or ''.""" 935 or ''."""
823 return 'quiet' 941 return 'quiet'
824 elif ui.verbose: 942 elif ui.verbose:
825 return 'verbose' 943 return 'verbose'
826 return '' 944 return ''
827 945
946
828 @templatekeyword('whyunstable', requires={'repo', 'ctx'}) 947 @templatekeyword('whyunstable', requires={'repo', 'ctx'})
829 def showwhyunstable(context, mapping): 948 def showwhyunstable(context, mapping):
830 """List of dicts explaining all instabilities of a changeset. 949 """List of dicts explaining all instabilities of a changeset.
831 (EXPERIMENTAL) 950 (EXPERIMENTAL)
832 """ 951 """
839 entries = obsutil.whyunstable(repo, ctx) 958 entries = obsutil.whyunstable(repo, ctx)
840 959
841 for entry in entries: 960 for entry in entries:
842 if entry.get('divergentnodes'): 961 if entry.get('divergentnodes'):
843 dnodes = entry['divergentnodes'] 962 dnodes = entry['divergentnodes']
844 dnhybrid = _hybrid(None, [dnode.hex() for dnode in dnodes], 963 dnhybrid = _hybrid(
845 lambda x: {'ctx': repo[x]}, 964 None,
846 lambda x: formatnode(repo[x])) 965 [dnode.hex() for dnode in dnodes],
966 lambda x: {'ctx': repo[x]},
967 lambda x: formatnode(repo[x]),
968 )
847 entry['divergentnodes'] = dnhybrid 969 entry['divergentnodes'] = dnhybrid
848 970
849 tmpl = ('{instability}:{if(divergentnodes, " ")}{divergentnodes} ' 971 tmpl = (
850 '{reason} {node|short}') 972 '{instability}:{if(divergentnodes, " ")}{divergentnodes} '
973 '{reason} {node|short}'
974 )
851 return templateutil.mappinglist(entries, tmpl=tmpl, sep='\n') 975 return templateutil.mappinglist(entries, tmpl=tmpl, sep='\n')
976
852 977
853 def loadkeyword(ui, extname, registrarobj): 978 def loadkeyword(ui, extname, registrarobj):
854 """Load template keyword from specified registrarobj 979 """Load template keyword from specified registrarobj
855 """ 980 """
856 for name, func in registrarobj._table.iteritems(): 981 for name, func in registrarobj._table.iteritems():
857 keywords[name] = func 982 keywords[name] = func
858 983
984
859 # tell hggettext to extract docstrings from these functions: 985 # tell hggettext to extract docstrings from these functions:
860 i18nfunctions = keywords.values() 986 i18nfunctions = keywords.values()