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