##// END OF EJS Templates
templatefilters: add parameterized fill function
Matt Mackall -
r17638:e2711975 default
parent child Browse files
Show More
@@ -1,395 +1,414
1 # template-filters.py - common template expansion filters
1 # template-filters.py - common template expansion filters
2 #
2 #
3 # Copyright 2005-2008 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2008 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 import cgi, re, os, time, urllib
8 import cgi, re, os, time, urllib
9 import encoding, node, util
9 import encoding, node, util, error
10 import hbisect
10 import hbisect
11
11
12 def addbreaks(text):
12 def addbreaks(text):
13 """:addbreaks: Any text. Add an XHTML "<br />" tag before the end of
13 """:addbreaks: Any text. Add an XHTML "<br />" tag before the end of
14 every line except the last.
14 every line except the last.
15 """
15 """
16 return text.replace('\n', '<br/>\n')
16 return text.replace('\n', '<br/>\n')
17
17
18 agescales = [("year", 3600 * 24 * 365),
18 agescales = [("year", 3600 * 24 * 365),
19 ("month", 3600 * 24 * 30),
19 ("month", 3600 * 24 * 30),
20 ("week", 3600 * 24 * 7),
20 ("week", 3600 * 24 * 7),
21 ("day", 3600 * 24),
21 ("day", 3600 * 24),
22 ("hour", 3600),
22 ("hour", 3600),
23 ("minute", 60),
23 ("minute", 60),
24 ("second", 1)]
24 ("second", 1)]
25
25
26 def age(date):
26 def age(date):
27 """:age: Date. Returns a human-readable date/time difference between the
27 """:age: Date. Returns a human-readable date/time difference between the
28 given date/time and the current date/time.
28 given date/time and the current date/time.
29 """
29 """
30
30
31 def plural(t, c):
31 def plural(t, c):
32 if c == 1:
32 if c == 1:
33 return t
33 return t
34 return t + "s"
34 return t + "s"
35 def fmt(t, c):
35 def fmt(t, c):
36 return "%d %s" % (c, plural(t, c))
36 return "%d %s" % (c, plural(t, c))
37
37
38 now = time.time()
38 now = time.time()
39 then = date[0]
39 then = date[0]
40 future = False
40 future = False
41 if then > now:
41 if then > now:
42 future = True
42 future = True
43 delta = max(1, int(then - now))
43 delta = max(1, int(then - now))
44 if delta > agescales[0][1] * 30:
44 if delta > agescales[0][1] * 30:
45 return 'in the distant future'
45 return 'in the distant future'
46 else:
46 else:
47 delta = max(1, int(now - then))
47 delta = max(1, int(now - then))
48 if delta > agescales[0][1] * 2:
48 if delta > agescales[0][1] * 2:
49 return util.shortdate(date)
49 return util.shortdate(date)
50
50
51 for t, s in agescales:
51 for t, s in agescales:
52 n = delta // s
52 n = delta // s
53 if n >= 2 or s == 1:
53 if n >= 2 or s == 1:
54 if future:
54 if future:
55 return '%s from now' % fmt(t, n)
55 return '%s from now' % fmt(t, n)
56 return '%s ago' % fmt(t, n)
56 return '%s ago' % fmt(t, n)
57
57
58 def basename(path):
58 def basename(path):
59 """:basename: Any text. Treats the text as a path, and returns the last
59 """:basename: Any text. Treats the text as a path, and returns the last
60 component of the path after splitting by the path separator
60 component of the path after splitting by the path separator
61 (ignoring trailing separators). For example, "foo/bar/baz" becomes
61 (ignoring trailing separators). For example, "foo/bar/baz" becomes
62 "baz" and "foo/bar//" becomes "bar".
62 "baz" and "foo/bar//" becomes "bar".
63 """
63 """
64 return os.path.basename(path)
64 return os.path.basename(path)
65
65
66 def datefilter(text):
66 def datefilter(text):
67 """:date: Date. Returns a date in a Unix date format, including the
67 """:date: Date. Returns a date in a Unix date format, including the
68 timezone: "Mon Sep 04 15:13:13 2006 0700".
68 timezone: "Mon Sep 04 15:13:13 2006 0700".
69 """
69 """
70 return util.datestr(text)
70 return util.datestr(text)
71
71
72 def domain(author):
72 def domain(author):
73 """:domain: Any text. Finds the first string that looks like an email
73 """:domain: Any text. Finds the first string that looks like an email
74 address, and extracts just the domain component. Example: ``User
74 address, and extracts just the domain component. Example: ``User
75 <user@example.com>`` becomes ``example.com``.
75 <user@example.com>`` becomes ``example.com``.
76 """
76 """
77 f = author.find('@')
77 f = author.find('@')
78 if f == -1:
78 if f == -1:
79 return ''
79 return ''
80 author = author[f + 1:]
80 author = author[f + 1:]
81 f = author.find('>')
81 f = author.find('>')
82 if f >= 0:
82 if f >= 0:
83 author = author[:f]
83 author = author[:f]
84 return author
84 return author
85
85
86 def email(text):
86 def email(text):
87 """:email: Any text. Extracts the first string that looks like an email
87 """:email: Any text. Extracts the first string that looks like an email
88 address. Example: ``User <user@example.com>`` becomes
88 address. Example: ``User <user@example.com>`` becomes
89 ``user@example.com``.
89 ``user@example.com``.
90 """
90 """
91 return util.email(text)
91 return util.email(text)
92
92
93 def escape(text):
93 def escape(text):
94 """:escape: Any text. Replaces the special XML/XHTML characters "&", "<"
94 """:escape: Any text. Replaces the special XML/XHTML characters "&", "<"
95 and ">" with XML entities.
95 and ">" with XML entities.
96 """
96 """
97 return cgi.escape(text, True)
97 return cgi.escape(text, True)
98
98
99 para_re = None
99 para_re = None
100 space_re = None
100 space_re = None
101
101
102 def fill(text, width):
102 def fill(text, width):
103 '''fill many paragraphs.'''
103 '''fill many paragraphs.'''
104 global para_re, space_re
104 global para_re, space_re
105 if para_re is None:
105 if para_re is None:
106 para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M)
106 para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M)
107 space_re = re.compile(r' +')
107 space_re = re.compile(r' +')
108
108
109 def findparas():
109 def findparas():
110 start = 0
110 start = 0
111 while True:
111 while True:
112 m = para_re.search(text, start)
112 m = para_re.search(text, start)
113 if not m:
113 if not m:
114 uctext = unicode(text[start:], encoding.encoding)
114 uctext = unicode(text[start:], encoding.encoding)
115 w = len(uctext)
115 w = len(uctext)
116 while 0 < w and uctext[w - 1].isspace():
116 while 0 < w and uctext[w - 1].isspace():
117 w -= 1
117 w -= 1
118 yield (uctext[:w].encode(encoding.encoding),
118 yield (uctext[:w].encode(encoding.encoding),
119 uctext[w:].encode(encoding.encoding))
119 uctext[w:].encode(encoding.encoding))
120 break
120 break
121 yield text[start:m.start(0)], m.group(1)
121 yield text[start:m.start(0)], m.group(1)
122 start = m.end(1)
122 start = m.end(1)
123
123
124 return "".join([space_re.sub(' ', util.wrap(para, width=width)) + rest
124 return "".join([space_re.sub(' ', util.wrap(para, width=width)) + rest
125 for para, rest in findparas()])
125 for para, rest in findparas()])
126
126
127 def fill68(text):
127 def fill68(text):
128 """:fill68: Any text. Wraps the text to fit in 68 columns."""
128 """:fill68: Any text. Wraps the text to fit in 68 columns."""
129 return fill(text, 68)
129 return fill(text, 68)
130
130
131 def fill76(text):
131 def fill76(text):
132 """:fill76: Any text. Wraps the text to fit in 76 columns."""
132 """:fill76: Any text. Wraps the text to fit in 76 columns."""
133 return fill(text, 76)
133 return fill(text, 76)
134
134
135 def firstline(text):
135 def firstline(text):
136 """:firstline: Any text. Returns the first line of text."""
136 """:firstline: Any text. Returns the first line of text."""
137 try:
137 try:
138 return text.splitlines(True)[0].rstrip('\r\n')
138 return text.splitlines(True)[0].rstrip('\r\n')
139 except IndexError:
139 except IndexError:
140 return ''
140 return ''
141
141
142 def hexfilter(text):
142 def hexfilter(text):
143 """:hex: Any text. Convert a binary Mercurial node identifier into
143 """:hex: Any text. Convert a binary Mercurial node identifier into
144 its long hexadecimal representation.
144 its long hexadecimal representation.
145 """
145 """
146 return node.hex(text)
146 return node.hex(text)
147
147
148 def hgdate(text):
148 def hgdate(text):
149 """:hgdate: Date. Returns the date as a pair of numbers: "1157407993
149 """:hgdate: Date. Returns the date as a pair of numbers: "1157407993
150 25200" (Unix timestamp, timezone offset).
150 25200" (Unix timestamp, timezone offset).
151 """
151 """
152 return "%d %d" % text
152 return "%d %d" % text
153
153
154 def isodate(text):
154 def isodate(text):
155 """:isodate: Date. Returns the date in ISO 8601 format: "2009-08-18 13:00
155 """:isodate: Date. Returns the date in ISO 8601 format: "2009-08-18 13:00
156 +0200".
156 +0200".
157 """
157 """
158 return util.datestr(text, '%Y-%m-%d %H:%M %1%2')
158 return util.datestr(text, '%Y-%m-%d %H:%M %1%2')
159
159
160 def isodatesec(text):
160 def isodatesec(text):
161 """:isodatesec: Date. Returns the date in ISO 8601 format, including
161 """:isodatesec: Date. Returns the date in ISO 8601 format, including
162 seconds: "2009-08-18 13:00:13 +0200". See also the rfc3339date
162 seconds: "2009-08-18 13:00:13 +0200". See also the rfc3339date
163 filter.
163 filter.
164 """
164 """
165 return util.datestr(text, '%Y-%m-%d %H:%M:%S %1%2')
165 return util.datestr(text, '%Y-%m-%d %H:%M:%S %1%2')
166
166
167 def indent(text, prefix):
167 def indent(text, prefix):
168 '''indent each non-empty line of text after first with prefix.'''
168 '''indent each non-empty line of text after first with prefix.'''
169 lines = text.splitlines()
169 lines = text.splitlines()
170 num_lines = len(lines)
170 num_lines = len(lines)
171 endswithnewline = text[-1:] == '\n'
171 endswithnewline = text[-1:] == '\n'
172 def indenter():
172 def indenter():
173 for i in xrange(num_lines):
173 for i in xrange(num_lines):
174 l = lines[i]
174 l = lines[i]
175 if i and l.strip():
175 if i and l.strip():
176 yield prefix
176 yield prefix
177 yield l
177 yield l
178 if i < num_lines - 1 or endswithnewline:
178 if i < num_lines - 1 or endswithnewline:
179 yield '\n'
179 yield '\n'
180 return "".join(indenter())
180 return "".join(indenter())
181
181
182 def json(obj):
182 def json(obj):
183 if obj is None or obj is False or obj is True:
183 if obj is None or obj is False or obj is True:
184 return {None: 'null', False: 'false', True: 'true'}[obj]
184 return {None: 'null', False: 'false', True: 'true'}[obj]
185 elif isinstance(obj, int) or isinstance(obj, float):
185 elif isinstance(obj, int) or isinstance(obj, float):
186 return str(obj)
186 return str(obj)
187 elif isinstance(obj, str):
187 elif isinstance(obj, str):
188 u = unicode(obj, encoding.encoding, 'replace')
188 u = unicode(obj, encoding.encoding, 'replace')
189 return '"%s"' % jsonescape(u)
189 return '"%s"' % jsonescape(u)
190 elif isinstance(obj, unicode):
190 elif isinstance(obj, unicode):
191 return '"%s"' % jsonescape(obj)
191 return '"%s"' % jsonescape(obj)
192 elif util.safehasattr(obj, 'keys'):
192 elif util.safehasattr(obj, 'keys'):
193 out = []
193 out = []
194 for k, v in obj.iteritems():
194 for k, v in obj.iteritems():
195 s = '%s: %s' % (json(k), json(v))
195 s = '%s: %s' % (json(k), json(v))
196 out.append(s)
196 out.append(s)
197 return '{' + ', '.join(out) + '}'
197 return '{' + ', '.join(out) + '}'
198 elif util.safehasattr(obj, '__iter__'):
198 elif util.safehasattr(obj, '__iter__'):
199 out = []
199 out = []
200 for i in obj:
200 for i in obj:
201 out.append(json(i))
201 out.append(json(i))
202 return '[' + ', '.join(out) + ']'
202 return '[' + ', '.join(out) + ']'
203 else:
203 else:
204 raise TypeError('cannot encode type %s' % obj.__class__.__name__)
204 raise TypeError('cannot encode type %s' % obj.__class__.__name__)
205
205
206 def _uescape(c):
206 def _uescape(c):
207 if ord(c) < 0x80:
207 if ord(c) < 0x80:
208 return c
208 return c
209 else:
209 else:
210 return '\\u%04x' % ord(c)
210 return '\\u%04x' % ord(c)
211
211
212 _escapes = [
212 _escapes = [
213 ('\\', '\\\\'), ('"', '\\"'), ('\t', '\\t'), ('\n', '\\n'),
213 ('\\', '\\\\'), ('"', '\\"'), ('\t', '\\t'), ('\n', '\\n'),
214 ('\r', '\\r'), ('\f', '\\f'), ('\b', '\\b'),
214 ('\r', '\\r'), ('\f', '\\f'), ('\b', '\\b'),
215 ]
215 ]
216
216
217 def jsonescape(s):
217 def jsonescape(s):
218 for k, v in _escapes:
218 for k, v in _escapes:
219 s = s.replace(k, v)
219 s = s.replace(k, v)
220 return ''.join(_uescape(c) for c in s)
220 return ''.join(_uescape(c) for c in s)
221
221
222 def localdate(text):
222 def localdate(text):
223 """:localdate: Date. Converts a date to local date."""
223 """:localdate: Date. Converts a date to local date."""
224 return (text[0], util.makedate()[1])
224 return (text[0], util.makedate()[1])
225
225
226 def nonempty(str):
226 def nonempty(str):
227 """:nonempty: Any text. Returns '(none)' if the string is empty."""
227 """:nonempty: Any text. Returns '(none)' if the string is empty."""
228 return str or "(none)"
228 return str or "(none)"
229
229
230 def obfuscate(text):
230 def obfuscate(text):
231 """:obfuscate: Any text. Returns the input text rendered as a sequence of
231 """:obfuscate: Any text. Returns the input text rendered as a sequence of
232 XML entities.
232 XML entities.
233 """
233 """
234 text = unicode(text, encoding.encoding, 'replace')
234 text = unicode(text, encoding.encoding, 'replace')
235 return ''.join(['&#%d;' % ord(c) for c in text])
235 return ''.join(['&#%d;' % ord(c) for c in text])
236
236
237 def permissions(flags):
237 def permissions(flags):
238 if "l" in flags:
238 if "l" in flags:
239 return "lrwxrwxrwx"
239 return "lrwxrwxrwx"
240 if "x" in flags:
240 if "x" in flags:
241 return "-rwxr-xr-x"
241 return "-rwxr-xr-x"
242 return "-rw-r--r--"
242 return "-rw-r--r--"
243
243
244 def person(author):
244 def person(author):
245 """:person: Any text. Returns the name before an email address,
245 """:person: Any text. Returns the name before an email address,
246 interpreting it as per RFC 5322.
246 interpreting it as per RFC 5322.
247
247
248 >>> person('foo@bar')
248 >>> person('foo@bar')
249 'foo'
249 'foo'
250 >>> person('Foo Bar <foo@bar>')
250 >>> person('Foo Bar <foo@bar>')
251 'Foo Bar'
251 'Foo Bar'
252 >>> person('"Foo Bar" <foo@bar>')
252 >>> person('"Foo Bar" <foo@bar>')
253 'Foo Bar'
253 'Foo Bar'
254 >>> person('"Foo \"buz\" Bar" <foo@bar>')
254 >>> person('"Foo \"buz\" Bar" <foo@bar>')
255 'Foo "buz" Bar'
255 'Foo "buz" Bar'
256 >>> # The following are invalid, but do exist in real-life
256 >>> # The following are invalid, but do exist in real-life
257 ...
257 ...
258 >>> person('Foo "buz" Bar <foo@bar>')
258 >>> person('Foo "buz" Bar <foo@bar>')
259 'Foo "buz" Bar'
259 'Foo "buz" Bar'
260 >>> person('"Foo Bar <foo@bar>')
260 >>> person('"Foo Bar <foo@bar>')
261 'Foo Bar'
261 'Foo Bar'
262 """
262 """
263 if '@' not in author:
263 if '@' not in author:
264 return author
264 return author
265 f = author.find('<')
265 f = author.find('<')
266 if f != -1:
266 if f != -1:
267 return author[:f].strip(' "').replace('\\"', '"')
267 return author[:f].strip(' "').replace('\\"', '"')
268 f = author.find('@')
268 f = author.find('@')
269 return author[:f].replace('.', ' ')
269 return author[:f].replace('.', ' ')
270
270
271 def rfc3339date(text):
271 def rfc3339date(text):
272 """:rfc3339date: Date. Returns a date using the Internet date format
272 """:rfc3339date: Date. Returns a date using the Internet date format
273 specified in RFC 3339: "2009-08-18T13:00:13+02:00".
273 specified in RFC 3339: "2009-08-18T13:00:13+02:00".
274 """
274 """
275 return util.datestr(text, "%Y-%m-%dT%H:%M:%S%1:%2")
275 return util.datestr(text, "%Y-%m-%dT%H:%M:%S%1:%2")
276
276
277 def rfc822date(text):
277 def rfc822date(text):
278 """:rfc822date: Date. Returns a date using the same format used in email
278 """:rfc822date: Date. Returns a date using the same format used in email
279 headers: "Tue, 18 Aug 2009 13:00:13 +0200".
279 headers: "Tue, 18 Aug 2009 13:00:13 +0200".
280 """
280 """
281 return util.datestr(text, "%a, %d %b %Y %H:%M:%S %1%2")
281 return util.datestr(text, "%a, %d %b %Y %H:%M:%S %1%2")
282
282
283 def short(text):
283 def short(text):
284 """:short: Changeset hash. Returns the short form of a changeset hash,
284 """:short: Changeset hash. Returns the short form of a changeset hash,
285 i.e. a 12 hexadecimal digit string.
285 i.e. a 12 hexadecimal digit string.
286 """
286 """
287 return text[:12]
287 return text[:12]
288
288
289 def shortbisect(text):
289 def shortbisect(text):
290 """:shortbisect: Any text. Treats `text` as a bisection status, and
290 """:shortbisect: Any text. Treats `text` as a bisection status, and
291 returns a single-character representing the status (G: good, B: bad,
291 returns a single-character representing the status (G: good, B: bad,
292 S: skipped, U: untested, I: ignored). Returns single space if `text`
292 S: skipped, U: untested, I: ignored). Returns single space if `text`
293 is not a valid bisection status.
293 is not a valid bisection status.
294 """
294 """
295 return hbisect.shortlabel(text) or ' '
295 return hbisect.shortlabel(text) or ' '
296
296
297 def shortdate(text):
297 def shortdate(text):
298 """:shortdate: Date. Returns a date like "2006-09-18"."""
298 """:shortdate: Date. Returns a date like "2006-09-18"."""
299 return util.shortdate(text)
299 return util.shortdate(text)
300
300
301 def stringescape(text):
301 def stringescape(text):
302 return text.encode('string_escape')
302 return text.encode('string_escape')
303
303
304 def stringify(thing):
304 def stringify(thing):
305 """:stringify: Any type. Turns the value into text by converting values into
305 """:stringify: Any type. Turns the value into text by converting values into
306 text and concatenating them.
306 text and concatenating them.
307 """
307 """
308 if util.safehasattr(thing, '__iter__') and not isinstance(thing, str):
308 if util.safehasattr(thing, '__iter__') and not isinstance(thing, str):
309 return "".join([stringify(t) for t in thing if t is not None])
309 return "".join([stringify(t) for t in thing if t is not None])
310 return str(thing)
310 return str(thing)
311
311
312 def strip(text):
312 def strip(text):
313 """:strip: Any text. Strips all leading and trailing whitespace."""
313 """:strip: Any text. Strips all leading and trailing whitespace."""
314 return text.strip()
314 return text.strip()
315
315
316 def stripdir(text):
316 def stripdir(text):
317 """:stripdir: Treat the text as path and strip a directory level, if
317 """:stripdir: Treat the text as path and strip a directory level, if
318 possible. For example, "foo" and "foo/bar" becomes "foo".
318 possible. For example, "foo" and "foo/bar" becomes "foo".
319 """
319 """
320 dir = os.path.dirname(text)
320 dir = os.path.dirname(text)
321 if dir == "":
321 if dir == "":
322 return os.path.basename(text)
322 return os.path.basename(text)
323 else:
323 else:
324 return dir
324 return dir
325
325
326 def tabindent(text):
326 def tabindent(text):
327 """:tabindent: Any text. Returns the text, with every line except the
327 """:tabindent: Any text. Returns the text, with every line except the
328 first starting with a tab character.
328 first starting with a tab character.
329 """
329 """
330 return indent(text, '\t')
330 return indent(text, '\t')
331
331
332 def urlescape(text):
332 def urlescape(text):
333 """:urlescape: Any text. Escapes all "special" characters. For example,
333 """:urlescape: Any text. Escapes all "special" characters. For example,
334 "foo bar" becomes "foo%20bar".
334 "foo bar" becomes "foo%20bar".
335 """
335 """
336 return urllib.quote(text)
336 return urllib.quote(text)
337
337
338 def userfilter(text):
338 def userfilter(text):
339 """:user: Any text. Returns a short representation of a user name or email
339 """:user: Any text. Returns a short representation of a user name or email
340 address."""
340 address."""
341 return util.shortuser(text)
341 return util.shortuser(text)
342
342
343 def emailuser(text):
343 def emailuser(text):
344 """:emailuser: Any text. Returns the user portion of an email address."""
344 """:emailuser: Any text. Returns the user portion of an email address."""
345 return util.emailuser(text)
345 return util.emailuser(text)
346
346
347 def xmlescape(text):
347 def xmlescape(text):
348 text = (text
348 text = (text
349 .replace('&', '&amp;')
349 .replace('&', '&amp;')
350 .replace('<', '&lt;')
350 .replace('<', '&lt;')
351 .replace('>', '&gt;')
351 .replace('>', '&gt;')
352 .replace('"', '&quot;')
352 .replace('"', '&quot;')
353 .replace("'", '&#39;')) # &apos; invalid in HTML
353 .replace("'", '&#39;')) # &apos; invalid in HTML
354 return re.sub('[\x00-\x08\x0B\x0C\x0E-\x1F]', ' ', text)
354 return re.sub('[\x00-\x08\x0B\x0C\x0E-\x1F]', ' ', text)
355
355
356 filters = {
356 filters = {
357 "addbreaks": addbreaks,
357 "addbreaks": addbreaks,
358 "age": age,
358 "age": age,
359 "basename": basename,
359 "basename": basename,
360 "date": datefilter,
360 "date": datefilter,
361 "domain": domain,
361 "domain": domain,
362 "email": email,
362 "email": email,
363 "escape": escape,
363 "escape": escape,
364 "fill68": fill68,
364 "fill68": fill68,
365 "fill76": fill76,
365 "fill76": fill76,
366 "firstline": firstline,
366 "firstline": firstline,
367 "hex": hexfilter,
367 "hex": hexfilter,
368 "hgdate": hgdate,
368 "hgdate": hgdate,
369 "isodate": isodate,
369 "isodate": isodate,
370 "isodatesec": isodatesec,
370 "isodatesec": isodatesec,
371 "json": json,
371 "json": json,
372 "jsonescape": jsonescape,
372 "jsonescape": jsonescape,
373 "localdate": localdate,
373 "localdate": localdate,
374 "nonempty": nonempty,
374 "nonempty": nonempty,
375 "obfuscate": obfuscate,
375 "obfuscate": obfuscate,
376 "permissions": permissions,
376 "permissions": permissions,
377 "person": person,
377 "person": person,
378 "rfc3339date": rfc3339date,
378 "rfc3339date": rfc3339date,
379 "rfc822date": rfc822date,
379 "rfc822date": rfc822date,
380 "short": short,
380 "short": short,
381 "shortbisect": shortbisect,
381 "shortbisect": shortbisect,
382 "shortdate": shortdate,
382 "shortdate": shortdate,
383 "stringescape": stringescape,
383 "stringescape": stringescape,
384 "stringify": stringify,
384 "stringify": stringify,
385 "strip": strip,
385 "strip": strip,
386 "stripdir": stripdir,
386 "stripdir": stripdir,
387 "tabindent": tabindent,
387 "tabindent": tabindent,
388 "urlescape": urlescape,
388 "urlescape": urlescape,
389 "user": userfilter,
389 "user": userfilter,
390 "emailuser": emailuser,
390 "emailuser": emailuser,
391 "xmlescape": xmlescape,
391 "xmlescape": xmlescape,
392 }
392 }
393
393
394 def fillfunc(context, mapping, args):
395 if not (1 <= len(args) <= 2):
396 raise error.ParseError(_("fill expects one or two arguments"))
397
398 text = stringify(args[0][0](context, mapping, args[0][1]))
399 width = 76
400 if len(args) == 2:
401 try:
402 width = int(stringify(args[1][0](context, mapping, args[1][1])))
403 except ValueError:
404 raise error.ParseError(_("fill expects an integer width"))
405
406 return fill(text, width)
407
408
409 funcs = {
410 "fill": fillfunc,
411 }
412
394 # tell hggettext to extract docstrings from these functions:
413 # tell hggettext to extract docstrings from these functions:
395 i18nfunctions = filters.values()
414 i18nfunctions = filters.values()
General Comments 0
You need to be logged in to leave comments. Login now