##// END OF EJS Templates
templatefilters: undeprecate hgdate...
Yuya Nishihara -
r38319:74b4a540 @39 default
parent child Browse files
Show More
@@ -1,475 +1,475 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('commonprefix')
102 @templatefilter('commonprefix')
103 def commonprefix(filelist):
103 def commonprefix(filelist):
104 """List of text. Treats each list item as file name with /
104 """List of text. Treats each list item as file name with /
105 as path separator and returns the longest common directory
105 as path separator and returns the longest common directory
106 prefix shared by all list items.
106 prefix shared by all list items.
107 Returns the empty string if no common prefix exists.
107 Returns the empty string if no common prefix exists.
108
108
109 The list items are not normalized, i.e. "foo/../bar" is handled as
109 The list items are not normalized, i.e. "foo/../bar" is handled as
110 file "bar" in the directory "foo/..". Leading slashes are ignored.
110 file "bar" in the directory "foo/..". Leading slashes are ignored.
111
111
112 For example, ["foo/bar/baz", "foo/baz/bar"] becomes "foo" and
112 For example, ["foo/bar/baz", "foo/baz/bar"] becomes "foo" and
113 ["foo/bar", "baz"] becomes "".
113 ["foo/bar", "baz"] becomes "".
114 """
114 """
115 def common(a, b):
115 def common(a, b):
116 if len(a) > len(b):
116 if len(a) > len(b):
117 a = b[:len(a)]
117 a = b[:len(a)]
118 elif len(b) > len(a):
118 elif len(b) > len(a):
119 b = b[:len(a)]
119 b = b[:len(a)]
120 if a == b:
120 if a == b:
121 return a
121 return a
122 for i in xrange(len(a)):
122 for i in xrange(len(a)):
123 if a[i] != b[i]:
123 if a[i] != b[i]:
124 return a[:i]
124 return a[:i]
125 return a
125 return a
126 try:
126 try:
127 if not filelist:
127 if not filelist:
128 return ""
128 return ""
129 dirlist = [f.lstrip('/').split('/')[:-1] for f in filelist]
129 dirlist = [f.lstrip('/').split('/')[:-1] for f in filelist]
130 if len(dirlist) == 1:
130 if len(dirlist) == 1:
131 return '/'.join(dirlist[0])
131 return '/'.join(dirlist[0])
132 a = min(dirlist)
132 a = min(dirlist)
133 b = max(dirlist)
133 b = max(dirlist)
134 # The common prefix of a and b is shared with all
134 # The common prefix of a and b is shared with all
135 # elements of the list since Python sorts lexicographical
135 # elements of the list since Python sorts lexicographical
136 # and [1, x] after [1].
136 # and [1, x] after [1].
137 return '/'.join(common(a, b))
137 return '/'.join(common(a, b))
138 except TypeError:
138 except TypeError:
139 raise error.ParseError(_('argument is not a list of text'))
139 raise error.ParseError(_('argument is not a list of text'))
140
140
141 @templatefilter('count')
141 @templatefilter('count')
142 def count(i):
142 def count(i):
143 """List or text. Returns the length as an integer."""
143 """List or text. Returns the length as an integer."""
144 try:
144 try:
145 return len(i)
145 return len(i)
146 except TypeError:
146 except TypeError:
147 raise error.ParseError(_('not countable'))
147 raise error.ParseError(_('not countable'))
148
148
149 @templatefilter('dirname', intype=bytes)
149 @templatefilter('dirname', intype=bytes)
150 def dirname(path):
150 def dirname(path):
151 """Any text. Treats the text as a path, and strips the last
151 """Any text. Treats the text as a path, and strips the last
152 component of the path after splitting by the path separator.
152 component of the path after splitting by the path separator.
153 """
153 """
154 return os.path.dirname(path)
154 return os.path.dirname(path)
155
155
156 @templatefilter('domain', intype=bytes)
156 @templatefilter('domain', intype=bytes)
157 def domain(author):
157 def domain(author):
158 """Any text. Finds the first string that looks like an email
158 """Any text. Finds the first string that looks like an email
159 address, and extracts just the domain component. Example: ``User
159 address, and extracts just the domain component. Example: ``User
160 <user@example.com>`` becomes ``example.com``.
160 <user@example.com>`` becomes ``example.com``.
161 """
161 """
162 f = author.find('@')
162 f = author.find('@')
163 if f == -1:
163 if f == -1:
164 return ''
164 return ''
165 author = author[f + 1:]
165 author = author[f + 1:]
166 f = author.find('>')
166 f = author.find('>')
167 if f >= 0:
167 if f >= 0:
168 author = author[:f]
168 author = author[:f]
169 return author
169 return author
170
170
171 @templatefilter('email', intype=bytes)
171 @templatefilter('email', intype=bytes)
172 def email(text):
172 def email(text):
173 """Any text. Extracts the first string that looks like an email
173 """Any text. Extracts the first string that looks like an email
174 address. Example: ``User <user@example.com>`` becomes
174 address. Example: ``User <user@example.com>`` becomes
175 ``user@example.com``.
175 ``user@example.com``.
176 """
176 """
177 return stringutil.email(text)
177 return stringutil.email(text)
178
178
179 @templatefilter('escape', intype=bytes)
179 @templatefilter('escape', intype=bytes)
180 def escape(text):
180 def escape(text):
181 """Any text. Replaces the special XML/XHTML characters "&", "<"
181 """Any text. Replaces the special XML/XHTML characters "&", "<"
182 and ">" with XML entities, and filters out NUL characters.
182 and ">" with XML entities, and filters out NUL characters.
183 """
183 """
184 return url.escape(text.replace('\0', ''), True)
184 return url.escape(text.replace('\0', ''), True)
185
185
186 para_re = None
186 para_re = None
187 space_re = None
187 space_re = None
188
188
189 def fill(text, width, initindent='', hangindent=''):
189 def fill(text, width, initindent='', hangindent=''):
190 '''fill many paragraphs with optional indentation.'''
190 '''fill many paragraphs with optional indentation.'''
191 global para_re, space_re
191 global para_re, space_re
192 if para_re is None:
192 if para_re is None:
193 para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M)
193 para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M)
194 space_re = re.compile(br' +')
194 space_re = re.compile(br' +')
195
195
196 def findparas():
196 def findparas():
197 start = 0
197 start = 0
198 while True:
198 while True:
199 m = para_re.search(text, start)
199 m = para_re.search(text, start)
200 if not m:
200 if not m:
201 uctext = encoding.unifromlocal(text[start:])
201 uctext = encoding.unifromlocal(text[start:])
202 w = len(uctext)
202 w = len(uctext)
203 while 0 < w and uctext[w - 1].isspace():
203 while 0 < w and uctext[w - 1].isspace():
204 w -= 1
204 w -= 1
205 yield (encoding.unitolocal(uctext[:w]),
205 yield (encoding.unitolocal(uctext[:w]),
206 encoding.unitolocal(uctext[w:]))
206 encoding.unitolocal(uctext[w:]))
207 break
207 break
208 yield text[start:m.start(0)], m.group(1)
208 yield text[start:m.start(0)], m.group(1)
209 start = m.end(1)
209 start = m.end(1)
210
210
211 return "".join([stringutil.wrap(space_re.sub(' ',
211 return "".join([stringutil.wrap(space_re.sub(' ',
212 stringutil.wrap(para, width)),
212 stringutil.wrap(para, width)),
213 width, initindent, hangindent) + rest
213 width, initindent, hangindent) + rest
214 for para, rest in findparas()])
214 for para, rest in findparas()])
215
215
216 @templatefilter('fill68', intype=bytes)
216 @templatefilter('fill68', intype=bytes)
217 def fill68(text):
217 def fill68(text):
218 """Any text. Wraps the text to fit in 68 columns."""
218 """Any text. Wraps the text to fit in 68 columns."""
219 return fill(text, 68)
219 return fill(text, 68)
220
220
221 @templatefilter('fill76', intype=bytes)
221 @templatefilter('fill76', intype=bytes)
222 def fill76(text):
222 def fill76(text):
223 """Any text. Wraps the text to fit in 76 columns."""
223 """Any text. Wraps the text to fit in 76 columns."""
224 return fill(text, 76)
224 return fill(text, 76)
225
225
226 @templatefilter('firstline', intype=bytes)
226 @templatefilter('firstline', intype=bytes)
227 def firstline(text):
227 def firstline(text):
228 """Any text. Returns the first line of text."""
228 """Any text. Returns the first line of text."""
229 try:
229 try:
230 return text.splitlines(True)[0].rstrip('\r\n')
230 return text.splitlines(True)[0].rstrip('\r\n')
231 except IndexError:
231 except IndexError:
232 return ''
232 return ''
233
233
234 @templatefilter('hex', intype=bytes)
234 @templatefilter('hex', intype=bytes)
235 def hexfilter(text):
235 def hexfilter(text):
236 """Any text. Convert a binary Mercurial node identifier into
236 """Any text. Convert a binary Mercurial node identifier into
237 its long hexadecimal representation.
237 its long hexadecimal representation.
238 """
238 """
239 return node.hex(text)
239 return node.hex(text)
240
240
241 @templatefilter('hgdate', intype=templateutil.date)
241 @templatefilter('hgdate', intype=templateutil.date)
242 def hgdate(text):
242 def hgdate(text):
243 """Date. Returns the date as a pair of numbers: "1157407993
243 """Date. Returns the date as a pair of numbers: "1157407993
244 25200" (Unix timestamp, timezone offset). (DEPRECATED)
244 25200" (Unix timestamp, timezone offset).
245 """
245 """
246 return "%d %d" % text
246 return "%d %d" % text
247
247
248 @templatefilter('isodate', intype=templateutil.date)
248 @templatefilter('isodate', intype=templateutil.date)
249 def isodate(text):
249 def isodate(text):
250 """Date. Returns the date in ISO 8601 format: "2009-08-18 13:00
250 """Date. Returns the date in ISO 8601 format: "2009-08-18 13:00
251 +0200".
251 +0200".
252 """
252 """
253 return dateutil.datestr(text, '%Y-%m-%d %H:%M %1%2')
253 return dateutil.datestr(text, '%Y-%m-%d %H:%M %1%2')
254
254
255 @templatefilter('isodatesec', intype=templateutil.date)
255 @templatefilter('isodatesec', intype=templateutil.date)
256 def isodatesec(text):
256 def isodatesec(text):
257 """Date. Returns the date in ISO 8601 format, including
257 """Date. Returns the date in ISO 8601 format, including
258 seconds: "2009-08-18 13:00:13 +0200". See also the rfc3339date
258 seconds: "2009-08-18 13:00:13 +0200". See also the rfc3339date
259 filter.
259 filter.
260 """
260 """
261 return dateutil.datestr(text, '%Y-%m-%d %H:%M:%S %1%2')
261 return dateutil.datestr(text, '%Y-%m-%d %H:%M:%S %1%2')
262
262
263 def indent(text, prefix):
263 def indent(text, prefix):
264 '''indent each non-empty line of text after first with prefix.'''
264 '''indent each non-empty line of text after first with prefix.'''
265 lines = text.splitlines()
265 lines = text.splitlines()
266 num_lines = len(lines)
266 num_lines = len(lines)
267 endswithnewline = text[-1:] == '\n'
267 endswithnewline = text[-1:] == '\n'
268 def indenter():
268 def indenter():
269 for i in xrange(num_lines):
269 for i in xrange(num_lines):
270 l = lines[i]
270 l = lines[i]
271 if i and l.strip():
271 if i and l.strip():
272 yield prefix
272 yield prefix
273 yield l
273 yield l
274 if i < num_lines - 1 or endswithnewline:
274 if i < num_lines - 1 or endswithnewline:
275 yield '\n'
275 yield '\n'
276 return "".join(indenter())
276 return "".join(indenter())
277
277
278 @templatefilter('json')
278 @templatefilter('json')
279 def json(obj, paranoid=True):
279 def json(obj, paranoid=True):
280 """Any object. Serializes the object to a JSON formatted text."""
280 """Any object. Serializes the object to a JSON formatted text."""
281 if obj is None:
281 if obj is None:
282 return 'null'
282 return 'null'
283 elif obj is False:
283 elif obj is False:
284 return 'false'
284 return 'false'
285 elif obj is True:
285 elif obj is True:
286 return 'true'
286 return 'true'
287 elif isinstance(obj, (int, long, float)):
287 elif isinstance(obj, (int, long, float)):
288 return pycompat.bytestr(obj)
288 return pycompat.bytestr(obj)
289 elif isinstance(obj, bytes):
289 elif isinstance(obj, bytes):
290 return '"%s"' % encoding.jsonescape(obj, paranoid=paranoid)
290 return '"%s"' % encoding.jsonescape(obj, paranoid=paranoid)
291 elif isinstance(obj, type(u'')):
291 elif isinstance(obj, type(u'')):
292 raise error.ProgrammingError(
292 raise error.ProgrammingError(
293 'Mercurial only does output with bytes: %r' % obj)
293 'Mercurial only does output with bytes: %r' % obj)
294 elif util.safehasattr(obj, 'keys'):
294 elif util.safehasattr(obj, 'keys'):
295 out = ['"%s": %s' % (encoding.jsonescape(k, paranoid=paranoid),
295 out = ['"%s": %s' % (encoding.jsonescape(k, paranoid=paranoid),
296 json(v, paranoid))
296 json(v, paranoid))
297 for k, v in sorted(obj.iteritems())]
297 for k, v in sorted(obj.iteritems())]
298 return '{' + ', '.join(out) + '}'
298 return '{' + ', '.join(out) + '}'
299 elif util.safehasattr(obj, '__iter__'):
299 elif util.safehasattr(obj, '__iter__'):
300 out = [json(i, paranoid) for i in obj]
300 out = [json(i, paranoid) for i in obj]
301 return '[' + ', '.join(out) + ']'
301 return '[' + ', '.join(out) + ']'
302 raise error.ProgrammingError('cannot encode %r' % obj)
302 raise error.ProgrammingError('cannot encode %r' % obj)
303
303
304 @templatefilter('lower', intype=bytes)
304 @templatefilter('lower', intype=bytes)
305 def lower(text):
305 def lower(text):
306 """Any text. Converts the text to lowercase."""
306 """Any text. Converts the text to lowercase."""
307 return encoding.lower(text)
307 return encoding.lower(text)
308
308
309 @templatefilter('nonempty', intype=bytes)
309 @templatefilter('nonempty', intype=bytes)
310 def nonempty(text):
310 def nonempty(text):
311 """Any text. Returns '(none)' if the string is empty."""
311 """Any text. Returns '(none)' if the string is empty."""
312 return text or "(none)"
312 return text or "(none)"
313
313
314 @templatefilter('obfuscate', intype=bytes)
314 @templatefilter('obfuscate', intype=bytes)
315 def obfuscate(text):
315 def obfuscate(text):
316 """Any text. Returns the input text rendered as a sequence of
316 """Any text. Returns the input text rendered as a sequence of
317 XML entities.
317 XML entities.
318 """
318 """
319 text = unicode(text, pycompat.sysstr(encoding.encoding), r'replace')
319 text = unicode(text, pycompat.sysstr(encoding.encoding), r'replace')
320 return ''.join(['&#%d;' % ord(c) for c in text])
320 return ''.join(['&#%d;' % ord(c) for c in text])
321
321
322 @templatefilter('permissions', intype=bytes)
322 @templatefilter('permissions', intype=bytes)
323 def permissions(flags):
323 def permissions(flags):
324 if "l" in flags:
324 if "l" in flags:
325 return "lrwxrwxrwx"
325 return "lrwxrwxrwx"
326 if "x" in flags:
326 if "x" in flags:
327 return "-rwxr-xr-x"
327 return "-rwxr-xr-x"
328 return "-rw-r--r--"
328 return "-rw-r--r--"
329
329
330 @templatefilter('person', intype=bytes)
330 @templatefilter('person', intype=bytes)
331 def person(author):
331 def person(author):
332 """Any text. Returns the name before an email address,
332 """Any text. Returns the name before an email address,
333 interpreting it as per RFC 5322.
333 interpreting it as per RFC 5322.
334 """
334 """
335 return stringutil.person(author)
335 return stringutil.person(author)
336
336
337 @templatefilter('revescape', intype=bytes)
337 @templatefilter('revescape', intype=bytes)
338 def revescape(text):
338 def revescape(text):
339 """Any text. Escapes all "special" characters, except @.
339 """Any text. Escapes all "special" characters, except @.
340 Forward slashes are escaped twice to prevent web servers from prematurely
340 Forward slashes are escaped twice to prevent web servers from prematurely
341 unescaping them. For example, "@foo bar/baz" becomes "@foo%20bar%252Fbaz".
341 unescaping them. For example, "@foo bar/baz" becomes "@foo%20bar%252Fbaz".
342 """
342 """
343 return urlreq.quote(text, safe='/@').replace('/', '%252F')
343 return urlreq.quote(text, safe='/@').replace('/', '%252F')
344
344
345 @templatefilter('rfc3339date', intype=templateutil.date)
345 @templatefilter('rfc3339date', intype=templateutil.date)
346 def rfc3339date(text):
346 def rfc3339date(text):
347 """Date. Returns a date using the Internet date format
347 """Date. Returns a date using the Internet date format
348 specified in RFC 3339: "2009-08-18T13:00:13+02:00".
348 specified in RFC 3339: "2009-08-18T13:00:13+02:00".
349 """
349 """
350 return dateutil.datestr(text, "%Y-%m-%dT%H:%M:%S%1:%2")
350 return dateutil.datestr(text, "%Y-%m-%dT%H:%M:%S%1:%2")
351
351
352 @templatefilter('rfc822date', intype=templateutil.date)
352 @templatefilter('rfc822date', intype=templateutil.date)
353 def rfc822date(text):
353 def rfc822date(text):
354 """Date. Returns a date using the same format used in email
354 """Date. Returns a date using the same format used in email
355 headers: "Tue, 18 Aug 2009 13:00:13 +0200".
355 headers: "Tue, 18 Aug 2009 13:00:13 +0200".
356 """
356 """
357 return dateutil.datestr(text, "%a, %d %b %Y %H:%M:%S %1%2")
357 return dateutil.datestr(text, "%a, %d %b %Y %H:%M:%S %1%2")
358
358
359 @templatefilter('short', intype=bytes)
359 @templatefilter('short', intype=bytes)
360 def short(text):
360 def short(text):
361 """Changeset hash. Returns the short form of a changeset hash,
361 """Changeset hash. Returns the short form of a changeset hash,
362 i.e. a 12 hexadecimal digit string.
362 i.e. a 12 hexadecimal digit string.
363 """
363 """
364 return text[:12]
364 return text[:12]
365
365
366 @templatefilter('shortbisect', intype=bytes)
366 @templatefilter('shortbisect', intype=bytes)
367 def shortbisect(label):
367 def shortbisect(label):
368 """Any text. Treats `label` as a bisection status, and
368 """Any text. Treats `label` as a bisection status, and
369 returns a single-character representing the status (G: good, B: bad,
369 returns a single-character representing the status (G: good, B: bad,
370 S: skipped, U: untested, I: ignored). Returns single space if `text`
370 S: skipped, U: untested, I: ignored). Returns single space if `text`
371 is not a valid bisection status.
371 is not a valid bisection status.
372 """
372 """
373 if label:
373 if label:
374 return label[0:1].upper()
374 return label[0:1].upper()
375 return ' '
375 return ' '
376
376
377 @templatefilter('shortdate', intype=templateutil.date)
377 @templatefilter('shortdate', intype=templateutil.date)
378 def shortdate(text):
378 def shortdate(text):
379 """Date. Returns a date like "2006-09-18"."""
379 """Date. Returns a date like "2006-09-18"."""
380 return dateutil.shortdate(text)
380 return dateutil.shortdate(text)
381
381
382 @templatefilter('slashpath', intype=bytes)
382 @templatefilter('slashpath', intype=bytes)
383 def slashpath(path):
383 def slashpath(path):
384 """Any text. Replaces the native path separator with slash."""
384 """Any text. Replaces the native path separator with slash."""
385 return util.pconvert(path)
385 return util.pconvert(path)
386
386
387 @templatefilter('splitlines', intype=bytes)
387 @templatefilter('splitlines', intype=bytes)
388 def splitlines(text):
388 def splitlines(text):
389 """Any text. Split text into a list of lines."""
389 """Any text. Split text into a list of lines."""
390 return templateutil.hybridlist(text.splitlines(), name='line')
390 return templateutil.hybridlist(text.splitlines(), name='line')
391
391
392 @templatefilter('stringescape', intype=bytes)
392 @templatefilter('stringescape', intype=bytes)
393 def stringescape(text):
393 def stringescape(text):
394 return stringutil.escapestr(text)
394 return stringutil.escapestr(text)
395
395
396 @templatefilter('stringify', intype=bytes)
396 @templatefilter('stringify', intype=bytes)
397 def stringify(thing):
397 def stringify(thing):
398 """Any type. Turns the value into text by converting values into
398 """Any type. Turns the value into text by converting values into
399 text and concatenating them.
399 text and concatenating them.
400 """
400 """
401 return thing # coerced by the intype
401 return thing # coerced by the intype
402
402
403 @templatefilter('stripdir', intype=bytes)
403 @templatefilter('stripdir', intype=bytes)
404 def stripdir(text):
404 def stripdir(text):
405 """Treat the text as path and strip a directory level, if
405 """Treat the text as path and strip a directory level, if
406 possible. For example, "foo" and "foo/bar" becomes "foo".
406 possible. For example, "foo" and "foo/bar" becomes "foo".
407 """
407 """
408 dir = os.path.dirname(text)
408 dir = os.path.dirname(text)
409 if dir == "":
409 if dir == "":
410 return os.path.basename(text)
410 return os.path.basename(text)
411 else:
411 else:
412 return dir
412 return dir
413
413
414 @templatefilter('tabindent', intype=bytes)
414 @templatefilter('tabindent', intype=bytes)
415 def tabindent(text):
415 def tabindent(text):
416 """Any text. Returns the text, with every non-empty line
416 """Any text. Returns the text, with every non-empty line
417 except the first starting with a tab character.
417 except the first starting with a tab character.
418 """
418 """
419 return indent(text, '\t')
419 return indent(text, '\t')
420
420
421 @templatefilter('upper', intype=bytes)
421 @templatefilter('upper', intype=bytes)
422 def upper(text):
422 def upper(text):
423 """Any text. Converts the text to uppercase."""
423 """Any text. Converts the text to uppercase."""
424 return encoding.upper(text)
424 return encoding.upper(text)
425
425
426 @templatefilter('urlescape', intype=bytes)
426 @templatefilter('urlescape', intype=bytes)
427 def urlescape(text):
427 def urlescape(text):
428 """Any text. Escapes all "special" characters. For example,
428 """Any text. Escapes all "special" characters. For example,
429 "foo bar" becomes "foo%20bar".
429 "foo bar" becomes "foo%20bar".
430 """
430 """
431 return urlreq.quote(text)
431 return urlreq.quote(text)
432
432
433 @templatefilter('user', intype=bytes)
433 @templatefilter('user', intype=bytes)
434 def userfilter(text):
434 def userfilter(text):
435 """Any text. Returns a short representation of a user name or email
435 """Any text. Returns a short representation of a user name or email
436 address."""
436 address."""
437 return stringutil.shortuser(text)
437 return stringutil.shortuser(text)
438
438
439 @templatefilter('emailuser', intype=bytes)
439 @templatefilter('emailuser', intype=bytes)
440 def emailuser(text):
440 def emailuser(text):
441 """Any text. Returns the user portion of an email address."""
441 """Any text. Returns the user portion of an email address."""
442 return stringutil.emailuser(text)
442 return stringutil.emailuser(text)
443
443
444 @templatefilter('utf8', intype=bytes)
444 @templatefilter('utf8', intype=bytes)
445 def utf8(text):
445 def utf8(text):
446 """Any text. Converts from the local character encoding to UTF-8."""
446 """Any text. Converts from the local character encoding to UTF-8."""
447 return encoding.fromlocal(text)
447 return encoding.fromlocal(text)
448
448
449 @templatefilter('xmlescape', intype=bytes)
449 @templatefilter('xmlescape', intype=bytes)
450 def xmlescape(text):
450 def xmlescape(text):
451 text = (text
451 text = (text
452 .replace('&', '&amp;')
452 .replace('&', '&amp;')
453 .replace('<', '&lt;')
453 .replace('<', '&lt;')
454 .replace('>', '&gt;')
454 .replace('>', '&gt;')
455 .replace('"', '&quot;')
455 .replace('"', '&quot;')
456 .replace("'", '&#39;')) # &apos; invalid in HTML
456 .replace("'", '&#39;')) # &apos; invalid in HTML
457 return re.sub('[\x00-\x08\x0B\x0C\x0E-\x1F]', ' ', text)
457 return re.sub('[\x00-\x08\x0B\x0C\x0E-\x1F]', ' ', text)
458
458
459 def websub(text, websubtable):
459 def websub(text, websubtable):
460 """:websub: Any text. Only applies to hgweb. Applies the regular
460 """:websub: Any text. Only applies to hgweb. Applies the regular
461 expression replacements defined in the websub section.
461 expression replacements defined in the websub section.
462 """
462 """
463 if websubtable:
463 if websubtable:
464 for regexp, format in websubtable:
464 for regexp, format in websubtable:
465 text = regexp.sub(format, text)
465 text = regexp.sub(format, text)
466 return text
466 return text
467
467
468 def loadfilter(ui, extname, registrarobj):
468 def loadfilter(ui, extname, registrarobj):
469 """Load template filter from specified registrarobj
469 """Load template filter from specified registrarobj
470 """
470 """
471 for name, func in registrarobj._table.iteritems():
471 for name, func in registrarobj._table.iteritems():
472 filters[name] = func
472 filters[name] = func
473
473
474 # tell hggettext to extract docstrings from these functions:
474 # tell hggettext to extract docstrings from these functions:
475 i18nfunctions = filters.values()
475 i18nfunctions = filters.values()
General Comments 0
You need to be logged in to leave comments. Login now