# HG changeset patch # User Vadim Gelfer # Date 1141081645 28800 # Node ID 37b9f80a5fbbd2802411155dabc75966d0475862 # Parent be71c04d62c0e5c74548ce4fef0f56a306a2b1e2 add doc comments to template code. diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -338,15 +338,20 @@ def trimuser(ui, name, rev, revcache): return user class changeset_templater(object): + '''use templater module to format changeset information.''' + def __init__(self, ui, repo, mapfile): self.t = templater.templater(mapfile, templater.common_filters) self.ui = ui self.repo = repo def use_template(self, t): + '''set template string to use''' self.t.cache['template'] = t def write(self, thing): + '''write expanded template. + uses in-order recursive traverse of iterators.''' for t in thing: if hasattr(t, '__iter__'): self.write(t) @@ -354,7 +359,7 @@ class changeset_templater(object): self.ui.write(t) def show(self, rev=0, changenode=None, brinfo=None): - """show a single changeset or file revision""" + '''show a single changeset or file revision''' log = self.repo.changelog if changenode is None: changenode = log.node(rev) @@ -364,6 +369,25 @@ class changeset_templater(object): changes = log.read(changenode) def showlist(name, values, plural=None, **args): + '''expand set of values. + name is name of key in template map. + values is list of strings or dicts. + plural is plural of name, if not simply name + 's'. + + expansion works like this, given name 'foo'. + + if values is empty, expand 'no_foos'. + + if 'foo' not in template map, return values as a string, + joined by space. + + expand 'start_foos'. + + for each value, expand 'foo'. if 'last_foo' in template + map, expand it instead of 'foo' for last key. + + expand 'end_foos'. + ''' if plural: names = plural else: names = name + 's' if not values: @@ -371,13 +395,13 @@ class changeset_templater(object): if noname in self.t: yield self.t(noname, **args) return - vargs = args.copy() if name not in self.t: yield ' '.join(values) return startname = 'start_' + names if startname in self.t: yield self.t(startname, **args) + vargs = args.copy() def one(v, tag=name): try: vargs.update(v) @@ -464,12 +488,14 @@ class changeset_templater(object): raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0])) class changeset_printer(object): + '''show changeset information when templating not requested.''' + def __init__(self, ui, repo): self.ui = ui self.repo = repo def show(self, rev=0, changenode=None, brinfo=None): - """show a single changeset or file revision""" + '''show a single changeset or file revision''' log = self.repo.changelog if changenode is None: changenode = log.node(rev) @@ -530,6 +556,9 @@ class changeset_printer(object): self.ui.status("\n") def show_changeset(ui, repo, opts): + '''show one changeset. uses template or regular display. caller + can pass in 'map_file' and 'template' options in opts.''' + tmpl = opts.get('template') if tmpl: tmpl = templater.parsestring(tmpl, quoted=False) diff --git a/mercurial/templater.py b/mercurial/templater.py --- a/mercurial/templater.py +++ b/mercurial/templater.py @@ -1,3 +1,10 @@ +# templater.py - template expansion for output +# +# Copyright 2005, 2006 Matt Mackall +# +# This software may be used and distributed according to the terms +# of the GNU General Public License, incorporated herein by reference. + import re from demandload import demandload from i18n import gettext as _ @@ -12,6 +19,8 @@ esctable = { } def parsestring(s, quoted=True): + '''parse a string using simple c-like syntax. + string must be in quotes if quoted is True.''' fp = cStringIO.StringIO() if quoted: first = s[0] @@ -31,7 +40,30 @@ def parsestring(s, quoted=True): return fp.getvalue() class templater(object): + '''template expansion engine. + + template expansion works like this. a map file contains key=value + pairs. if value is quoted, it is treated as string. otherwise, it + is treated as name of template file. + + templater is asked to expand a key in map. it looks up key, and + looks for atrings like this: {foo}. it expands {foo} by looking up + foo in map, and substituting it. expansion is recursive: it stops + when there is no more {foo} to replace. + + expansion also allows formatting and filtering. + + format uses key to expand each item in list. syntax is + {key%format}. + + filter uses function to transform value. syntax is + {key|filter1|filter2|...}.''' + def __init__(self, mapfile, filters={}, defaults={}): + '''set up template engine. + mapfile is name of file to read map definitions from. + filters is dict of functions. each transforms a value into another. + defaults is dict of default map definitions.''' self.mapfile = mapfile or 'template' self.cache = {} self.map = {} @@ -64,6 +96,9 @@ class templater(object): return key in self.cache def __call__(self, t, **map): + '''perform expansion. + t is name of map element to expand. + map is added elements to use during expansion.''' m = self.defaults.copy() m.update(map) try: @@ -127,7 +162,9 @@ agescales = [("second", 1), agescales.reverse() -def age(x): +def age(date): + '''turn a (timestamp, tzoff) tuple into an age string.''' + def plural(t, c): if c == 1: return t @@ -136,7 +173,7 @@ def age(x): return "%d %s" % (c, plural(t, c)) now = time.time() - then = x[0] + then = date[0] delta = max(1, int(now - then)) for t, s in agescales: @@ -145,15 +182,18 @@ def age(x): return fmt(t, n) def isodate(date): + '''turn a (timestamp, tzoff) tuple into an iso 8631 date.''' return util.datestr(date, format='%Y-%m-%d %H:%M') def nl2br(text): + '''replace raw newlines with xhtml line breaks.''' return text.replace('\n', '
\n') def obfuscate(text): return ''.join(['&#%d;' % ord(c) for c in text]) def domain(author): + '''get domain of author, or empty string if none.''' f = author.find('@') if f == -1: return '' author = author[f+1:] @@ -162,6 +202,7 @@ def domain(author): return author def person(author): + '''get name of author, or else username.''' f = author.find('<') if f == -1: return util.shortuser(author) return author[:f].rstrip() @@ -185,6 +226,8 @@ common_filters = { } def templatepath(name=None): + '''return location of template file or directory (if no name). + returns None if not found.''' for f in 'templates', '../templates': fl = f.split('/') if name: fl.append(name)