##// END OF EJS Templates
templatefilters: convert arguments to sysstrs for unicode() ctor...
Augie Fackler -
r36581:9d71bd25 default
parent child Browse files
Show More
@@ -1,456 +1,456 b''
1 # templatefilters.py - common template expansion filters
1 # templatefilters.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 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import os
10 import os
11 import re
11 import re
12 import time
12 import time
13
13
14 from . import (
14 from . import (
15 encoding,
15 encoding,
16 error,
16 error,
17 hbisect,
17 hbisect,
18 node,
18 node,
19 pycompat,
19 pycompat,
20 registrar,
20 registrar,
21 templatekw,
21 templatekw,
22 url,
22 url,
23 util,
23 util,
24 )
24 )
25
25
26 urlerr = util.urlerr
26 urlerr = util.urlerr
27 urlreq = util.urlreq
27 urlreq = util.urlreq
28
28
29 if pycompat.ispy3:
29 if pycompat.ispy3:
30 long = int
30 long = int
31
31
32 # filters are callables like:
32 # filters are callables like:
33 # fn(obj)
33 # fn(obj)
34 # with:
34 # with:
35 # obj - object to be filtered (text, date, list and so on)
35 # obj - object to be filtered (text, date, list and so on)
36 filters = {}
36 filters = {}
37
37
38 templatefilter = registrar.templatefilter(filters)
38 templatefilter = registrar.templatefilter(filters)
39
39
40 @templatefilter('addbreaks')
40 @templatefilter('addbreaks')
41 def addbreaks(text):
41 def addbreaks(text):
42 """Any text. Add an XHTML "<br />" tag before the end of
42 """Any text. Add an XHTML "<br />" tag before the end of
43 every line except the last.
43 every line except the last.
44 """
44 """
45 return text.replace('\n', '<br/>\n')
45 return text.replace('\n', '<br/>\n')
46
46
47 agescales = [("year", 3600 * 24 * 365, 'Y'),
47 agescales = [("year", 3600 * 24 * 365, 'Y'),
48 ("month", 3600 * 24 * 30, 'M'),
48 ("month", 3600 * 24 * 30, 'M'),
49 ("week", 3600 * 24 * 7, 'W'),
49 ("week", 3600 * 24 * 7, 'W'),
50 ("day", 3600 * 24, 'd'),
50 ("day", 3600 * 24, 'd'),
51 ("hour", 3600, 'h'),
51 ("hour", 3600, 'h'),
52 ("minute", 60, 'm'),
52 ("minute", 60, 'm'),
53 ("second", 1, 's')]
53 ("second", 1, 's')]
54
54
55 @templatefilter('age')
55 @templatefilter('age')
56 def age(date, abbrev=False):
56 def age(date, abbrev=False):
57 """Date. Returns a human-readable date/time difference between the
57 """Date. Returns a human-readable date/time difference between the
58 given date/time and the current date/time.
58 given date/time and the current date/time.
59 """
59 """
60
60
61 def plural(t, c):
61 def plural(t, c):
62 if c == 1:
62 if c == 1:
63 return t
63 return t
64 return t + "s"
64 return t + "s"
65 def fmt(t, c, a):
65 def fmt(t, c, a):
66 if abbrev:
66 if abbrev:
67 return "%d%s" % (c, a)
67 return "%d%s" % (c, a)
68 return "%d %s" % (c, plural(t, c))
68 return "%d %s" % (c, plural(t, c))
69
69
70 now = time.time()
70 now = time.time()
71 then = date[0]
71 then = date[0]
72 future = False
72 future = False
73 if then > now:
73 if then > now:
74 future = True
74 future = True
75 delta = max(1, int(then - now))
75 delta = max(1, int(then - now))
76 if delta > agescales[0][1] * 30:
76 if delta > agescales[0][1] * 30:
77 return 'in the distant future'
77 return 'in the distant future'
78 else:
78 else:
79 delta = max(1, int(now - then))
79 delta = max(1, int(now - then))
80 if delta > agescales[0][1] * 2:
80 if delta > agescales[0][1] * 2:
81 return util.shortdate(date)
81 return util.shortdate(date)
82
82
83 for t, s, a in agescales:
83 for t, s, a in agescales:
84 n = delta // s
84 n = delta // s
85 if n >= 2 or s == 1:
85 if n >= 2 or s == 1:
86 if future:
86 if future:
87 return '%s from now' % fmt(t, n, a)
87 return '%s from now' % fmt(t, n, a)
88 return '%s ago' % fmt(t, n, a)
88 return '%s ago' % fmt(t, n, a)
89
89
90 @templatefilter('basename')
90 @templatefilter('basename')
91 def basename(path):
91 def basename(path):
92 """Any text. Treats the text as a path, and returns the last
92 """Any text. Treats the text as a path, and returns the last
93 component of the path after splitting by the path separator.
93 component of the path after splitting by the path separator.
94 For example, "foo/bar/baz" becomes "baz" and "foo/bar//" becomes "".
94 For example, "foo/bar/baz" becomes "baz" and "foo/bar//" becomes "".
95 """
95 """
96 return os.path.basename(path)
96 return os.path.basename(path)
97
97
98 @templatefilter('count')
98 @templatefilter('count')
99 def count(i):
99 def count(i):
100 """List or text. Returns the length as an integer."""
100 """List or text. Returns the length as an integer."""
101 return len(i)
101 return len(i)
102
102
103 @templatefilter('dirname')
103 @templatefilter('dirname')
104 def dirname(path):
104 def dirname(path):
105 """Any text. Treats the text as a path, and strips the last
105 """Any text. Treats the text as a path, and strips the last
106 component of the path after splitting by the path separator.
106 component of the path after splitting by the path separator.
107 """
107 """
108 return os.path.dirname(path)
108 return os.path.dirname(path)
109
109
110 @templatefilter('domain')
110 @templatefilter('domain')
111 def domain(author):
111 def domain(author):
112 """Any text. Finds the first string that looks like an email
112 """Any text. Finds the first string that looks like an email
113 address, and extracts just the domain component. Example: ``User
113 address, and extracts just the domain component. Example: ``User
114 <user@example.com>`` becomes ``example.com``.
114 <user@example.com>`` becomes ``example.com``.
115 """
115 """
116 f = author.find('@')
116 f = author.find('@')
117 if f == -1:
117 if f == -1:
118 return ''
118 return ''
119 author = author[f + 1:]
119 author = author[f + 1:]
120 f = author.find('>')
120 f = author.find('>')
121 if f >= 0:
121 if f >= 0:
122 author = author[:f]
122 author = author[:f]
123 return author
123 return author
124
124
125 @templatefilter('email')
125 @templatefilter('email')
126 def email(text):
126 def email(text):
127 """Any text. Extracts the first string that looks like an email
127 """Any text. Extracts the first string that looks like an email
128 address. Example: ``User <user@example.com>`` becomes
128 address. Example: ``User <user@example.com>`` becomes
129 ``user@example.com``.
129 ``user@example.com``.
130 """
130 """
131 return util.email(text)
131 return util.email(text)
132
132
133 @templatefilter('escape')
133 @templatefilter('escape')
134 def escape(text):
134 def escape(text):
135 """Any text. Replaces the special XML/XHTML characters "&", "<"
135 """Any text. Replaces the special XML/XHTML characters "&", "<"
136 and ">" with XML entities, and filters out NUL characters.
136 and ">" with XML entities, and filters out NUL characters.
137 """
137 """
138 return url.escape(text.replace('\0', ''), True)
138 return url.escape(text.replace('\0', ''), True)
139
139
140 para_re = None
140 para_re = None
141 space_re = None
141 space_re = None
142
142
143 def fill(text, width, initindent='', hangindent=''):
143 def fill(text, width, initindent='', hangindent=''):
144 '''fill many paragraphs with optional indentation.'''
144 '''fill many paragraphs with optional indentation.'''
145 global para_re, space_re
145 global para_re, space_re
146 if para_re is None:
146 if para_re is None:
147 para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M)
147 para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M)
148 space_re = re.compile(br' +')
148 space_re = re.compile(br' +')
149
149
150 def findparas():
150 def findparas():
151 start = 0
151 start = 0
152 while True:
152 while True:
153 m = para_re.search(text, start)
153 m = para_re.search(text, start)
154 if not m:
154 if not m:
155 uctext = encoding.unifromlocal(text[start:])
155 uctext = encoding.unifromlocal(text[start:])
156 w = len(uctext)
156 w = len(uctext)
157 while 0 < w and uctext[w - 1].isspace():
157 while 0 < w and uctext[w - 1].isspace():
158 w -= 1
158 w -= 1
159 yield (encoding.unitolocal(uctext[:w]),
159 yield (encoding.unitolocal(uctext[:w]),
160 encoding.unitolocal(uctext[w:]))
160 encoding.unitolocal(uctext[w:]))
161 break
161 break
162 yield text[start:m.start(0)], m.group(1)
162 yield text[start:m.start(0)], m.group(1)
163 start = m.end(1)
163 start = m.end(1)
164
164
165 return "".join([util.wrap(space_re.sub(' ', util.wrap(para, width)),
165 return "".join([util.wrap(space_re.sub(' ', util.wrap(para, width)),
166 width, initindent, hangindent) + rest
166 width, initindent, hangindent) + rest
167 for para, rest in findparas()])
167 for para, rest in findparas()])
168
168
169 @templatefilter('fill68')
169 @templatefilter('fill68')
170 def fill68(text):
170 def fill68(text):
171 """Any text. Wraps the text to fit in 68 columns."""
171 """Any text. Wraps the text to fit in 68 columns."""
172 return fill(text, 68)
172 return fill(text, 68)
173
173
174 @templatefilter('fill76')
174 @templatefilter('fill76')
175 def fill76(text):
175 def fill76(text):
176 """Any text. Wraps the text to fit in 76 columns."""
176 """Any text. Wraps the text to fit in 76 columns."""
177 return fill(text, 76)
177 return fill(text, 76)
178
178
179 @templatefilter('firstline')
179 @templatefilter('firstline')
180 def firstline(text):
180 def firstline(text):
181 """Any text. Returns the first line of text."""
181 """Any text. Returns the first line of text."""
182 try:
182 try:
183 return text.splitlines(True)[0].rstrip('\r\n')
183 return text.splitlines(True)[0].rstrip('\r\n')
184 except IndexError:
184 except IndexError:
185 return ''
185 return ''
186
186
187 @templatefilter('hex')
187 @templatefilter('hex')
188 def hexfilter(text):
188 def hexfilter(text):
189 """Any text. Convert a binary Mercurial node identifier into
189 """Any text. Convert a binary Mercurial node identifier into
190 its long hexadecimal representation.
190 its long hexadecimal representation.
191 """
191 """
192 return node.hex(text)
192 return node.hex(text)
193
193
194 @templatefilter('hgdate')
194 @templatefilter('hgdate')
195 def hgdate(text):
195 def hgdate(text):
196 """Date. Returns the date as a pair of numbers: "1157407993
196 """Date. Returns the date as a pair of numbers: "1157407993
197 25200" (Unix timestamp, timezone offset).
197 25200" (Unix timestamp, timezone offset).
198 """
198 """
199 return "%d %d" % text
199 return "%d %d" % text
200
200
201 @templatefilter('isodate')
201 @templatefilter('isodate')
202 def isodate(text):
202 def isodate(text):
203 """Date. Returns the date in ISO 8601 format: "2009-08-18 13:00
203 """Date. Returns the date in ISO 8601 format: "2009-08-18 13:00
204 +0200".
204 +0200".
205 """
205 """
206 return util.datestr(text, '%Y-%m-%d %H:%M %1%2')
206 return util.datestr(text, '%Y-%m-%d %H:%M %1%2')
207
207
208 @templatefilter('isodatesec')
208 @templatefilter('isodatesec')
209 def isodatesec(text):
209 def isodatesec(text):
210 """Date. Returns the date in ISO 8601 format, including
210 """Date. Returns the date in ISO 8601 format, including
211 seconds: "2009-08-18 13:00:13 +0200". See also the rfc3339date
211 seconds: "2009-08-18 13:00:13 +0200". See also the rfc3339date
212 filter.
212 filter.
213 """
213 """
214 return util.datestr(text, '%Y-%m-%d %H:%M:%S %1%2')
214 return util.datestr(text, '%Y-%m-%d %H:%M:%S %1%2')
215
215
216 def indent(text, prefix):
216 def indent(text, prefix):
217 '''indent each non-empty line of text after first with prefix.'''
217 '''indent each non-empty line of text after first with prefix.'''
218 lines = text.splitlines()
218 lines = text.splitlines()
219 num_lines = len(lines)
219 num_lines = len(lines)
220 endswithnewline = text[-1:] == '\n'
220 endswithnewline = text[-1:] == '\n'
221 def indenter():
221 def indenter():
222 for i in xrange(num_lines):
222 for i in xrange(num_lines):
223 l = lines[i]
223 l = lines[i]
224 if i and l.strip():
224 if i and l.strip():
225 yield prefix
225 yield prefix
226 yield l
226 yield l
227 if i < num_lines - 1 or endswithnewline:
227 if i < num_lines - 1 or endswithnewline:
228 yield '\n'
228 yield '\n'
229 return "".join(indenter())
229 return "".join(indenter())
230
230
231 @templatefilter('json')
231 @templatefilter('json')
232 def json(obj, paranoid=True):
232 def json(obj, paranoid=True):
233 if obj is None:
233 if obj is None:
234 return 'null'
234 return 'null'
235 elif obj is False:
235 elif obj is False:
236 return 'false'
236 return 'false'
237 elif obj is True:
237 elif obj is True:
238 return 'true'
238 return 'true'
239 elif isinstance(obj, (int, long, float)):
239 elif isinstance(obj, (int, long, float)):
240 return pycompat.bytestr(obj)
240 return pycompat.bytestr(obj)
241 elif isinstance(obj, bytes):
241 elif isinstance(obj, bytes):
242 return '"%s"' % encoding.jsonescape(obj, paranoid=paranoid)
242 return '"%s"' % encoding.jsonescape(obj, paranoid=paranoid)
243 elif isinstance(obj, str):
243 elif isinstance(obj, str):
244 # This branch is unreachable on Python 2, because bytes == str
244 # This branch is unreachable on Python 2, because bytes == str
245 # and we'll return in the next-earlier block in the elif
245 # and we'll return in the next-earlier block in the elif
246 # ladder. On Python 3, this helps us catch bugs before they
246 # ladder. On Python 3, this helps us catch bugs before they
247 # hurt someone.
247 # hurt someone.
248 raise error.ProgrammingError(
248 raise error.ProgrammingError(
249 'Mercurial only does output with bytes on Python 3: %r' % obj)
249 'Mercurial only does output with bytes on Python 3: %r' % obj)
250 elif util.safehasattr(obj, 'keys'):
250 elif util.safehasattr(obj, 'keys'):
251 out = ['"%s": %s' % (encoding.jsonescape(k, paranoid=paranoid),
251 out = ['"%s": %s' % (encoding.jsonescape(k, paranoid=paranoid),
252 json(v, paranoid))
252 json(v, paranoid))
253 for k, v in sorted(obj.iteritems())]
253 for k, v in sorted(obj.iteritems())]
254 return '{' + ', '.join(out) + '}'
254 return '{' + ', '.join(out) + '}'
255 elif util.safehasattr(obj, '__iter__'):
255 elif util.safehasattr(obj, '__iter__'):
256 out = [json(i, paranoid) for i in obj]
256 out = [json(i, paranoid) for i in obj]
257 return '[' + ', '.join(out) + ']'
257 return '[' + ', '.join(out) + ']'
258 else:
258 else:
259 raise TypeError('cannot encode type %s' % obj.__class__.__name__)
259 raise TypeError('cannot encode type %s' % obj.__class__.__name__)
260
260
261 @templatefilter('lower')
261 @templatefilter('lower')
262 def lower(text):
262 def lower(text):
263 """Any text. Converts the text to lowercase."""
263 """Any text. Converts the text to lowercase."""
264 return encoding.lower(text)
264 return encoding.lower(text)
265
265
266 @templatefilter('nonempty')
266 @templatefilter('nonempty')
267 def nonempty(text):
267 def nonempty(text):
268 """Any text. Returns '(none)' if the string is empty."""
268 """Any text. Returns '(none)' if the string is empty."""
269 return text or "(none)"
269 return text or "(none)"
270
270
271 @templatefilter('obfuscate')
271 @templatefilter('obfuscate')
272 def obfuscate(text):
272 def obfuscate(text):
273 """Any text. Returns the input text rendered as a sequence of
273 """Any text. Returns the input text rendered as a sequence of
274 XML entities.
274 XML entities.
275 """
275 """
276 text = unicode(text, encoding.encoding, 'replace')
276 text = unicode(text, pycompat.sysstr(encoding.encoding), r'replace')
277 return ''.join(['&#%d;' % ord(c) for c in text])
277 return ''.join(['&#%d;' % ord(c) for c in text])
278
278
279 @templatefilter('permissions')
279 @templatefilter('permissions')
280 def permissions(flags):
280 def permissions(flags):
281 if "l" in flags:
281 if "l" in flags:
282 return "lrwxrwxrwx"
282 return "lrwxrwxrwx"
283 if "x" in flags:
283 if "x" in flags:
284 return "-rwxr-xr-x"
284 return "-rwxr-xr-x"
285 return "-rw-r--r--"
285 return "-rw-r--r--"
286
286
287 @templatefilter('person')
287 @templatefilter('person')
288 def person(author):
288 def person(author):
289 """Any text. Returns the name before an email address,
289 """Any text. Returns the name before an email address,
290 interpreting it as per RFC 5322.
290 interpreting it as per RFC 5322.
291
291
292 >>> person(b'foo@bar')
292 >>> person(b'foo@bar')
293 'foo'
293 'foo'
294 >>> person(b'Foo Bar <foo@bar>')
294 >>> person(b'Foo Bar <foo@bar>')
295 'Foo Bar'
295 'Foo Bar'
296 >>> person(b'"Foo Bar" <foo@bar>')
296 >>> person(b'"Foo Bar" <foo@bar>')
297 'Foo Bar'
297 'Foo Bar'
298 >>> person(b'"Foo \"buz\" Bar" <foo@bar>')
298 >>> person(b'"Foo \"buz\" Bar" <foo@bar>')
299 'Foo "buz" Bar'
299 'Foo "buz" Bar'
300 >>> # The following are invalid, but do exist in real-life
300 >>> # The following are invalid, but do exist in real-life
301 ...
301 ...
302 >>> person(b'Foo "buz" Bar <foo@bar>')
302 >>> person(b'Foo "buz" Bar <foo@bar>')
303 'Foo "buz" Bar'
303 'Foo "buz" Bar'
304 >>> person(b'"Foo Bar <foo@bar>')
304 >>> person(b'"Foo Bar <foo@bar>')
305 'Foo Bar'
305 'Foo Bar'
306 """
306 """
307 if '@' not in author:
307 if '@' not in author:
308 return author
308 return author
309 f = author.find('<')
309 f = author.find('<')
310 if f != -1:
310 if f != -1:
311 return author[:f].strip(' "').replace('\\"', '"')
311 return author[:f].strip(' "').replace('\\"', '"')
312 f = author.find('@')
312 f = author.find('@')
313 return author[:f].replace('.', ' ')
313 return author[:f].replace('.', ' ')
314
314
315 @templatefilter('revescape')
315 @templatefilter('revescape')
316 def revescape(text):
316 def revescape(text):
317 """Any text. Escapes all "special" characters, except @.
317 """Any text. Escapes all "special" characters, except @.
318 Forward slashes are escaped twice to prevent web servers from prematurely
318 Forward slashes are escaped twice to prevent web servers from prematurely
319 unescaping them. For example, "@foo bar/baz" becomes "@foo%20bar%252Fbaz".
319 unescaping them. For example, "@foo bar/baz" becomes "@foo%20bar%252Fbaz".
320 """
320 """
321 return urlreq.quote(text, safe='/@').replace('/', '%252F')
321 return urlreq.quote(text, safe='/@').replace('/', '%252F')
322
322
323 @templatefilter('rfc3339date')
323 @templatefilter('rfc3339date')
324 def rfc3339date(text):
324 def rfc3339date(text):
325 """Date. Returns a date using the Internet date format
325 """Date. Returns a date using the Internet date format
326 specified in RFC 3339: "2009-08-18T13:00:13+02:00".
326 specified in RFC 3339: "2009-08-18T13:00:13+02:00".
327 """
327 """
328 return util.datestr(text, "%Y-%m-%dT%H:%M:%S%1:%2")
328 return util.datestr(text, "%Y-%m-%dT%H:%M:%S%1:%2")
329
329
330 @templatefilter('rfc822date')
330 @templatefilter('rfc822date')
331 def rfc822date(text):
331 def rfc822date(text):
332 """Date. Returns a date using the same format used in email
332 """Date. Returns a date using the same format used in email
333 headers: "Tue, 18 Aug 2009 13:00:13 +0200".
333 headers: "Tue, 18 Aug 2009 13:00:13 +0200".
334 """
334 """
335 return util.datestr(text, "%a, %d %b %Y %H:%M:%S %1%2")
335 return util.datestr(text, "%a, %d %b %Y %H:%M:%S %1%2")
336
336
337 @templatefilter('short')
337 @templatefilter('short')
338 def short(text):
338 def short(text):
339 """Changeset hash. Returns the short form of a changeset hash,
339 """Changeset hash. Returns the short form of a changeset hash,
340 i.e. a 12 hexadecimal digit string.
340 i.e. a 12 hexadecimal digit string.
341 """
341 """
342 return text[:12]
342 return text[:12]
343
343
344 @templatefilter('shortbisect')
344 @templatefilter('shortbisect')
345 def shortbisect(text):
345 def shortbisect(text):
346 """Any text. Treats `text` as a bisection status, and
346 """Any text. Treats `text` as a bisection status, and
347 returns a single-character representing the status (G: good, B: bad,
347 returns a single-character representing the status (G: good, B: bad,
348 S: skipped, U: untested, I: ignored). Returns single space if `text`
348 S: skipped, U: untested, I: ignored). Returns single space if `text`
349 is not a valid bisection status.
349 is not a valid bisection status.
350 """
350 """
351 return hbisect.shortlabel(text) or ' '
351 return hbisect.shortlabel(text) or ' '
352
352
353 @templatefilter('shortdate')
353 @templatefilter('shortdate')
354 def shortdate(text):
354 def shortdate(text):
355 """Date. Returns a date like "2006-09-18"."""
355 """Date. Returns a date like "2006-09-18"."""
356 return util.shortdate(text)
356 return util.shortdate(text)
357
357
358 @templatefilter('slashpath')
358 @templatefilter('slashpath')
359 def slashpath(path):
359 def slashpath(path):
360 """Any text. Replaces the native path separator with slash."""
360 """Any text. Replaces the native path separator with slash."""
361 return util.pconvert(path)
361 return util.pconvert(path)
362
362
363 @templatefilter('splitlines')
363 @templatefilter('splitlines')
364 def splitlines(text):
364 def splitlines(text):
365 """Any text. Split text into a list of lines."""
365 """Any text. Split text into a list of lines."""
366 return templatekw.hybridlist(text.splitlines(), name='line')
366 return templatekw.hybridlist(text.splitlines(), name='line')
367
367
368 @templatefilter('stringescape')
368 @templatefilter('stringescape')
369 def stringescape(text):
369 def stringescape(text):
370 return util.escapestr(text)
370 return util.escapestr(text)
371
371
372 @templatefilter('stringify')
372 @templatefilter('stringify')
373 def stringify(thing):
373 def stringify(thing):
374 """Any type. Turns the value into text by converting values into
374 """Any type. Turns the value into text by converting values into
375 text and concatenating them.
375 text and concatenating them.
376 """
376 """
377 thing = templatekw.unwraphybrid(thing)
377 thing = templatekw.unwraphybrid(thing)
378 if util.safehasattr(thing, '__iter__') and not isinstance(thing, bytes):
378 if util.safehasattr(thing, '__iter__') and not isinstance(thing, bytes):
379 return "".join([stringify(t) for t in thing if t is not None])
379 return "".join([stringify(t) for t in thing if t is not None])
380 if thing is None:
380 if thing is None:
381 return ""
381 return ""
382 return pycompat.bytestr(thing)
382 return pycompat.bytestr(thing)
383
383
384 @templatefilter('stripdir')
384 @templatefilter('stripdir')
385 def stripdir(text):
385 def stripdir(text):
386 """Treat the text as path and strip a directory level, if
386 """Treat the text as path and strip a directory level, if
387 possible. For example, "foo" and "foo/bar" becomes "foo".
387 possible. For example, "foo" and "foo/bar" becomes "foo".
388 """
388 """
389 dir = os.path.dirname(text)
389 dir = os.path.dirname(text)
390 if dir == "":
390 if dir == "":
391 return os.path.basename(text)
391 return os.path.basename(text)
392 else:
392 else:
393 return dir
393 return dir
394
394
395 @templatefilter('tabindent')
395 @templatefilter('tabindent')
396 def tabindent(text):
396 def tabindent(text):
397 """Any text. Returns the text, with every non-empty line
397 """Any text. Returns the text, with every non-empty line
398 except the first starting with a tab character.
398 except the first starting with a tab character.
399 """
399 """
400 return indent(text, '\t')
400 return indent(text, '\t')
401
401
402 @templatefilter('upper')
402 @templatefilter('upper')
403 def upper(text):
403 def upper(text):
404 """Any text. Converts the text to uppercase."""
404 """Any text. Converts the text to uppercase."""
405 return encoding.upper(text)
405 return encoding.upper(text)
406
406
407 @templatefilter('urlescape')
407 @templatefilter('urlescape')
408 def urlescape(text):
408 def urlescape(text):
409 """Any text. Escapes all "special" characters. For example,
409 """Any text. Escapes all "special" characters. For example,
410 "foo bar" becomes "foo%20bar".
410 "foo bar" becomes "foo%20bar".
411 """
411 """
412 return urlreq.quote(text)
412 return urlreq.quote(text)
413
413
414 @templatefilter('user')
414 @templatefilter('user')
415 def userfilter(text):
415 def userfilter(text):
416 """Any text. Returns a short representation of a user name or email
416 """Any text. Returns a short representation of a user name or email
417 address."""
417 address."""
418 return util.shortuser(text)
418 return util.shortuser(text)
419
419
420 @templatefilter('emailuser')
420 @templatefilter('emailuser')
421 def emailuser(text):
421 def emailuser(text):
422 """Any text. Returns the user portion of an email address."""
422 """Any text. Returns the user portion of an email address."""
423 return util.emailuser(text)
423 return util.emailuser(text)
424
424
425 @templatefilter('utf8')
425 @templatefilter('utf8')
426 def utf8(text):
426 def utf8(text):
427 """Any text. Converts from the local character encoding to UTF-8."""
427 """Any text. Converts from the local character encoding to UTF-8."""
428 return encoding.fromlocal(text)
428 return encoding.fromlocal(text)
429
429
430 @templatefilter('xmlescape')
430 @templatefilter('xmlescape')
431 def xmlescape(text):
431 def xmlescape(text):
432 text = (text
432 text = (text
433 .replace('&', '&amp;')
433 .replace('&', '&amp;')
434 .replace('<', '&lt;')
434 .replace('<', '&lt;')
435 .replace('>', '&gt;')
435 .replace('>', '&gt;')
436 .replace('"', '&quot;')
436 .replace('"', '&quot;')
437 .replace("'", '&#39;')) # &apos; invalid in HTML
437 .replace("'", '&#39;')) # &apos; invalid in HTML
438 return re.sub('[\x00-\x08\x0B\x0C\x0E-\x1F]', ' ', text)
438 return re.sub('[\x00-\x08\x0B\x0C\x0E-\x1F]', ' ', text)
439
439
440 def websub(text, websubtable):
440 def websub(text, websubtable):
441 """:websub: Any text. Only applies to hgweb. Applies the regular
441 """:websub: Any text. Only applies to hgweb. Applies the regular
442 expression replacements defined in the websub section.
442 expression replacements defined in the websub section.
443 """
443 """
444 if websubtable:
444 if websubtable:
445 for regexp, format in websubtable:
445 for regexp, format in websubtable:
446 text = regexp.sub(format, text)
446 text = regexp.sub(format, text)
447 return text
447 return text
448
448
449 def loadfilter(ui, extname, registrarobj):
449 def loadfilter(ui, extname, registrarobj):
450 """Load template filter from specified registrarobj
450 """Load template filter from specified registrarobj
451 """
451 """
452 for name, func in registrarobj._table.iteritems():
452 for name, func in registrarobj._table.iteritems():
453 filters[name] = func
453 filters[name] = func
454
454
455 # tell hggettext to extract docstrings from these functions:
455 # tell hggettext to extract docstrings from these functions:
456 i18nfunctions = filters.values()
456 i18nfunctions = filters.values()
General Comments 0
You need to be logged in to leave comments. Login now