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