comparison mercurial/cmdutil.py @ 3646:b4ad640a3bcf

templates: move changeset templating bits to cmdutils
author Matt Mackall <mpm@selenic.com>
date Mon, 13 Nov 2006 13:26:57 -0600
parents 730ca93ed788
children b984dcb1df71
comparison
equal deleted inserted replaced
3645:b2c47652e8e3 3646:b4ad640a3bcf
6 # of the GNU General Public License, incorporated herein by reference. 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 from demandload import demandload 8 from demandload import demandload
9 from node import * 9 from node import *
10 from i18n import gettext as _ 10 from i18n import gettext as _
11 demandload(globals(), 'mdiff util')
12 demandload(globals(), 'os sys') 11 demandload(globals(), 'os sys')
12 demandload(globals(), 'mdiff util templater cStringIO')
13 13
14 revrangesep = ':' 14 revrangesep = ':'
15 15
16 def revpair(ui, repo, revs): 16 def revpair(ui, repo, revs):
17 '''return pair of nodes, given list of revisions. second item can 17 '''return pair of nodes, given list of revisions. second item can
193 repo.ui.status(_('recording removal of %s as rename to %s ' 193 repo.ui.status(_('recording removal of %s as rename to %s '
194 '(%d%% similar)\n') % 194 '(%d%% similar)\n') %
195 (oldrel, newrel, score * 100)) 195 (oldrel, newrel, score * 100))
196 if not dry_run: 196 if not dry_run:
197 repo.copy(old, new, wlock=wlock) 197 repo.copy(old, new, wlock=wlock)
198
199 class changeset_printer(object):
200 '''show changeset information when templating not requested.'''
201
202 def __init__(self, ui, repo):
203 self.ui = ui
204 self.repo = repo
205
206 def show(self, rev=0, changenode=None, brinfo=None, copies=None):
207 '''show a single changeset or file revision'''
208 log = self.repo.changelog
209 if changenode is None:
210 changenode = log.node(rev)
211 elif not rev:
212 rev = log.rev(changenode)
213
214 if self.ui.quiet:
215 self.ui.write("%d:%s\n" % (rev, short(changenode)))
216 return
217
218 changes = log.read(changenode)
219 date = util.datestr(changes[2])
220 extra = changes[5]
221 branch = extra.get("branch")
222
223 hexfunc = self.ui.debugflag and hex or short
224
225 parents = log.parentrevs(rev)
226 if not self.ui.debugflag:
227 if parents[1] == nullrev:
228 if parents[0] >= rev - 1:
229 parents = []
230 else:
231 parents = [parents[0]]
232 parents = [(p, hexfunc(log.node(p))) for p in parents]
233
234 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
235
236 if branch:
237 self.ui.write(_("branch: %s\n") % branch)
238 for tag in self.repo.nodetags(changenode):
239 self.ui.write(_("tag: %s\n") % tag)
240 for parent in parents:
241 self.ui.write(_("parent: %d:%s\n") % parent)
242
243 if brinfo and changenode in brinfo:
244 br = brinfo[changenode]
245 self.ui.write(_("branch: %s\n") % " ".join(br))
246
247 if self.ui.debugflag:
248 self.ui.write(_("manifest: %d:%s\n") %
249 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
250 self.ui.write(_("user: %s\n") % changes[1])
251 self.ui.write(_("date: %s\n") % date)
252
253 if self.ui.debugflag:
254 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
255 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
256 files):
257 if value:
258 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
259 elif changes[3] and self.ui.verbose:
260 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
261 if copies and self.ui.verbose:
262 copies = ['%s (%s)' % c for c in copies]
263 self.ui.write(_("copies: %s\n") % ' '.join(copies))
264
265 if extra and self.ui.debugflag:
266 extraitems = extra.items()
267 extraitems.sort()
268 for key, value in extraitems:
269 self.ui.write(_("extra: %s=%s\n")
270 % (key, value.encode('string_escape')))
271
272 description = changes[4].strip()
273 if description:
274 if self.ui.verbose:
275 self.ui.write(_("description:\n"))
276 self.ui.write(description)
277 self.ui.write("\n\n")
278 else:
279 self.ui.write(_("summary: %s\n") %
280 description.splitlines()[0])
281 self.ui.write("\n")
282
283 class changeset_templater(object):
284 '''format changeset information.'''
285
286 def __init__(self, ui, repo, mapfile, dest=None):
287 self.t = templater.templater(mapfile, templater.common_filters,
288 cache={'parent': '{rev}:{node|short} ',
289 'manifest': '{rev}:{node|short}',
290 'filecopy': '{name} ({source})'})
291 self.ui = ui
292 self.dest = dest
293 self.repo = repo
294
295 def use_template(self, t):
296 '''set template string to use'''
297 self.t.cache['changeset'] = t
298
299 def show(self, rev=0, changenode=None, brinfo=None, copies=[], **props):
300 '''show a single changeset or file revision'''
301 log = self.repo.changelog
302 if changenode is None:
303 changenode = log.node(rev)
304 elif not rev:
305 rev = log.rev(changenode)
306
307 changes = log.read(changenode)
308
309 def showlist(name, values, plural=None, **args):
310 '''expand set of values.
311 name is name of key in template map.
312 values is list of strings or dicts.
313 plural is plural of name, if not simply name + 's'.
314
315 expansion works like this, given name 'foo'.
316
317 if values is empty, expand 'no_foos'.
318
319 if 'foo' not in template map, return values as a string,
320 joined by space.
321
322 expand 'start_foos'.
323
324 for each value, expand 'foo'. if 'last_foo' in template
325 map, expand it instead of 'foo' for last key.
326
327 expand 'end_foos'.
328 '''
329 if plural: names = plural
330 else: names = name + 's'
331 if not values:
332 noname = 'no_' + names
333 if noname in self.t:
334 yield self.t(noname, **args)
335 return
336 if name not in self.t:
337 if isinstance(values[0], str):
338 yield ' '.join(values)
339 else:
340 for v in values:
341 yield dict(v, **args)
342 return
343 startname = 'start_' + names
344 if startname in self.t:
345 yield self.t(startname, **args)
346 vargs = args.copy()
347 def one(v, tag=name):
348 try:
349 vargs.update(v)
350 except (AttributeError, ValueError):
351 try:
352 for a, b in v:
353 vargs[a] = b
354 except ValueError:
355 vargs[name] = v
356 return self.t(tag, **vargs)
357 lastname = 'last_' + name
358 if lastname in self.t:
359 last = values.pop()
360 else:
361 last = None
362 for v in values:
363 yield one(v)
364 if last is not None:
365 yield one(last, tag=lastname)
366 endname = 'end_' + names
367 if endname in self.t:
368 yield self.t(endname, **args)
369
370 def showbranches(**args):
371 branch = changes[5].get("branch")
372 if branch:
373 yield showlist('branch', [branch], plural='branches', **args)
374 # add old style branches if requested
375 if brinfo and changenode in brinfo:
376 yield showlist('branch', brinfo[changenode],
377 plural='branches', **args)
378
379 def showparents(**args):
380 parents = [[('rev', log.rev(p)), ('node', hex(p))]
381 for p in log.parents(changenode)
382 if self.ui.debugflag or p != nullid]
383 if (not self.ui.debugflag and len(parents) == 1 and
384 parents[0][0][1] == rev - 1):
385 return
386 return showlist('parent', parents, **args)
387
388 def showtags(**args):
389 return showlist('tag', self.repo.nodetags(changenode), **args)
390
391 def showextras(**args):
392 extras = changes[5].items()
393 extras.sort()
394 for key, value in extras:
395 args = args.copy()
396 args.update(dict(key=key, value=value))
397 yield self.t('extra', **args)
398
399 def showcopies(**args):
400 c = [{'name': x[0], 'source': x[1]} for x in copies]
401 return showlist('file_copy', c, plural='file_copies', **args)
402
403 if self.ui.debugflag:
404 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
405 def showfiles(**args):
406 return showlist('file', files[0], **args)
407 def showadds(**args):
408 return showlist('file_add', files[1], **args)
409 def showdels(**args):
410 return showlist('file_del', files[2], **args)
411 def showmanifest(**args):
412 args = args.copy()
413 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
414 node=hex(changes[0])))
415 return self.t('manifest', **args)
416 else:
417 def showfiles(**args):
418 yield showlist('file', changes[3], **args)
419 showadds = ''
420 showdels = ''
421 showmanifest = ''
422
423 defprops = {
424 'author': changes[1],
425 'branches': showbranches,
426 'date': changes[2],
427 'desc': changes[4],
428 'file_adds': showadds,
429 'file_dels': showdels,
430 'files': showfiles,
431 'file_copies': showcopies,
432 'manifest': showmanifest,
433 'node': hex(changenode),
434 'parents': showparents,
435 'rev': rev,
436 'tags': showtags,
437 'extras': showextras,
438 }
439 props = props.copy()
440 props.update(defprops)
441
442 try:
443 dest = self.dest or self.ui
444 if self.ui.debugflag and 'header_debug' in self.t:
445 key = 'header_debug'
446 elif self.ui.quiet and 'header_quiet' in self.t:
447 key = 'header_quiet'
448 elif self.ui.verbose and 'header_verbose' in self.t:
449 key = 'header_verbose'
450 elif 'header' in self.t:
451 key = 'header'
452 else:
453 key = ''
454 if key:
455 dest.write_header(templater.stringify(self.t(key, **props)))
456 if self.ui.debugflag and 'changeset_debug' in self.t:
457 key = 'changeset_debug'
458 elif self.ui.quiet and 'changeset_quiet' in self.t:
459 key = 'changeset_quiet'
460 elif self.ui.verbose and 'changeset_verbose' in self.t:
461 key = 'changeset_verbose'
462 else:
463 key = 'changeset'
464 dest.write(templater.stringify(self.t(key, **props)))
465 except KeyError, inst:
466 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
467 inst.args[0]))
468 except SyntaxError, inst:
469 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
470
471 class stringio(object):
472 '''wrap cStringIO for use by changeset_templater.'''
473 def __init__(self):
474 self.fp = cStringIO.StringIO()
475
476 def write(self, *args):
477 for a in args:
478 self.fp.write(a)
479
480 write_header = write
481
482 def __getattr__(self, key):
483 return getattr(self.fp, key)
484
485 def show_changeset(ui, repo, opts):
486 """show one changeset using template or regular display.
487
488 Display format will be the first non-empty hit of:
489 1. option 'template'
490 2. option 'style'
491 3. [ui] setting 'logtemplate'
492 4. [ui] setting 'style'
493 If all of these values are either the unset or the empty string,
494 regular display via changeset_printer() is done.
495 """
496 # options
497 tmpl = opts.get('template')
498 mapfile = None
499 if tmpl:
500 tmpl = templater.parsestring(tmpl, quoted=False)
501 else:
502 mapfile = opts.get('style')
503 # ui settings
504 if not mapfile:
505 tmpl = ui.config('ui', 'logtemplate')
506 if tmpl:
507 tmpl = templater.parsestring(tmpl)
508 else:
509 mapfile = ui.config('ui', 'style')
510
511 if tmpl or mapfile:
512 if mapfile:
513 if not os.path.split(mapfile)[0]:
514 mapname = (templater.templatepath('map-cmdline.' + mapfile)
515 or templater.templatepath(mapfile))
516 if mapname: mapfile = mapname
517 try:
518 t = changeset_templater(ui, repo, mapfile)
519 except SyntaxError, inst:
520 raise util.Abort(inst.args[0])
521 if tmpl: t.use_template(tmpl)
522 return t
523 return changeset_printer(ui, repo)
524