comparison mercurial/templatefilters.py @ 13591:264f292a0c6f

templatefilters: move doc from templates.txt to docstrings
author Patrick Mezard <pmezard@gmail.com>
date Sat, 12 Mar 2011 12:46:31 +0100
parents 1a752dcfe062
children cc4721ed7a2a
comparison
equal deleted inserted replaced
13590:1a752dcfe062 13591:264f292a0c6f
5 # This software may be used and distributed according to the terms of the 5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version. 6 # GNU General Public License version 2 or any later version.
7 7
8 import cgi, re, os, time, urllib 8 import cgi, re, os, time, urllib
9 import encoding, node, util 9 import encoding, node, util
10 from i18n import gettext
10 11
11 def addbreaks(text): 12 def addbreaks(text):
12 '''replace raw newlines with xhtml line breaks.''' 13 """:addbreaks: Any text. Add an XHTML "<br />" tag before the end of
14 every line except the last.
15 """
13 return text.replace('\n', '<br/>\n') 16 return text.replace('\n', '<br/>\n')
14 17
15 agescales = [("year", 3600 * 24 * 365), 18 agescales = [("year", 3600 * 24 * 365),
16 ("month", 3600 * 24 * 30), 19 ("month", 3600 * 24 * 30),
17 ("week", 3600 * 24 * 7), 20 ("week", 3600 * 24 * 7),
19 ("hour", 3600), 22 ("hour", 3600),
20 ("minute", 60), 23 ("minute", 60),
21 ("second", 1)] 24 ("second", 1)]
22 25
23 def age(date): 26 def age(date):
24 '''turn a (timestamp, tzoff) tuple into an age string.''' 27 """:age: Date. Returns a human-readable date/time difference between the
28 given date/time and the current date/time.
29 """
25 30
26 def plural(t, c): 31 def plural(t, c):
27 if c == 1: 32 if c == 1:
28 return t 33 return t
29 return t + "s" 34 return t + "s"
43 n = delta // s 48 n = delta // s
44 if n >= 2 or s == 1: 49 if n >= 2 or s == 1:
45 return '%s ago' % fmt(t, n) 50 return '%s ago' % fmt(t, n)
46 51
47 def basename(path): 52 def basename(path):
53 """:basename: Any text. Treats the text as a path, and returns the last
54 component of the path after splitting by the path separator
55 (ignoring trailing separators). For example, "foo/bar/baz" becomes
56 "baz" and "foo/bar//" becomes "bar".
57 """
48 return os.path.basename(path) 58 return os.path.basename(path)
49 59
50 def datefilter(text): 60 def datefilter(text):
61 """:date: Date. Returns a date in a Unix date format, including the
62 timezone: "Mon Sep 04 15:13:13 2006 0700".
63 """
51 return util.datestr(text) 64 return util.datestr(text)
52 65
53 def domain(author): 66 def domain(author):
54 '''get domain of author, or empty string if none.''' 67 """:domain: Any text. Finds the first string that looks like an email
68 address, and extracts just the domain component. Example: ``User
69 <user@example.com>`` becomes ``example.com``.
70 """
55 f = author.find('@') 71 f = author.find('@')
56 if f == -1: 72 if f == -1:
57 return '' 73 return ''
58 author = author[f + 1:] 74 author = author[f + 1:]
59 f = author.find('>') 75 f = author.find('>')
60 if f >= 0: 76 if f >= 0:
61 author = author[:f] 77 author = author[:f]
62 return author 78 return author
63 79
64 def email(text): 80 def email(text):
81 """:email: Any text. Extracts the first string that looks like an email
82 address. Example: ``User <user@example.com>`` becomes
83 ``user@example.com``.
84 """
65 return util.email(text) 85 return util.email(text)
66 86
67 def escape(text): 87 def escape(text):
88 """:escape: Any text. Replaces the special XML/XHTML characters "&", "<"
89 and ">" with XML entities.
90 """
68 return cgi.escape(text, True) 91 return cgi.escape(text, True)
69 92
70 para_re = None 93 para_re = None
71 space_re = None 94 space_re = None
72 95
94 117
95 return "".join([space_re.sub(' ', util.wrap(para, width=width)) + rest 118 return "".join([space_re.sub(' ', util.wrap(para, width=width)) + rest
96 for para, rest in findparas()]) 119 for para, rest in findparas()])
97 120
98 def fill68(text): 121 def fill68(text):
122 """:fill68: Any text. Wraps the text to fit in 68 columns."""
99 return fill(text, 68) 123 return fill(text, 68)
100 124
101 def fill76(text): 125 def fill76(text):
126 """:fill76: Any text. Wraps the text to fit in 76 columns."""
102 return fill(text, 76) 127 return fill(text, 76)
103 128
104 def firstline(text): 129 def firstline(text):
105 '''return the first line of text''' 130 """:firstline: Any text. Returns the first line of text."""
106 try: 131 try:
107 return text.splitlines(True)[0].rstrip('\r\n') 132 return text.splitlines(True)[0].rstrip('\r\n')
108 except IndexError: 133 except IndexError:
109 return '' 134 return ''
110 135
111 def hexfilter(text): 136 def hexfilter(text):
137 """:hex: Any text. Convert a binary Mercurial node identifier into
138 its long hexadecimal representation.
139 """
112 return node.hex(text) 140 return node.hex(text)
113 141
114 def hgdate(text): 142 def hgdate(text):
143 """:hgdate: Date. Returns the date as a pair of numbers: "1157407993
144 25200" (Unix timestamp, timezone offset).
145 """
115 return "%d %d" % text 146 return "%d %d" % text
116 147
117 def isodate(text): 148 def isodate(text):
149 """:isodate: Date. Returns the date in ISO 8601 format: "2009-08-18 13:00
150 +0200".
151 """
118 return util.datestr(text, '%Y-%m-%d %H:%M %1%2') 152 return util.datestr(text, '%Y-%m-%d %H:%M %1%2')
119 153
120 def isodatesec(text): 154 def isodatesec(text):
155 """:isodatesec: Date. Returns the date in ISO 8601 format, including
156 seconds: "2009-08-18 13:00:13 +0200". See also the rfc3339date
157 filter.
158 """
121 return util.datestr(text, '%Y-%m-%d %H:%M:%S %1%2') 159 return util.datestr(text, '%Y-%m-%d %H:%M:%S %1%2')
122 160
123 def indent(text, prefix): 161 def indent(text, prefix):
124 '''indent each non-empty line of text after first with prefix.''' 162 '''indent each non-empty line of text after first with prefix.'''
125 lines = text.splitlines() 163 lines = text.splitlines()
174 for k, v in _escapes: 212 for k, v in _escapes:
175 s = s.replace(k, v) 213 s = s.replace(k, v)
176 return ''.join(_uescape(c) for c in s) 214 return ''.join(_uescape(c) for c in s)
177 215
178 def localdate(text): 216 def localdate(text):
217 """:localdate: Date. Converts a date to local date."""
179 return (text[0], util.makedate()[1]) 218 return (text[0], util.makedate()[1])
180 219
181 def nonempty(str): 220 def nonempty(str):
221 """:nonempty: Any text. Returns '(none)' if the string is empty."""
182 return str or "(none)" 222 return str or "(none)"
183 223
184 def obfuscate(text): 224 def obfuscate(text):
225 """:obfuscate: Any text. Returns the input text rendered as a sequence of
226 XML entities.
227 """
185 text = unicode(text, encoding.encoding, 'replace') 228 text = unicode(text, encoding.encoding, 'replace')
186 return ''.join(['&#%d;' % ord(c) for c in text]) 229 return ''.join(['&#%d;' % ord(c) for c in text])
187 230
188 def permissions(flags): 231 def permissions(flags):
189 if "l" in flags: 232 if "l" in flags:
191 if "x" in flags: 234 if "x" in flags:
192 return "-rwxr-xr-x" 235 return "-rwxr-xr-x"
193 return "-rw-r--r--" 236 return "-rw-r--r--"
194 237
195 def person(author): 238 def person(author):
196 '''get name of author, or else username.''' 239 """:person: Any text. Returns the text before an email address."""
197 if not '@' in author: 240 if not '@' in author:
198 return author 241 return author
199 f = author.find('<') 242 f = author.find('<')
200 if f == -1: 243 if f == -1:
201 return util.shortuser(author) 244 return util.shortuser(author)
202 return author[:f].rstrip() 245 return author[:f].rstrip()
203 246
204 def rfc3339date(text): 247 def rfc3339date(text):
248 """:rfc3339date: Date. Returns a date using the Internet date format
249 specified in RFC 3339: "2009-08-18T13:00:13+02:00".
250 """
205 return util.datestr(text, "%Y-%m-%dT%H:%M:%S%1:%2") 251 return util.datestr(text, "%Y-%m-%dT%H:%M:%S%1:%2")
206 252
207 def rfc822date(text): 253 def rfc822date(text):
254 """:rfc822date: Date. Returns a date using the same format used in email
255 headers: "Tue, 18 Aug 2009 13:00:13 +0200".
256 """
208 return util.datestr(text, "%a, %d %b %Y %H:%M:%S %1%2") 257 return util.datestr(text, "%a, %d %b %Y %H:%M:%S %1%2")
209 258
210 def short(text): 259 def short(text):
260 """:short: Changeset hash. Returns the short form of a changeset hash,
261 i.e. a 12 hexadecimal digit string.
262 """
211 return text[:12] 263 return text[:12]
212 264
213 def shortdate(text): 265 def shortdate(text):
266 """:shortdate: Date. Returns a date like "2006-09-18"."""
214 return util.shortdate(text) 267 return util.shortdate(text)
215 268
216 def stringescape(text): 269 def stringescape(text):
217 return text.encode('string_escape') 270 return text.encode('string_escape')
218 271
219 def stringify(thing): 272 def stringify(thing):
220 '''turn nested template iterator into string.''' 273 """:stringify: Any type. Turns the value into text by converting values into
274 text and concatenating them.
275 """
221 if hasattr(thing, '__iter__') and not isinstance(thing, str): 276 if hasattr(thing, '__iter__') and not isinstance(thing, str):
222 return "".join([stringify(t) for t in thing if t is not None]) 277 return "".join([stringify(t) for t in thing if t is not None])
223 return str(thing) 278 return str(thing)
224 279
225 def strip(text): 280 def strip(text):
281 """:strip: Any text. Strips all leading and trailing whitespace."""
226 return text.strip() 282 return text.strip()
227 283
228 def stripdir(text): 284 def stripdir(text):
229 '''Treat the text as path and strip a directory level, if possible.''' 285 """:stripdir: Treat the text as path and strip a directory level, if
286 possible. For example, "foo" and "foo/bar" becomes "foo".
287 """
230 dir = os.path.dirname(text) 288 dir = os.path.dirname(text)
231 if dir == "": 289 if dir == "":
232 return os.path.basename(text) 290 return os.path.basename(text)
233 else: 291 else:
234 return dir 292 return dir
235 293
236 def tabindent(text): 294 def tabindent(text):
295 """:tabindent: Any text. Returns the text, with every line except the
296 first starting with a tab character.
297 """
237 return indent(text, '\t') 298 return indent(text, '\t')
238 299
239 def urlescape(text): 300 def urlescape(text):
301 """:urlescape: Any text. Escapes all "special" characters. For example,
302 "foo bar" becomes "foo%20bar".
303 """
240 return urllib.quote(text) 304 return urllib.quote(text)
241 305
242 def userfilter(text): 306 def userfilter(text):
307 """:user: Any text. Returns the user portion of an email address."""
243 return util.shortuser(text) 308 return util.shortuser(text)
244 309
245 def xmlescape(text): 310 def xmlescape(text):
246 text = (text 311 text = (text
247 .replace('&', '&amp;') 312 .replace('&', '&amp;')
284 "tabindent": tabindent, 349 "tabindent": tabindent,
285 "urlescape": urlescape, 350 "urlescape": urlescape,
286 "user": userfilter, 351 "user": userfilter,
287 "xmlescape": xmlescape, 352 "xmlescape": xmlescape,
288 } 353 }
354
355 def makedoc(topic, doc):
356 """Generate and include templatefilters help in templating topic."""
357 entries = []
358 for name in sorted(filters):
359 text = (filters[name].__doc__ or '').rstrip()
360 if not text:
361 continue
362 text = gettext(text)
363 lines = text.splitlines()
364 lines[1:] = [(' ' + l.strip()) for l in lines[1:]]
365 entries.append('\n'.join(lines))
366 entries = '\n\n'.join(entries)
367 doc = doc.replace('.. filtersmarker', entries)
368 return doc
369
370 # tell hggettext to extract docstrings from these functions:
371 i18nfunctions = filters.values()