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