##// END OF EJS Templates
Add new branch info to templater and use it in map-cmdline.default....
Thomas Arendsen Hein -
r3461:451ec905 default
parent child Browse files
Show More
@@ -1,545 +1,546 b''
1 # templater.py - template expansion for output
1 # templater.py - template expansion for output
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
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 i18n import gettext as _
9 from i18n import gettext as _
10 from node import *
10 from node import *
11 demandload(globals(), "cStringIO cgi re sys os time urllib util textwrap")
11 demandload(globals(), "cStringIO cgi re sys os time urllib util textwrap")
12
12
13 esctable = {
13 esctable = {
14 '\\': '\\',
14 '\\': '\\',
15 'r': '\r',
15 'r': '\r',
16 't': '\t',
16 't': '\t',
17 'n': '\n',
17 'n': '\n',
18 'v': '\v',
18 'v': '\v',
19 }
19 }
20
20
21 def parsestring(s, quoted=True):
21 def parsestring(s, quoted=True):
22 '''parse a string using simple c-like syntax.
22 '''parse a string using simple c-like syntax.
23 string must be in quotes if quoted is True.'''
23 string must be in quotes if quoted is True.'''
24 fp = cStringIO.StringIO()
24 fp = cStringIO.StringIO()
25 if quoted:
25 if quoted:
26 first = s[0]
26 first = s[0]
27 if len(s) < 2: raise SyntaxError(_('string too short'))
27 if len(s) < 2: raise SyntaxError(_('string too short'))
28 if first not in "'\"": raise SyntaxError(_('invalid quote'))
28 if first not in "'\"": raise SyntaxError(_('invalid quote'))
29 if s[-1] != first: raise SyntaxError(_('unmatched quotes'))
29 if s[-1] != first: raise SyntaxError(_('unmatched quotes'))
30 s = s[1:-1]
30 s = s[1:-1]
31 escape = False
31 escape = False
32 for c in s:
32 for c in s:
33 if escape:
33 if escape:
34 fp.write(esctable.get(c, c))
34 fp.write(esctable.get(c, c))
35 escape = False
35 escape = False
36 elif c == '\\': escape = True
36 elif c == '\\': escape = True
37 elif quoted and c == first: raise SyntaxError(_('string ends early'))
37 elif quoted and c == first: raise SyntaxError(_('string ends early'))
38 else: fp.write(c)
38 else: fp.write(c)
39 if escape: raise SyntaxError(_('unterminated escape'))
39 if escape: raise SyntaxError(_('unterminated escape'))
40 return fp.getvalue()
40 return fp.getvalue()
41
41
42 class templater(object):
42 class templater(object):
43 '''template expansion engine.
43 '''template expansion engine.
44
44
45 template expansion works like this. a map file contains key=value
45 template expansion works like this. a map file contains key=value
46 pairs. if value is quoted, it is treated as string. otherwise, it
46 pairs. if value is quoted, it is treated as string. otherwise, it
47 is treated as name of template file.
47 is treated as name of template file.
48
48
49 templater is asked to expand a key in map. it looks up key, and
49 templater is asked to expand a key in map. it looks up key, and
50 looks for atrings like this: {foo}. it expands {foo} by looking up
50 looks for atrings like this: {foo}. it expands {foo} by looking up
51 foo in map, and substituting it. expansion is recursive: it stops
51 foo in map, and substituting it. expansion is recursive: it stops
52 when there is no more {foo} to replace.
52 when there is no more {foo} to replace.
53
53
54 expansion also allows formatting and filtering.
54 expansion also allows formatting and filtering.
55
55
56 format uses key to expand each item in list. syntax is
56 format uses key to expand each item in list. syntax is
57 {key%format}.
57 {key%format}.
58
58
59 filter uses function to transform value. syntax is
59 filter uses function to transform value. syntax is
60 {key|filter1|filter2|...}.'''
60 {key|filter1|filter2|...}.'''
61
61
62 def __init__(self, mapfile, filters={}, defaults={}, cache={}):
62 def __init__(self, mapfile, filters={}, defaults={}, cache={}):
63 '''set up template engine.
63 '''set up template engine.
64 mapfile is name of file to read map definitions from.
64 mapfile is name of file to read map definitions from.
65 filters is dict of functions. each transforms a value into another.
65 filters is dict of functions. each transforms a value into another.
66 defaults is dict of default map definitions.'''
66 defaults is dict of default map definitions.'''
67 self.mapfile = mapfile or 'template'
67 self.mapfile = mapfile or 'template'
68 self.cache = cache.copy()
68 self.cache = cache.copy()
69 self.map = {}
69 self.map = {}
70 self.base = (mapfile and os.path.dirname(mapfile)) or ''
70 self.base = (mapfile and os.path.dirname(mapfile)) or ''
71 self.filters = filters
71 self.filters = filters
72 self.defaults = defaults
72 self.defaults = defaults
73
73
74 if not mapfile:
74 if not mapfile:
75 return
75 return
76 i = 0
76 i = 0
77 for l in file(mapfile):
77 for l in file(mapfile):
78 l = l.strip()
78 l = l.strip()
79 i += 1
79 i += 1
80 if not l or l[0] in '#;': continue
80 if not l or l[0] in '#;': continue
81 m = re.match(r'([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.+)$', l)
81 m = re.match(r'([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.+)$', l)
82 if m:
82 if m:
83 key, val = m.groups()
83 key, val = m.groups()
84 if val[0] in "'\"":
84 if val[0] in "'\"":
85 try:
85 try:
86 self.cache[key] = parsestring(val)
86 self.cache[key] = parsestring(val)
87 except SyntaxError, inst:
87 except SyntaxError, inst:
88 raise SyntaxError('%s:%s: %s' %
88 raise SyntaxError('%s:%s: %s' %
89 (mapfile, i, inst.args[0]))
89 (mapfile, i, inst.args[0]))
90 else:
90 else:
91 self.map[key] = os.path.join(self.base, val)
91 self.map[key] = os.path.join(self.base, val)
92 else:
92 else:
93 raise SyntaxError(_("%s:%s: parse error") % (mapfile, i))
93 raise SyntaxError(_("%s:%s: parse error") % (mapfile, i))
94
94
95 def __contains__(self, key):
95 def __contains__(self, key):
96 return key in self.cache
96 return key in self.cache
97
97
98 def __call__(self, t, **map):
98 def __call__(self, t, **map):
99 '''perform expansion.
99 '''perform expansion.
100 t is name of map element to expand.
100 t is name of map element to expand.
101 map is added elements to use during expansion.'''
101 map is added elements to use during expansion.'''
102 m = self.defaults.copy()
102 m = self.defaults.copy()
103 m.update(map)
103 m.update(map)
104 try:
104 try:
105 tmpl = self.cache[t]
105 tmpl = self.cache[t]
106 except KeyError:
106 except KeyError:
107 try:
107 try:
108 tmpl = self.cache[t] = file(self.map[t]).read()
108 tmpl = self.cache[t] = file(self.map[t]).read()
109 except IOError, inst:
109 except IOError, inst:
110 raise IOError(inst.args[0], _('template file %s: %s') %
110 raise IOError(inst.args[0], _('template file %s: %s') %
111 (self.map[t], inst.args[1]))
111 (self.map[t], inst.args[1]))
112 return self.template(tmpl, self.filters, **m)
112 return self.template(tmpl, self.filters, **m)
113
113
114 template_re = re.compile(r"[#{]([a-zA-Z_][a-zA-Z0-9_]*)"
114 template_re = re.compile(r"[#{]([a-zA-Z_][a-zA-Z0-9_]*)"
115 r"((%[a-zA-Z_][a-zA-Z0-9_]*)*)"
115 r"((%[a-zA-Z_][a-zA-Z0-9_]*)*)"
116 r"((\|[a-zA-Z_][a-zA-Z0-9_]*)*)[#}]")
116 r"((\|[a-zA-Z_][a-zA-Z0-9_]*)*)[#}]")
117
117
118 def template(self, tmpl, filters={}, **map):
118 def template(self, tmpl, filters={}, **map):
119 lm = map.copy()
119 lm = map.copy()
120 while tmpl:
120 while tmpl:
121 m = self.template_re.search(tmpl)
121 m = self.template_re.search(tmpl)
122 if m:
122 if m:
123 start, end = m.span(0)
123 start, end = m.span(0)
124 s, e = tmpl[start], tmpl[end - 1]
124 s, e = tmpl[start], tmpl[end - 1]
125 key = m.group(1)
125 key = m.group(1)
126 if ((s == '#' and e != '#') or (s == '{' and e != '}')):
126 if ((s == '#' and e != '#') or (s == '{' and e != '}')):
127 raise SyntaxError(_("'%s'/'%s' mismatch expanding '%s'") %
127 raise SyntaxError(_("'%s'/'%s' mismatch expanding '%s'") %
128 (s, e, key))
128 (s, e, key))
129 if start:
129 if start:
130 yield tmpl[:start]
130 yield tmpl[:start]
131 v = map.get(key, "")
131 v = map.get(key, "")
132 v = callable(v) and v(**map) or v
132 v = callable(v) and v(**map) or v
133
133
134 format = m.group(2)
134 format = m.group(2)
135 fl = m.group(4)
135 fl = m.group(4)
136
136
137 if format:
137 if format:
138 try:
138 try:
139 q = v.__iter__
139 q = v.__iter__
140 except AttributeError:
140 except AttributeError:
141 raise SyntaxError(_("Error expanding '%s%s'")
141 raise SyntaxError(_("Error expanding '%s%s'")
142 % (key, format))
142 % (key, format))
143 for i in q():
143 for i in q():
144 lm.update(i)
144 lm.update(i)
145 yield self(format[1:], **lm)
145 yield self(format[1:], **lm)
146
146
147 v = ""
147 v = ""
148
148
149 elif fl:
149 elif fl:
150 for f in fl.split("|")[1:]:
150 for f in fl.split("|")[1:]:
151 v = filters[f](v)
151 v = filters[f](v)
152
152
153 yield v
153 yield v
154 tmpl = tmpl[end:]
154 tmpl = tmpl[end:]
155 else:
155 else:
156 yield tmpl
156 yield tmpl
157 break
157 break
158
158
159 agescales = [("second", 1),
159 agescales = [("second", 1),
160 ("minute", 60),
160 ("minute", 60),
161 ("hour", 3600),
161 ("hour", 3600),
162 ("day", 3600 * 24),
162 ("day", 3600 * 24),
163 ("week", 3600 * 24 * 7),
163 ("week", 3600 * 24 * 7),
164 ("month", 3600 * 24 * 30),
164 ("month", 3600 * 24 * 30),
165 ("year", 3600 * 24 * 365)]
165 ("year", 3600 * 24 * 365)]
166
166
167 agescales.reverse()
167 agescales.reverse()
168
168
169 def age(date):
169 def age(date):
170 '''turn a (timestamp, tzoff) tuple into an age string.'''
170 '''turn a (timestamp, tzoff) tuple into an age string.'''
171
171
172 def plural(t, c):
172 def plural(t, c):
173 if c == 1:
173 if c == 1:
174 return t
174 return t
175 return t + "s"
175 return t + "s"
176 def fmt(t, c):
176 def fmt(t, c):
177 return "%d %s" % (c, plural(t, c))
177 return "%d %s" % (c, plural(t, c))
178
178
179 now = time.time()
179 now = time.time()
180 then = date[0]
180 then = date[0]
181 delta = max(1, int(now - then))
181 delta = max(1, int(now - then))
182
182
183 for t, s in agescales:
183 for t, s in agescales:
184 n = delta / s
184 n = delta / s
185 if n >= 2 or s == 1:
185 if n >= 2 or s == 1:
186 return fmt(t, n)
186 return fmt(t, n)
187
187
188 def stringify(thing):
188 def stringify(thing):
189 '''turn nested template iterator into string.'''
189 '''turn nested template iterator into string.'''
190 cs = cStringIO.StringIO()
190 cs = cStringIO.StringIO()
191 def walk(things):
191 def walk(things):
192 for t in things:
192 for t in things:
193 if hasattr(t, '__iter__'):
193 if hasattr(t, '__iter__'):
194 walk(t)
194 walk(t)
195 else:
195 else:
196 cs.write(t)
196 cs.write(t)
197 walk(thing)
197 walk(thing)
198 return cs.getvalue()
198 return cs.getvalue()
199
199
200 para_re = None
200 para_re = None
201 space_re = None
201 space_re = None
202
202
203 def fill(text, width):
203 def fill(text, width):
204 '''fill many paragraphs.'''
204 '''fill many paragraphs.'''
205 global para_re, space_re
205 global para_re, space_re
206 if para_re is None:
206 if para_re is None:
207 para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M)
207 para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M)
208 space_re = re.compile(r' +')
208 space_re = re.compile(r' +')
209
209
210 def findparas():
210 def findparas():
211 start = 0
211 start = 0
212 while True:
212 while True:
213 m = para_re.search(text, start)
213 m = para_re.search(text, start)
214 if not m:
214 if not m:
215 w = len(text)
215 w = len(text)
216 while w > start and text[w-1].isspace(): w -= 1
216 while w > start and text[w-1].isspace(): w -= 1
217 yield text[start:w], text[w:]
217 yield text[start:w], text[w:]
218 break
218 break
219 yield text[start:m.start(0)], m.group(1)
219 yield text[start:m.start(0)], m.group(1)
220 start = m.end(1)
220 start = m.end(1)
221
221
222 fp = cStringIO.StringIO()
222 fp = cStringIO.StringIO()
223 for para, rest in findparas():
223 for para, rest in findparas():
224 fp.write(space_re.sub(' ', textwrap.fill(para, width)))
224 fp.write(space_re.sub(' ', textwrap.fill(para, width)))
225 fp.write(rest)
225 fp.write(rest)
226 return fp.getvalue()
226 return fp.getvalue()
227
227
228 def firstline(text):
228 def firstline(text):
229 '''return the first line of text'''
229 '''return the first line of text'''
230 try:
230 try:
231 return text.splitlines(1)[0].rstrip('\r\n')
231 return text.splitlines(1)[0].rstrip('\r\n')
232 except IndexError:
232 except IndexError:
233 return ''
233 return ''
234
234
235 def isodate(date):
235 def isodate(date):
236 '''turn a (timestamp, tzoff) tuple into an iso 8631 date and time.'''
236 '''turn a (timestamp, tzoff) tuple into an iso 8631 date and time.'''
237 return util.datestr(date, format='%Y-%m-%d %H:%M')
237 return util.datestr(date, format='%Y-%m-%d %H:%M')
238
238
239 def hgdate(date):
239 def hgdate(date):
240 '''turn a (timestamp, tzoff) tuple into an hg cset timestamp.'''
240 '''turn a (timestamp, tzoff) tuple into an hg cset timestamp.'''
241 return "%d %d" % date
241 return "%d %d" % date
242
242
243 def nl2br(text):
243 def nl2br(text):
244 '''replace raw newlines with xhtml line breaks.'''
244 '''replace raw newlines with xhtml line breaks.'''
245 return text.replace('\n', '<br/>\n')
245 return text.replace('\n', '<br/>\n')
246
246
247 def obfuscate(text):
247 def obfuscate(text):
248 text = unicode(text, 'utf-8', 'replace')
248 text = unicode(text, 'utf-8', 'replace')
249 return ''.join(['&#%d;' % ord(c) for c in text])
249 return ''.join(['&#%d;' % ord(c) for c in text])
250
250
251 def domain(author):
251 def domain(author):
252 '''get domain of author, or empty string if none.'''
252 '''get domain of author, or empty string if none.'''
253 f = author.find('@')
253 f = author.find('@')
254 if f == -1: return ''
254 if f == -1: return ''
255 author = author[f+1:]
255 author = author[f+1:]
256 f = author.find('>')
256 f = author.find('>')
257 if f >= 0: author = author[:f]
257 if f >= 0: author = author[:f]
258 return author
258 return author
259
259
260 def email(author):
260 def email(author):
261 '''get email of author.'''
261 '''get email of author.'''
262 r = author.find('>')
262 r = author.find('>')
263 if r == -1: r = None
263 if r == -1: r = None
264 return author[author.find('<')+1:r]
264 return author[author.find('<')+1:r]
265
265
266 def person(author):
266 def person(author):
267 '''get name of author, or else username.'''
267 '''get name of author, or else username.'''
268 f = author.find('<')
268 f = author.find('<')
269 if f == -1: return util.shortuser(author)
269 if f == -1: return util.shortuser(author)
270 return author[:f].rstrip()
270 return author[:f].rstrip()
271
271
272 def shortdate(date):
272 def shortdate(date):
273 '''turn (timestamp, tzoff) tuple into iso 8631 date.'''
273 '''turn (timestamp, tzoff) tuple into iso 8631 date.'''
274 return util.datestr(date, format='%Y-%m-%d', timezone=False)
274 return util.datestr(date, format='%Y-%m-%d', timezone=False)
275
275
276 def indent(text, prefix):
276 def indent(text, prefix):
277 '''indent each non-empty line of text after first with prefix.'''
277 '''indent each non-empty line of text after first with prefix.'''
278 fp = cStringIO.StringIO()
278 fp = cStringIO.StringIO()
279 lines = text.splitlines()
279 lines = text.splitlines()
280 num_lines = len(lines)
280 num_lines = len(lines)
281 for i in xrange(num_lines):
281 for i in xrange(num_lines):
282 l = lines[i]
282 l = lines[i]
283 if i and l.strip(): fp.write(prefix)
283 if i and l.strip(): fp.write(prefix)
284 fp.write(l)
284 fp.write(l)
285 if i < num_lines - 1 or text.endswith('\n'):
285 if i < num_lines - 1 or text.endswith('\n'):
286 fp.write('\n')
286 fp.write('\n')
287 return fp.getvalue()
287 return fp.getvalue()
288
288
289 common_filters = {
289 common_filters = {
290 "addbreaks": nl2br,
290 "addbreaks": nl2br,
291 "basename": os.path.basename,
291 "basename": os.path.basename,
292 "age": age,
292 "age": age,
293 "date": lambda x: util.datestr(x),
293 "date": lambda x: util.datestr(x),
294 "domain": domain,
294 "domain": domain,
295 "email": email,
295 "email": email,
296 "escape": lambda x: cgi.escape(x, True),
296 "escape": lambda x: cgi.escape(x, True),
297 "fill68": lambda x: fill(x, width=68),
297 "fill68": lambda x: fill(x, width=68),
298 "fill76": lambda x: fill(x, width=76),
298 "fill76": lambda x: fill(x, width=76),
299 "firstline": firstline,
299 "firstline": firstline,
300 "tabindent": lambda x: indent(x, '\t'),
300 "tabindent": lambda x: indent(x, '\t'),
301 "hgdate": hgdate,
301 "hgdate": hgdate,
302 "isodate": isodate,
302 "isodate": isodate,
303 "obfuscate": obfuscate,
303 "obfuscate": obfuscate,
304 "permissions": lambda x: x and "-rwxr-xr-x" or "-rw-r--r--",
304 "permissions": lambda x: x and "-rwxr-xr-x" or "-rw-r--r--",
305 "person": person,
305 "person": person,
306 "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S"),
306 "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S"),
307 "short": lambda x: x[:12],
307 "short": lambda x: x[:12],
308 "shortdate": shortdate,
308 "shortdate": shortdate,
309 "stringify": stringify,
309 "stringify": stringify,
310 "strip": lambda x: x.strip(),
310 "strip": lambda x: x.strip(),
311 "urlescape": lambda x: urllib.quote(x),
311 "urlescape": lambda x: urllib.quote(x),
312 "user": lambda x: util.shortuser(x),
312 "user": lambda x: util.shortuser(x),
313 }
313 }
314
314
315 def templatepath(name=None):
315 def templatepath(name=None):
316 '''return location of template file or directory (if no name).
316 '''return location of template file or directory (if no name).
317 returns None if not found.'''
317 returns None if not found.'''
318
318
319 # executable version (py2exe) doesn't support __file__
319 # executable version (py2exe) doesn't support __file__
320 if hasattr(sys, 'frozen'):
320 if hasattr(sys, 'frozen'):
321 module = sys.executable
321 module = sys.executable
322 else:
322 else:
323 module = __file__
323 module = __file__
324 for f in 'templates', '../templates':
324 for f in 'templates', '../templates':
325 fl = f.split('/')
325 fl = f.split('/')
326 if name: fl.append(name)
326 if name: fl.append(name)
327 p = os.path.join(os.path.dirname(module), *fl)
327 p = os.path.join(os.path.dirname(module), *fl)
328 if (name and os.path.exists(p)) or os.path.isdir(p):
328 if (name and os.path.exists(p)) or os.path.isdir(p):
329 return os.path.normpath(p)
329 return os.path.normpath(p)
330
330
331 class changeset_templater(object):
331 class changeset_templater(object):
332 '''format changeset information.'''
332 '''format changeset information.'''
333
333
334 def __init__(self, ui, repo, mapfile, dest=None):
334 def __init__(self, ui, repo, mapfile, dest=None):
335 self.t = templater(mapfile, common_filters,
335 self.t = templater(mapfile, common_filters,
336 cache={'parent': '{rev}:{node|short} ',
336 cache={'parent': '{rev}:{node|short} ',
337 'manifest': '{rev}:{node|short}',
337 'manifest': '{rev}:{node|short}',
338 'filecopy': '{name} ({source})'})
338 'filecopy': '{name} ({source})'})
339 self.ui = ui
339 self.ui = ui
340 self.dest = dest
340 self.dest = dest
341 self.repo = repo
341 self.repo = repo
342
342
343 def use_template(self, t):
343 def use_template(self, t):
344 '''set template string to use'''
344 '''set template string to use'''
345 self.t.cache['changeset'] = t
345 self.t.cache['changeset'] = t
346
346
347 def write(self, thing, header=False):
347 def write(self, thing, header=False):
348 '''write expanded template.
348 '''write expanded template.
349 uses in-order recursive traverse of iterators.'''
349 uses in-order recursive traverse of iterators.'''
350 dest = self.dest or self.ui
350 dest = self.dest or self.ui
351 for t in thing:
351 for t in thing:
352 if hasattr(t, '__iter__'):
352 if hasattr(t, '__iter__'):
353 self.write(t, header=header)
353 self.write(t, header=header)
354 elif header:
354 elif header:
355 dest.write_header(t)
355 dest.write_header(t)
356 else:
356 else:
357 dest.write(t)
357 dest.write(t)
358
358
359 def write_header(self, thing):
359 def write_header(self, thing):
360 self.write(thing, header=True)
360 self.write(thing, header=True)
361
361
362 def show(self, rev=0, changenode=None, brinfo=None, changes=None,
362 def show(self, rev=0, changenode=None, brinfo=None, changes=None,
363 copies=[], **props):
363 copies=[], **props):
364 '''show a single changeset or file revision'''
364 '''show a single changeset or file revision'''
365 log = self.repo.changelog
365 log = self.repo.changelog
366 if changenode is None:
366 if changenode is None:
367 changenode = log.node(rev)
367 changenode = log.node(rev)
368 elif not rev:
368 elif not rev:
369 rev = log.rev(changenode)
369 rev = log.rev(changenode)
370 if changes is None:
370 if changes is None:
371 changes = log.read(changenode)
371 changes = log.read(changenode)
372
372
373 def showlist(name, values, plural=None, **args):
373 def showlist(name, values, plural=None, **args):
374 '''expand set of values.
374 '''expand set of values.
375 name is name of key in template map.
375 name is name of key in template map.
376 values is list of strings or dicts.
376 values is list of strings or dicts.
377 plural is plural of name, if not simply name + 's'.
377 plural is plural of name, if not simply name + 's'.
378
378
379 expansion works like this, given name 'foo'.
379 expansion works like this, given name 'foo'.
380
380
381 if values is empty, expand 'no_foos'.
381 if values is empty, expand 'no_foos'.
382
382
383 if 'foo' not in template map, return values as a string,
383 if 'foo' not in template map, return values as a string,
384 joined by space.
384 joined by space.
385
385
386 expand 'start_foos'.
386 expand 'start_foos'.
387
387
388 for each value, expand 'foo'. if 'last_foo' in template
388 for each value, expand 'foo'. if 'last_foo' in template
389 map, expand it instead of 'foo' for last key.
389 map, expand it instead of 'foo' for last key.
390
390
391 expand 'end_foos'.
391 expand 'end_foos'.
392 '''
392 '''
393 if plural: names = plural
393 if plural: names = plural
394 else: names = name + 's'
394 else: names = name + 's'
395 if not values:
395 if not values:
396 noname = 'no_' + names
396 noname = 'no_' + names
397 if noname in self.t:
397 if noname in self.t:
398 yield self.t(noname, **args)
398 yield self.t(noname, **args)
399 return
399 return
400 if name not in self.t:
400 if name not in self.t:
401 if isinstance(values[0], str):
401 if isinstance(values[0], str):
402 yield ' '.join(values)
402 yield ' '.join(values)
403 else:
403 else:
404 for v in values:
404 for v in values:
405 yield dict(v, **args)
405 yield dict(v, **args)
406 return
406 return
407 startname = 'start_' + names
407 startname = 'start_' + names
408 if startname in self.t:
408 if startname in self.t:
409 yield self.t(startname, **args)
409 yield self.t(startname, **args)
410 vargs = args.copy()
410 vargs = args.copy()
411 def one(v, tag=name):
411 def one(v, tag=name):
412 try:
412 try:
413 vargs.update(v)
413 vargs.update(v)
414 except (AttributeError, ValueError):
414 except (AttributeError, ValueError):
415 try:
415 try:
416 for a, b in v:
416 for a, b in v:
417 vargs[a] = b
417 vargs[a] = b
418 except ValueError:
418 except ValueError:
419 vargs[name] = v
419 vargs[name] = v
420 return self.t(tag, **vargs)
420 return self.t(tag, **vargs)
421 lastname = 'last_' + name
421 lastname = 'last_' + name
422 if lastname in self.t:
422 if lastname in self.t:
423 last = values.pop()
423 last = values.pop()
424 else:
424 else:
425 last = None
425 last = None
426 for v in values:
426 for v in values:
427 yield one(v)
427 yield one(v)
428 if last is not None:
428 if last is not None:
429 yield one(last, tag=lastname)
429 yield one(last, tag=lastname)
430 endname = 'end_' + names
430 endname = 'end_' + names
431 if endname in self.t:
431 if endname in self.t:
432 yield self.t(endname, **args)
432 yield self.t(endname, **args)
433
433
434 if brinfo:
434 def showbranches(**args):
435 def showbranches(**args):
435 branch = changes[5].get("branch")
436 if changenode in brinfo:
436 if branch:
437 for x in showlist('branch', brinfo[changenode],
437 yield showlist('branch', [branch], plural='branches', **args)
438 plural='branches', **args):
438 # add old style branches if requested
439 yield x
439 if brinfo and changenode in brinfo:
440 else:
440 for x in showlist('branch', brinfo[changenode],
441 showbranches = ''
441 plural='branches', **args):
442 yield x
442
443
443 if self.ui.debugflag:
444 if self.ui.debugflag:
444 def showmanifest(**args):
445 def showmanifest(**args):
445 args = args.copy()
446 args = args.copy()
446 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
447 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
447 node=hex(changes[0])))
448 node=hex(changes[0])))
448 yield self.t('manifest', **args)
449 yield self.t('manifest', **args)
449 else:
450 else:
450 showmanifest = ''
451 showmanifest = ''
451
452
452 def showparents(**args):
453 def showparents(**args):
453 parents = [[('rev', log.rev(p)), ('node', hex(p))]
454 parents = [[('rev', log.rev(p)), ('node', hex(p))]
454 for p in log.parents(changenode)
455 for p in log.parents(changenode)
455 if self.ui.debugflag or p != nullid]
456 if self.ui.debugflag or p != nullid]
456 if (not self.ui.debugflag and len(parents) == 1 and
457 if (not self.ui.debugflag and len(parents) == 1 and
457 parents[0][0][1] == rev - 1):
458 parents[0][0][1] == rev - 1):
458 return
459 return
459 for x in showlist('parent', parents, **args):
460 for x in showlist('parent', parents, **args):
460 yield x
461 yield x
461
462
462 def showtags(**args):
463 def showtags(**args):
463 for x in showlist('tag', self.repo.nodetags(changenode), **args):
464 for x in showlist('tag', self.repo.nodetags(changenode), **args):
464 yield x
465 yield x
465
466
466 if self.ui.debugflag:
467 if self.ui.debugflag:
467 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
468 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
468 def showfiles(**args):
469 def showfiles(**args):
469 for x in showlist('file', files[0], **args): yield x
470 for x in showlist('file', files[0], **args): yield x
470 def showadds(**args):
471 def showadds(**args):
471 for x in showlist('file_add', files[1], **args): yield x
472 for x in showlist('file_add', files[1], **args): yield x
472 def showdels(**args):
473 def showdels(**args):
473 for x in showlist('file_del', files[2], **args): yield x
474 for x in showlist('file_del', files[2], **args): yield x
474 else:
475 else:
475 def showfiles(**args):
476 def showfiles(**args):
476 for x in showlist('file', changes[3], **args): yield x
477 for x in showlist('file', changes[3], **args): yield x
477 showadds = ''
478 showadds = ''
478 showdels = ''
479 showdels = ''
479
480
480 copies = [{'name': x[0], 'source': x[1]}
481 copies = [{'name': x[0], 'source': x[1]}
481 for x in copies]
482 for x in copies]
482 def showcopies(**args):
483 def showcopies(**args):
483 for x in showlist('file_copy', copies, plural='file_copies',
484 for x in showlist('file_copy', copies, plural='file_copies',
484 **args):
485 **args):
485 yield x
486 yield x
486
487
487 defprops = {
488 defprops = {
488 'author': changes[1],
489 'author': changes[1],
489 'branches': showbranches,
490 'branches': showbranches,
490 'date': changes[2],
491 'date': changes[2],
491 'desc': changes[4],
492 'desc': changes[4],
492 'file_adds': showadds,
493 'file_adds': showadds,
493 'file_dels': showdels,
494 'file_dels': showdels,
494 'files': showfiles,
495 'files': showfiles,
495 'file_copies': showcopies,
496 'file_copies': showcopies,
496 'manifest': showmanifest,
497 'manifest': showmanifest,
497 'node': hex(changenode),
498 'node': hex(changenode),
498 'parents': showparents,
499 'parents': showparents,
499 'rev': rev,
500 'rev': rev,
500 'tags': showtags,
501 'tags': showtags,
501 }
502 }
502 props = props.copy()
503 props = props.copy()
503 props.update(defprops)
504 props.update(defprops)
504
505
505 try:
506 try:
506 if self.ui.debugflag and 'header_debug' in self.t:
507 if self.ui.debugflag and 'header_debug' in self.t:
507 key = 'header_debug'
508 key = 'header_debug'
508 elif self.ui.quiet and 'header_quiet' in self.t:
509 elif self.ui.quiet and 'header_quiet' in self.t:
509 key = 'header_quiet'
510 key = 'header_quiet'
510 elif self.ui.verbose and 'header_verbose' in self.t:
511 elif self.ui.verbose and 'header_verbose' in self.t:
511 key = 'header_verbose'
512 key = 'header_verbose'
512 elif 'header' in self.t:
513 elif 'header' in self.t:
513 key = 'header'
514 key = 'header'
514 else:
515 else:
515 key = ''
516 key = ''
516 if key:
517 if key:
517 self.write_header(self.t(key, **props))
518 self.write_header(self.t(key, **props))
518 if self.ui.debugflag and 'changeset_debug' in self.t:
519 if self.ui.debugflag and 'changeset_debug' in self.t:
519 key = 'changeset_debug'
520 key = 'changeset_debug'
520 elif self.ui.quiet and 'changeset_quiet' in self.t:
521 elif self.ui.quiet and 'changeset_quiet' in self.t:
521 key = 'changeset_quiet'
522 key = 'changeset_quiet'
522 elif self.ui.verbose and 'changeset_verbose' in self.t:
523 elif self.ui.verbose and 'changeset_verbose' in self.t:
523 key = 'changeset_verbose'
524 key = 'changeset_verbose'
524 else:
525 else:
525 key = 'changeset'
526 key = 'changeset'
526 self.write(self.t(key, **props))
527 self.write(self.t(key, **props))
527 except KeyError, inst:
528 except KeyError, inst:
528 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
529 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
529 inst.args[0]))
530 inst.args[0]))
530 except SyntaxError, inst:
531 except SyntaxError, inst:
531 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
532 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
532
533
533 class stringio(object):
534 class stringio(object):
534 '''wrap cStringIO for use by changeset_templater.'''
535 '''wrap cStringIO for use by changeset_templater.'''
535 def __init__(self):
536 def __init__(self):
536 self.fp = cStringIO.StringIO()
537 self.fp = cStringIO.StringIO()
537
538
538 def write(self, *args):
539 def write(self, *args):
539 for a in args:
540 for a in args:
540 self.fp.write(a)
541 self.fp.write(a)
541
542
542 write_header = write
543 write_header = write
543
544
544 def __getattr__(self, key):
545 def __getattr__(self, key):
545 return getattr(self.fp, key)
546 return getattr(self.fp, key)
@@ -1,20 +1,21 b''
1 changeset = 'changeset: {rev}:{node|short}\n{tags}{short_parents}user: {author}\ndate: {date|date}\nsummary: {desc|firstline}\n\n'
1 changeset = 'changeset: {rev}:{node|short}\n{branches}{tags}{short_parents}user: {author}\ndate: {date|date}\nsummary: {desc|firstline}\n\n'
2 changeset_quiet = '{rev}:{node|short}\n'
2 changeset_quiet = '{rev}:{node|short}\n'
3 changeset_verbose = 'changeset: {rev}:{node|short}\n{tags}{parents}{manifest}user: {author}\ndate: {date|date}\n{files}{file_adds}{file_dels}{file_copies}description:\n{desc|strip}\n\n\n'
3 changeset_verbose = 'changeset: {rev}:{node|short}\n{branches}{tags}{parents}{manifest}user: {author}\ndate: {date|date}\n{files}{file_adds}{file_dels}{file_copies}description:\n{desc|strip}\n\n\n'
4 changeset_debug = 'changeset: {rev}:{node}\n{tags}{parents}{manifest}user: {author}\ndate: {date|date}\n{files}{file_adds}{file_dels}{file_copies}description:\n{desc|strip}\n\n\n'
4 changeset_debug = 'changeset: {rev}:{node}\n{branches}{tags}{parents}{manifest}user: {author}\ndate: {date|date}\n{files}{file_adds}{file_dels}{file_copies}description:\n{desc|strip}\n\n\n'
5 start_files = 'files: '
5 start_files = 'files: '
6 file = ' {file}'
6 file = ' {file}'
7 end_files = '\n'
7 end_files = '\n'
8 start_file_adds = 'files+: '
8 start_file_adds = 'files+: '
9 file_add = ' {file_add}'
9 file_add = ' {file_add}'
10 end_file_adds = '\n'
10 end_file_adds = '\n'
11 start_file_dels = 'files-: '
11 start_file_dels = 'files-: '
12 file_del = ' {file_del}'
12 file_del = ' {file_del}'
13 end_file_dels = '\n'
13 end_file_dels = '\n'
14 start_file_copies = 'copies: '
14 start_file_copies = 'copies: '
15 file_copy = ' {name} ({source})'
15 file_copy = ' {name} ({source})'
16 end_file_copies = '\n'
16 end_file_copies = '\n'
17 short_parent = 'parent: {rev}:{node|short}\n'
17 short_parent = 'parent: {rev}:{node|short}\n'
18 parent = 'parent: {rev}:{node}\n'
18 parent = 'parent: {rev}:{node}\n'
19 manifest = 'manifest: {rev}:{node}\n'
19 manifest = 'manifest: {rev}:{node}\n'
20 branch = 'branch: {branch}\n'
20 tag = 'tag: {tag}\n'
21 tag = 'tag: {tag}\n'
General Comments 0
You need to be logged in to leave comments. Login now