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