##// END OF EJS Templates
templates: move filters to their own module...
Matt Mackall -
r5976:9f1e6ab7 default
parent child Browse files
Show More
@@ -0,0 +1,155 b''
1 # template-filters.py - common template expansion filters
2 #
3 # Copyright 2005-2008 Matt Mackall <mpm@selenic.com>
4 #
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
7
8 import cgi, re, os, time, urllib, textwrap
9 import util, templater
10
11 agescales = [("second", 1),
12 ("minute", 60),
13 ("hour", 3600),
14 ("day", 3600 * 24),
15 ("week", 3600 * 24 * 7),
16 ("month", 3600 * 24 * 30),
17 ("year", 3600 * 24 * 365)]
18
19 agescales.reverse()
20
21 def age(date):
22 '''turn a (timestamp, tzoff) tuple into an age string.'''
23
24 def plural(t, c):
25 if c == 1:
26 return t
27 return t + "s"
28 def fmt(t, c):
29 return "%d %s" % (c, plural(t, c))
30
31 now = time.time()
32 then = date[0]
33 delta = max(1, int(now - then))
34
35 for t, s in agescales:
36 n = delta / s
37 if n >= 2 or s == 1:
38 return fmt(t, n)
39
40 para_re = None
41 space_re = None
42
43 def fill(text, width):
44 '''fill many paragraphs.'''
45 global para_re, space_re
46 if para_re is None:
47 para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M)
48 space_re = re.compile(r' +')
49
50 def findparas():
51 start = 0
52 while True:
53 m = para_re.search(text, start)
54 if not m:
55 w = len(text)
56 while w > start and text[w-1].isspace(): w -= 1
57 yield text[start:w], text[w:]
58 break
59 yield text[start:m.start(0)], m.group(1)
60 start = m.end(1)
61
62 return "".join([space_re.sub(' ', textwrap.fill(para, width)) + rest
63 for para, rest in findparas()])
64
65 def firstline(text):
66 '''return the first line of text'''
67 try:
68 return text.splitlines(1)[0].rstrip('\r\n')
69 except IndexError:
70 return ''
71
72 def isodate(date):
73 '''turn a (timestamp, tzoff) tuple into an iso 8631 date and time.'''
74 return util.datestr(date, format='%Y-%m-%d %H:%M')
75
76 def hgdate(date):
77 '''turn a (timestamp, tzoff) tuple into an hg cset timestamp.'''
78 return "%d %d" % date
79
80 def nl2br(text):
81 '''replace raw newlines with xhtml line breaks.'''
82 return text.replace('\n', '<br/>\n')
83
84 def obfuscate(text):
85 text = unicode(text, util._encoding, 'replace')
86 return ''.join(['&#%d;' % ord(c) for c in text])
87
88 def domain(author):
89 '''get domain of author, or empty string if none.'''
90 f = author.find('@')
91 if f == -1: return ''
92 author = author[f+1:]
93 f = author.find('>')
94 if f >= 0: author = author[:f]
95 return author
96
97 def person(author):
98 '''get name of author, or else username.'''
99 f = author.find('<')
100 if f == -1: return util.shortuser(author)
101 return author[:f].rstrip()
102
103 def shortdate(date):
104 '''turn (timestamp, tzoff) tuple into iso 8631 date.'''
105 return util.datestr(date, format='%Y-%m-%d', timezone=False)
106
107 def indent(text, prefix):
108 '''indent each non-empty line of text after first with prefix.'''
109 lines = text.splitlines()
110 num_lines = len(lines)
111 def indenter():
112 for i in xrange(num_lines):
113 l = lines[i]
114 if i and l.strip():
115 yield prefix
116 yield l
117 if i < num_lines - 1 or text.endswith('\n'):
118 yield '\n'
119 return "".join(indenter())
120
121 def permissions(flags):
122 if "l" in flags:
123 return "lrwxrwxrwx"
124 if "x" in flags:
125 return "-rwxr-xr-x"
126 return "-rw-r--r--"
127
128 filters = {
129 "addbreaks": nl2br,
130 "basename": os.path.basename,
131 "age": age,
132 "date": lambda x: util.datestr(x),
133 "domain": domain,
134 "email": util.email,
135 "escape": lambda x: cgi.escape(x, True),
136 "fill68": lambda x: fill(x, width=68),
137 "fill76": lambda x: fill(x, width=76),
138 "firstline": firstline,
139 "tabindent": lambda x: indent(x, '\t'),
140 "hgdate": hgdate,
141 "isodate": isodate,
142 "obfuscate": obfuscate,
143 "permissions": permissions,
144 "person": person,
145 "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S"),
146 "rfc3339date": lambda x: util.datestr(x, "%Y-%m-%dT%H:%M:%S", True, "%+03d:%02d"),
147 "short": lambda x: x[:12],
148 "shortdate": shortdate,
149 "stringify": templater.stringify,
150 "strip": lambda x: x.strip(),
151 "urlescape": lambda x: urllib.quote(x),
152 "user": lambda x: util.shortuser(x),
153 "stringescape": lambda x: x.encode('string_escape'),
154 }
155
@@ -12,7 +12,7 b''
12 12 # <alias email> <actual email>
13 13
14 14 from mercurial.i18n import gettext as _
15 from mercurial import hg, mdiff, cmdutil, ui, util, templater, node
15 from mercurial import hg, mdiff, cmdutil, ui, util, templatefilters, node
16 16 import os, sys
17 17
18 18 def get_tty_width():
@@ -27,9 +27,9 b''
27 27
28 28 import re
29 29 from mercurial.hgweb import hgweb_mod
30 from mercurial import templater
30 from mercurial import templatefilters
31 31
32 orig_escape = templater.common_filters["escape"]
32 orig_escape = templatefilters.filters["escape"]
33 33
34 34 interhg_table = []
35 35
@@ -39,7 +39,7 b' def interhg_escape(x):'
39 39 escstr = regexp.sub(format, escstr)
40 40 return escstr
41 41
42 templater.common_filters["escape"] = interhg_escape
42 templatefilters.filters["escape"] = interhg_escape
43 43
44 44 orig_refresh = hgweb_mod.hgweb.refresh
45 45
@@ -78,8 +78,8 b" like CVS' $Log$, are not supported. A ke"
78 78 "Log = {desc}" expands to the first line of the changeset description.
79 79 '''
80 80
81 from mercurial import commands, cmdutil, context, dispatch, filelog
82 from mercurial import patch, localrepo, revlog, templater, util
81 from mercurial import commands, cmdutil, context, dispatch, filelog, revlog
82 from mercurial import patch, localrepo, templater, templatefilters, util
83 83 from mercurial.node import *
84 84 from mercurial.i18n import _
85 85 import re, shutil, sys, tempfile, time
@@ -130,7 +130,7 b' class kwtemplater(object):'
130 130 kwpat = r'\$(%s)(: [^$\n\r]*? )??\$' % '|'.join(escaped)
131 131 self.re_kw = re.compile(kwpat)
132 132
133 templater.common_filters['utcdate'] = utcdate
133 templatefilters.filters['utcdate'] = utcdate
134 134 self.ct = cmdutil.changeset_templater(self.ui, self.repo,
135 135 False, '', False)
136 136
@@ -149,7 +149,8 b' class kwtemplater(object):'
149 149 self.ct.use_template(self.templates[kw])
150 150 self.ui.pushbuffer()
151 151 self.ct.show(changenode=fnode, root=self.repo.root, file=self.path)
152 return '$%s: %s $' % (kw, templater.firstline(self.ui.popbuffer()))
152 return '$%s: %s $' % (kw, templatefilters.firstline(
153 self.ui.popbuffer()))
153 154
154 155 return subfunc(kwsub, data)
155 156
@@ -8,7 +8,7 b''
8 8 from node import *
9 9 from i18n import _
10 10 import os, sys, bisect, stat
11 import mdiff, bdiff, util, templater, patch, errno
11 import mdiff, bdiff, util, templater, templatefilters, patch, errno
12 12
13 13 revrangesep = ':'
14 14
@@ -673,7 +673,7 b' class changeset_templater(changeset_prin'
673 673
674 674 def __init__(self, ui, repo, patch, mapfile, buffered):
675 675 changeset_printer.__init__(self, ui, repo, patch, buffered)
676 filters = templater.common_filters.copy()
676 filters = templatefilters.filters.copy()
677 677 filters['formatnode'] = (ui.debugflag and (lambda x: x)
678 678 or (lambda x: x[:12]))
679 679 self.t = templater.templater(mapfile, filters,
@@ -9,7 +9,7 b''
9 9 import os, mimetypes, re
10 10 from mercurial.node import *
11 11 from mercurial import mdiff, ui, hg, util, archival, patch, hook
12 from mercurial import revlog, templater
12 from mercurial import revlog, templater, templatefilters
13 13 from common import ErrorResponse, get_mtime, style_map, paritygen, get_contact
14 14 from request import wsgirequest
15 15 import webcommands, protocol
@@ -288,7 +288,7 b' class hgweb(object):'
288 288
289 289 # create the templater
290 290
291 tmpl = templater.templater(mapfile, templater.common_filters,
291 tmpl = templater.templater(mapfile, templatefilters.filters,
292 292 defaults={"url": req.url,
293 293 "staticurl": staticurl,
294 294 "urlbase": urlbase,
@@ -8,8 +8,8 b''
8 8
9 9 import os
10 10 from mercurial.i18n import gettext as _
11 from mercurial import ui, hg, util, templater
12 from common import ErrorResponse, get_mtime, staticfile, style_map, paritygen, \
11 from mercurial import ui, hg, util, templater, templatefilters
12 from common import ErrorResponse, get_mtime, staticfile, style_map, paritygen,\
13 13 get_contact
14 14 from hgweb_mod import hgweb
15 15 from request import wsgirequest
@@ -266,7 +266,7 b' class hgwebdir(object):'
266 266 if self.stripecount is None:
267 267 self.stripecount = int(config('web', 'stripes', 1))
268 268 mapfile = style_map(templater.templatepath(), style)
269 tmpl = templater.templater(mapfile, templater.common_filters,
269 tmpl = templater.templater(mapfile, templatefilters.filters,
270 270 defaults={"header": header,
271 271 "footer": footer,
272 272 "motd": motd,
@@ -6,7 +6,7 b''
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from i18n import _
9 import cgi, re, sys, os, time, urllib, util, textwrap
9 import re, sys, os
10 10
11 11 def parsestring(s, quoted=True):
12 12 '''parse a string using simple c-like syntax.
@@ -122,157 +122,6 b' class templater(object):'
122 122 v = self.filters[f](v)
123 123 yield v
124 124
125 agescales = [("second", 1),
126 ("minute", 60),
127 ("hour", 3600),
128 ("day", 3600 * 24),
129 ("week", 3600 * 24 * 7),
130 ("month", 3600 * 24 * 30),
131 ("year", 3600 * 24 * 365)]
132
133 agescales.reverse()
134
135 def age(date):
136 '''turn a (timestamp, tzoff) tuple into an age string.'''
137
138 def plural(t, c):
139 if c == 1:
140 return t
141 return t + "s"
142 def fmt(t, c):
143 return "%d %s" % (c, plural(t, c))
144
145 now = time.time()
146 then = date[0]
147 delta = max(1, int(now - then))
148
149 for t, s in agescales:
150 n = delta / s
151 if n >= 2 or s == 1:
152 return fmt(t, n)
153
154 def stringify(thing):
155 '''turn nested template iterator into string.'''
156 if hasattr(thing, '__iter__'):
157 return "".join([stringify(t) for t in thing if t is not None])
158 return str(thing)
159
160 para_re = None
161 space_re = None
162
163 def fill(text, width):
164 '''fill many paragraphs.'''
165 global para_re, space_re
166 if para_re is None:
167 para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M)
168 space_re = re.compile(r' +')
169
170 def findparas():
171 start = 0
172 while True:
173 m = para_re.search(text, start)
174 if not m:
175 w = len(text)
176 while w > start and text[w-1].isspace(): w -= 1
177 yield text[start:w], text[w:]
178 break
179 yield text[start:m.start(0)], m.group(1)
180 start = m.end(1)
181
182 return "".join([space_re.sub(' ', textwrap.fill(para, width)) + rest
183 for para, rest in findparas()])
184
185 def firstline(text):
186 '''return the first line of text'''
187 try:
188 return text.splitlines(1)[0].rstrip('\r\n')
189 except IndexError:
190 return ''
191
192 def isodate(date):
193 '''turn a (timestamp, tzoff) tuple into an iso 8631 date and time.'''
194 return util.datestr(date, format='%Y-%m-%d %H:%M')
195
196 def hgdate(date):
197 '''turn a (timestamp, tzoff) tuple into an hg cset timestamp.'''
198 return "%d %d" % date
199
200 def nl2br(text):
201 '''replace raw newlines with xhtml line breaks.'''
202 return text.replace('\n', '<br/>\n')
203
204 def obfuscate(text):
205 text = unicode(text, util._encoding, 'replace')
206 return ''.join(['&#%d;' % ord(c) for c in text])
207
208 def domain(author):
209 '''get domain of author, or empty string if none.'''
210 f = author.find('@')
211 if f == -1: return ''
212 author = author[f+1:]
213 f = author.find('>')
214 if f >= 0: author = author[:f]
215 return author
216
217 def person(author):
218 '''get name of author, or else username.'''
219 f = author.find('<')
220 if f == -1: return util.shortuser(author)
221 return author[:f].rstrip()
222
223 def shortdate(date):
224 '''turn (timestamp, tzoff) tuple into iso 8631 date.'''
225 return util.datestr(date, format='%Y-%m-%d', timezone=False)
226
227 def indent(text, prefix):
228 '''indent each non-empty line of text after first with prefix.'''
229 lines = text.splitlines()
230 num_lines = len(lines)
231 def indenter():
232 for i in xrange(num_lines):
233 l = lines[i]
234 if i and l.strip():
235 yield prefix
236 yield l
237 if i < num_lines - 1 or text.endswith('\n'):
238 yield '\n'
239 return "".join(indenter())
240
241 def permissions(flags):
242 if "l" in flags:
243 return "lrwxrwxrwx"
244 if "x" in flags:
245 return "-rwxr-xr-x"
246 return "-rw-r--r--"
247
248 common_filters = {
249 "addbreaks": nl2br,
250 "basename": os.path.basename,
251 "age": age,
252 "date": lambda x: util.datestr(x),
253 "domain": domain,
254 "email": util.email,
255 "escape": lambda x: cgi.escape(x, True),
256 "fill68": lambda x: fill(x, width=68),
257 "fill76": lambda x: fill(x, width=76),
258 "firstline": firstline,
259 "tabindent": lambda x: indent(x, '\t'),
260 "hgdate": hgdate,
261 "isodate": isodate,
262 "obfuscate": obfuscate,
263 "permissions": permissions,
264 "person": person,
265 "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S"),
266 "rfc3339date": lambda x: util.datestr(x, "%Y-%m-%dT%H:%M:%S", True, "%+03d:%02d"),
267 "short": lambda x: x[:12],
268 "shortdate": shortdate,
269 "stringify": stringify,
270 "strip": lambda x: x.strip(),
271 "urlescape": lambda x: urllib.quote(x),
272 "user": lambda x: util.shortuser(x),
273 "stringescape": lambda x: x.encode('string_escape'),
274 }
275
276 125 def templatepath(name=None):
277 126 '''return location of template file or directory (if no name).
278 127 returns None if not found.'''
@@ -289,3 +138,9 b' def templatepath(name=None):'
289 138 if (name and os.path.exists(p)) or os.path.isdir(p):
290 139 return os.path.normpath(p)
291 140
141 def stringify(thing):
142 '''turn nested template iterator into string.'''
143 if hasattr(thing, '__iter__'):
144 return "".join([stringify(t) for t in thing if t is not None])
145 return str(thing)
146
General Comments 0
You need to be logged in to leave comments. Login now