diff --git a/mercurial/templater.py b/mercurial/templater.py --- a/mercurial/templater.py +++ b/mercurial/templater.py @@ -8,7 +8,7 @@ import re from demandload import demandload from i18n import gettext as _ -demandload(globals(), "cStringIO cgi sys os time urllib util") +demandload(globals(), "cStringIO cgi re sys os time urllib util textwrap") esctable = { '\\': '\\', @@ -181,8 +181,43 @@ def age(date): if n >= 2 or s == 1: return fmt(t, n) +def stringify(thing): + '''turn nested template iterator into string.''' + cs = cStringIO.StringIO() + def walk(things): + for t in things: + if hasattr(t, '__iter__'): + walk(t) + else: + cs.write(t) + walk(thing) + return cs.getvalue() + +para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M) +space_re = re.compile(r' +') + +def fill(text, width): + '''fill many paragraphs.''' + def findparas(): + start = 0 + while True: + m = para_re.search(text, start) + if not m: + w = len(text) + while w > start and text[w-1].isspace(): w -= 1 + yield text[start:w], text[w:] + break + yield text[start:m.start(0)], m.group(1) + start = m.end(1) + + fp = cStringIO.StringIO() + for para, rest in findparas(): + fp.write(space_re.sub(' ', textwrap.fill(para, width))) + fp.write(rest) + return fp.getvalue() + def isodate(date): - '''turn a (timestamp, tzoff) tuple into an iso 8631 date.''' + '''turn a (timestamp, tzoff) tuple into an iso 8631 date and time.''' return util.datestr(date, format='%Y-%m-%d %H:%M') def nl2br(text): @@ -201,25 +236,54 @@ def domain(author): if f >= 0: author = author[:f] return author +def email(author): + '''get email of author.''' + r = author.find('>') + if r == -1: r = None + return author[author.find('<')+1:r] + def person(author): '''get name of author, or else username.''' f = author.find('<') if f == -1: return util.shortuser(author) return author[:f].rstrip() +def shortdate(date): + '''turn (timestamp, tzoff) tuple into iso 8631 date.''' + return util.datestr(date, format='%Y-%m-%d', timezone=False) + +def indent(text, prefix): + '''indent each non-empty line of text after first with prefix.''' + fp = cStringIO.StringIO() + lines = text.splitlines() + num_lines = len(lines) + for i in xrange(num_lines): + l = lines[i] + if i and l.strip(): fp.write(prefix) + fp.write(l) + if i < num_lines - 1 or text.endswith('\n'): + fp.write('\n') + return fp.getvalue() + common_filters = { "addbreaks": nl2br, "age": age, "date": lambda x: util.datestr(x), "domain": domain, + "email": email, "escape": lambda x: cgi.escape(x, True), + "fill68": lambda x: fill(x, width=68), + "fill76": lambda x: fill(x, width=76), "firstline": lambda x: x.splitlines(1)[0].rstrip('\r\n'), + "tabindent": lambda x: indent(x, '\t'), "isodate": isodate, "obfuscate": obfuscate, "permissions": lambda x: x and "-rwxr-xr-x" or "-rw-r--r--", "person": person, "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S"), "short": lambda x: x[:12], + "shortdate": shortdate, + "stringify": stringify, "strip": lambda x: x.strip(), "urlescape": lambda x: urllib.quote(x), "user": lambda x: util.shortuser(x), diff --git a/mercurial/ui.py b/mercurial/ui.py --- a/mercurial/ui.py +++ b/mercurial/ui.py @@ -8,7 +8,7 @@ import ConfigParser from i18n import gettext as _ from demandload import * -demandload(globals(), "os re socket sys util tempfile") +demandload(globals(), "errno os re socket sys tempfile util") class ui(object): def __init__(self, verbose=False, debug=False, quiet=False, @@ -179,9 +179,13 @@ class ui(object): sys.stdout.write(str(a)) def write_err(self, *args): - if not sys.stdout.closed: sys.stdout.flush() - for a in args: - sys.stderr.write(str(a)) + try: + if not sys.stdout.closed: sys.stdout.flush() + for a in args: + sys.stderr.write(str(a)) + except IOError, inst: + if inst.errno != errno.EPIPE: + raise def flush(self): try: diff --git a/mercurial/util.py b/mercurial/util.py --- a/mercurial/util.py +++ b/mercurial/util.py @@ -751,15 +751,16 @@ def makedate(): tz = time.timezone return time.mktime(lt), tz -def datestr(date=None, format='%a %b %d %H:%M:%S %Y'): +def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True): """represent a (unixtime, offset) tuple as a localized time. unixtime is seconds since the epoch, and offset is the time zone's - number of seconds away from UTC.""" + number of seconds away from UTC. if timezone is false, do not + append time zone to string.""" t, tz = date or makedate() - return ("%s %+03d%02d" % - (time.strftime(format, time.gmtime(float(t) - tz)), - -tz / 3600, - ((-tz % 3600) / 60))) + s = time.strftime(format, time.gmtime(float(t) - tz)) + if timezone: + s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60)) + return s def shortuser(user): """Return a short representation of a user name or email address.""" diff --git a/templates/map-cmdline.changelog b/templates/map-cmdline.changelog new file mode 100644 --- /dev/null +++ b/templates/map-cmdline.changelog @@ -0,0 +1,15 @@ +changeset = '{date|shortdate} {author|person} <{author|email}> ({node|short}{tags})\n\n\t* {files|stringify|fill68|tabindent}{desc|fill68|tabindent|strip}\n\n' +changeset_quiet = '{date|shortdate} {author|person} <{author|email}>\n\n\t* {desc|firstline|fill68|tabindent|strip}\n\n' +changeset_verbose = '{date|isodate} {author|person} <{author|email}> ({node|short}{tags})\n\n\t* {file_adds|stringify|fill68|tabindent}{file_dels|stringify|fill68|tabindent}{files|stringify|fill68|tabindent}{desc|fill68|tabindent|strip}\n\n' +start_tags = ' [' +tag = '{tag}, ' +last_tag = '{tag}]' +start_files = '(' +file = '{file}, ' +last_file = '{file}):\n\t' +start_file_adds = '(' +file_add = '{file_add}, ' +last_file_add = '{file_add}): new file.\n* ' +start_file_dels = '(' +file_del = '{file_del}, ' +last_file_del = '{file_del}): deleted file.\n* '