##// END OF EJS Templates
paper: make different blocks of annotated lines have different colors
av6 -
r29572:d86b54d9 default
parent child Browse files
Show More
@@ -1,1311 +1,1317 b''
1 #
1 #
2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 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 cgi
10 import cgi
11 import copy
11 import copy
12 import mimetypes
12 import mimetypes
13 import os
13 import os
14 import re
14 import re
15
15
16 from ..i18n import _
16 from ..i18n import _
17 from ..node import hex, short
17 from ..node import hex, short
18
18
19 from .common import (
19 from .common import (
20 ErrorResponse,
20 ErrorResponse,
21 HTTP_FORBIDDEN,
21 HTTP_FORBIDDEN,
22 HTTP_NOT_FOUND,
22 HTTP_NOT_FOUND,
23 HTTP_OK,
23 HTTP_OK,
24 get_contact,
24 get_contact,
25 paritygen,
25 paritygen,
26 staticfile,
26 staticfile,
27 )
27 )
28
28
29 from .. import (
29 from .. import (
30 archival,
30 archival,
31 encoding,
31 encoding,
32 error,
32 error,
33 graphmod,
33 graphmod,
34 patch,
34 patch,
35 revset,
35 revset,
36 scmutil,
36 scmutil,
37 templatefilters,
37 templatefilters,
38 templater,
38 templater,
39 util,
39 util,
40 )
40 )
41
41
42 from . import (
42 from . import (
43 webutil,
43 webutil,
44 )
44 )
45
45
46 __all__ = []
46 __all__ = []
47 commands = {}
47 commands = {}
48
48
49 class webcommand(object):
49 class webcommand(object):
50 """Decorator used to register a web command handler.
50 """Decorator used to register a web command handler.
51
51
52 The decorator takes as its positional arguments the name/path the
52 The decorator takes as its positional arguments the name/path the
53 command should be accessible under.
53 command should be accessible under.
54
54
55 Usage:
55 Usage:
56
56
57 @webcommand('mycommand')
57 @webcommand('mycommand')
58 def mycommand(web, req, tmpl):
58 def mycommand(web, req, tmpl):
59 pass
59 pass
60 """
60 """
61
61
62 def __init__(self, name):
62 def __init__(self, name):
63 self.name = name
63 self.name = name
64
64
65 def __call__(self, func):
65 def __call__(self, func):
66 __all__.append(self.name)
66 __all__.append(self.name)
67 commands[self.name] = func
67 commands[self.name] = func
68 return func
68 return func
69
69
70 @webcommand('log')
70 @webcommand('log')
71 def log(web, req, tmpl):
71 def log(web, req, tmpl):
72 """
72 """
73 /log[/{revision}[/{path}]]
73 /log[/{revision}[/{path}]]
74 --------------------------
74 --------------------------
75
75
76 Show repository or file history.
76 Show repository or file history.
77
77
78 For URLs of the form ``/log/{revision}``, a list of changesets starting at
78 For URLs of the form ``/log/{revision}``, a list of changesets starting at
79 the specified changeset identifier is shown. If ``{revision}`` is not
79 the specified changeset identifier is shown. If ``{revision}`` is not
80 defined, the default is ``tip``. This form is equivalent to the
80 defined, the default is ``tip``. This form is equivalent to the
81 ``changelog`` handler.
81 ``changelog`` handler.
82
82
83 For URLs of the form ``/log/{revision}/{file}``, the history for a specific
83 For URLs of the form ``/log/{revision}/{file}``, the history for a specific
84 file will be shown. This form is equivalent to the ``filelog`` handler.
84 file will be shown. This form is equivalent to the ``filelog`` handler.
85 """
85 """
86
86
87 if 'file' in req.form and req.form['file'][0]:
87 if 'file' in req.form and req.form['file'][0]:
88 return filelog(web, req, tmpl)
88 return filelog(web, req, tmpl)
89 else:
89 else:
90 return changelog(web, req, tmpl)
90 return changelog(web, req, tmpl)
91
91
92 @webcommand('rawfile')
92 @webcommand('rawfile')
93 def rawfile(web, req, tmpl):
93 def rawfile(web, req, tmpl):
94 guessmime = web.configbool('web', 'guessmime', False)
94 guessmime = web.configbool('web', 'guessmime', False)
95
95
96 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
96 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
97 if not path:
97 if not path:
98 content = manifest(web, req, tmpl)
98 content = manifest(web, req, tmpl)
99 req.respond(HTTP_OK, web.ctype)
99 req.respond(HTTP_OK, web.ctype)
100 return content
100 return content
101
101
102 try:
102 try:
103 fctx = webutil.filectx(web.repo, req)
103 fctx = webutil.filectx(web.repo, req)
104 except error.LookupError as inst:
104 except error.LookupError as inst:
105 try:
105 try:
106 content = manifest(web, req, tmpl)
106 content = manifest(web, req, tmpl)
107 req.respond(HTTP_OK, web.ctype)
107 req.respond(HTTP_OK, web.ctype)
108 return content
108 return content
109 except ErrorResponse:
109 except ErrorResponse:
110 raise inst
110 raise inst
111
111
112 path = fctx.path()
112 path = fctx.path()
113 text = fctx.data()
113 text = fctx.data()
114 mt = 'application/binary'
114 mt = 'application/binary'
115 if guessmime:
115 if guessmime:
116 mt = mimetypes.guess_type(path)[0]
116 mt = mimetypes.guess_type(path)[0]
117 if mt is None:
117 if mt is None:
118 if util.binary(text):
118 if util.binary(text):
119 mt = 'application/binary'
119 mt = 'application/binary'
120 else:
120 else:
121 mt = 'text/plain'
121 mt = 'text/plain'
122 if mt.startswith('text/'):
122 if mt.startswith('text/'):
123 mt += '; charset="%s"' % encoding.encoding
123 mt += '; charset="%s"' % encoding.encoding
124
124
125 req.respond(HTTP_OK, mt, path, body=text)
125 req.respond(HTTP_OK, mt, path, body=text)
126 return []
126 return []
127
127
128 def _filerevision(web, req, tmpl, fctx):
128 def _filerevision(web, req, tmpl, fctx):
129 f = fctx.path()
129 f = fctx.path()
130 text = fctx.data()
130 text = fctx.data()
131 parity = paritygen(web.stripecount)
131 parity = paritygen(web.stripecount)
132
132
133 if util.binary(text):
133 if util.binary(text):
134 mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
134 mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
135 text = '(binary:%s)' % mt
135 text = '(binary:%s)' % mt
136
136
137 def lines():
137 def lines():
138 for lineno, t in enumerate(text.splitlines(True)):
138 for lineno, t in enumerate(text.splitlines(True)):
139 yield {"line": t,
139 yield {"line": t,
140 "lineid": "l%d" % (lineno + 1),
140 "lineid": "l%d" % (lineno + 1),
141 "linenumber": "% 6d" % (lineno + 1),
141 "linenumber": "% 6d" % (lineno + 1),
142 "parity": next(parity)}
142 "parity": next(parity)}
143
143
144 return tmpl("filerevision",
144 return tmpl("filerevision",
145 file=f,
145 file=f,
146 path=webutil.up(f),
146 path=webutil.up(f),
147 text=lines(),
147 text=lines(),
148 symrev=webutil.symrevorshortnode(req, fctx),
148 symrev=webutil.symrevorshortnode(req, fctx),
149 rename=webutil.renamelink(fctx),
149 rename=webutil.renamelink(fctx),
150 permissions=fctx.manifest().flags(f),
150 permissions=fctx.manifest().flags(f),
151 **webutil.commonentry(web.repo, fctx))
151 **webutil.commonentry(web.repo, fctx))
152
152
153 @webcommand('file')
153 @webcommand('file')
154 def file(web, req, tmpl):
154 def file(web, req, tmpl):
155 """
155 """
156 /file/{revision}[/{path}]
156 /file/{revision}[/{path}]
157 -------------------------
157 -------------------------
158
158
159 Show information about a directory or file in the repository.
159 Show information about a directory or file in the repository.
160
160
161 Info about the ``path`` given as a URL parameter will be rendered.
161 Info about the ``path`` given as a URL parameter will be rendered.
162
162
163 If ``path`` is a directory, information about the entries in that
163 If ``path`` is a directory, information about the entries in that
164 directory will be rendered. This form is equivalent to the ``manifest``
164 directory will be rendered. This form is equivalent to the ``manifest``
165 handler.
165 handler.
166
166
167 If ``path`` is a file, information about that file will be shown via
167 If ``path`` is a file, information about that file will be shown via
168 the ``filerevision`` template.
168 the ``filerevision`` template.
169
169
170 If ``path`` is not defined, information about the root directory will
170 If ``path`` is not defined, information about the root directory will
171 be rendered.
171 be rendered.
172 """
172 """
173 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
173 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
174 if not path:
174 if not path:
175 return manifest(web, req, tmpl)
175 return manifest(web, req, tmpl)
176 try:
176 try:
177 return _filerevision(web, req, tmpl, webutil.filectx(web.repo, req))
177 return _filerevision(web, req, tmpl, webutil.filectx(web.repo, req))
178 except error.LookupError as inst:
178 except error.LookupError as inst:
179 try:
179 try:
180 return manifest(web, req, tmpl)
180 return manifest(web, req, tmpl)
181 except ErrorResponse:
181 except ErrorResponse:
182 raise inst
182 raise inst
183
183
184 def _search(web, req, tmpl):
184 def _search(web, req, tmpl):
185 MODE_REVISION = 'rev'
185 MODE_REVISION = 'rev'
186 MODE_KEYWORD = 'keyword'
186 MODE_KEYWORD = 'keyword'
187 MODE_REVSET = 'revset'
187 MODE_REVSET = 'revset'
188
188
189 def revsearch(ctx):
189 def revsearch(ctx):
190 yield ctx
190 yield ctx
191
191
192 def keywordsearch(query):
192 def keywordsearch(query):
193 lower = encoding.lower
193 lower = encoding.lower
194 qw = lower(query).split()
194 qw = lower(query).split()
195
195
196 def revgen():
196 def revgen():
197 cl = web.repo.changelog
197 cl = web.repo.changelog
198 for i in xrange(len(web.repo) - 1, 0, -100):
198 for i in xrange(len(web.repo) - 1, 0, -100):
199 l = []
199 l = []
200 for j in cl.revs(max(0, i - 99), i):
200 for j in cl.revs(max(0, i - 99), i):
201 ctx = web.repo[j]
201 ctx = web.repo[j]
202 l.append(ctx)
202 l.append(ctx)
203 l.reverse()
203 l.reverse()
204 for e in l:
204 for e in l:
205 yield e
205 yield e
206
206
207 for ctx in revgen():
207 for ctx in revgen():
208 miss = 0
208 miss = 0
209 for q in qw:
209 for q in qw:
210 if not (q in lower(ctx.user()) or
210 if not (q in lower(ctx.user()) or
211 q in lower(ctx.description()) or
211 q in lower(ctx.description()) or
212 q in lower(" ".join(ctx.files()))):
212 q in lower(" ".join(ctx.files()))):
213 miss = 1
213 miss = 1
214 break
214 break
215 if miss:
215 if miss:
216 continue
216 continue
217
217
218 yield ctx
218 yield ctx
219
219
220 def revsetsearch(revs):
220 def revsetsearch(revs):
221 for r in revs:
221 for r in revs:
222 yield web.repo[r]
222 yield web.repo[r]
223
223
224 searchfuncs = {
224 searchfuncs = {
225 MODE_REVISION: (revsearch, 'exact revision search'),
225 MODE_REVISION: (revsearch, 'exact revision search'),
226 MODE_KEYWORD: (keywordsearch, 'literal keyword search'),
226 MODE_KEYWORD: (keywordsearch, 'literal keyword search'),
227 MODE_REVSET: (revsetsearch, 'revset expression search'),
227 MODE_REVSET: (revsetsearch, 'revset expression search'),
228 }
228 }
229
229
230 def getsearchmode(query):
230 def getsearchmode(query):
231 try:
231 try:
232 ctx = web.repo[query]
232 ctx = web.repo[query]
233 except (error.RepoError, error.LookupError):
233 except (error.RepoError, error.LookupError):
234 # query is not an exact revision pointer, need to
234 # query is not an exact revision pointer, need to
235 # decide if it's a revset expression or keywords
235 # decide if it's a revset expression or keywords
236 pass
236 pass
237 else:
237 else:
238 return MODE_REVISION, ctx
238 return MODE_REVISION, ctx
239
239
240 revdef = 'reverse(%s)' % query
240 revdef = 'reverse(%s)' % query
241 try:
241 try:
242 tree = revset.parse(revdef)
242 tree = revset.parse(revdef)
243 except error.ParseError:
243 except error.ParseError:
244 # can't parse to a revset tree
244 # can't parse to a revset tree
245 return MODE_KEYWORD, query
245 return MODE_KEYWORD, query
246
246
247 if revset.depth(tree) <= 2:
247 if revset.depth(tree) <= 2:
248 # no revset syntax used
248 # no revset syntax used
249 return MODE_KEYWORD, query
249 return MODE_KEYWORD, query
250
250
251 if any((token, (value or '')[:3]) == ('string', 're:')
251 if any((token, (value or '')[:3]) == ('string', 're:')
252 for token, value, pos in revset.tokenize(revdef)):
252 for token, value, pos in revset.tokenize(revdef)):
253 return MODE_KEYWORD, query
253 return MODE_KEYWORD, query
254
254
255 funcsused = revset.funcsused(tree)
255 funcsused = revset.funcsused(tree)
256 if not funcsused.issubset(revset.safesymbols):
256 if not funcsused.issubset(revset.safesymbols):
257 return MODE_KEYWORD, query
257 return MODE_KEYWORD, query
258
258
259 mfunc = revset.match(web.repo.ui, revdef)
259 mfunc = revset.match(web.repo.ui, revdef)
260 try:
260 try:
261 revs = mfunc(web.repo)
261 revs = mfunc(web.repo)
262 return MODE_REVSET, revs
262 return MODE_REVSET, revs
263 # ParseError: wrongly placed tokens, wrongs arguments, etc
263 # ParseError: wrongly placed tokens, wrongs arguments, etc
264 # RepoLookupError: no such revision, e.g. in 'revision:'
264 # RepoLookupError: no such revision, e.g. in 'revision:'
265 # Abort: bookmark/tag not exists
265 # Abort: bookmark/tag not exists
266 # LookupError: ambiguous identifier, e.g. in '(bc)' on a large repo
266 # LookupError: ambiguous identifier, e.g. in '(bc)' on a large repo
267 except (error.ParseError, error.RepoLookupError, error.Abort,
267 except (error.ParseError, error.RepoLookupError, error.Abort,
268 LookupError):
268 LookupError):
269 return MODE_KEYWORD, query
269 return MODE_KEYWORD, query
270
270
271 def changelist(**map):
271 def changelist(**map):
272 count = 0
272 count = 0
273
273
274 for ctx in searchfunc[0](funcarg):
274 for ctx in searchfunc[0](funcarg):
275 count += 1
275 count += 1
276 n = ctx.node()
276 n = ctx.node()
277 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
277 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
278 files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
278 files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
279
279
280 yield tmpl('searchentry',
280 yield tmpl('searchentry',
281 parity=next(parity),
281 parity=next(parity),
282 changelogtag=showtags,
282 changelogtag=showtags,
283 files=files,
283 files=files,
284 **webutil.commonentry(web.repo, ctx))
284 **webutil.commonentry(web.repo, ctx))
285
285
286 if count >= revcount:
286 if count >= revcount:
287 break
287 break
288
288
289 query = req.form['rev'][0]
289 query = req.form['rev'][0]
290 revcount = web.maxchanges
290 revcount = web.maxchanges
291 if 'revcount' in req.form:
291 if 'revcount' in req.form:
292 try:
292 try:
293 revcount = int(req.form.get('revcount', [revcount])[0])
293 revcount = int(req.form.get('revcount', [revcount])[0])
294 revcount = max(revcount, 1)
294 revcount = max(revcount, 1)
295 tmpl.defaults['sessionvars']['revcount'] = revcount
295 tmpl.defaults['sessionvars']['revcount'] = revcount
296 except ValueError:
296 except ValueError:
297 pass
297 pass
298
298
299 lessvars = copy.copy(tmpl.defaults['sessionvars'])
299 lessvars = copy.copy(tmpl.defaults['sessionvars'])
300 lessvars['revcount'] = max(revcount / 2, 1)
300 lessvars['revcount'] = max(revcount / 2, 1)
301 lessvars['rev'] = query
301 lessvars['rev'] = query
302 morevars = copy.copy(tmpl.defaults['sessionvars'])
302 morevars = copy.copy(tmpl.defaults['sessionvars'])
303 morevars['revcount'] = revcount * 2
303 morevars['revcount'] = revcount * 2
304 morevars['rev'] = query
304 morevars['rev'] = query
305
305
306 mode, funcarg = getsearchmode(query)
306 mode, funcarg = getsearchmode(query)
307
307
308 if 'forcekw' in req.form:
308 if 'forcekw' in req.form:
309 showforcekw = ''
309 showforcekw = ''
310 showunforcekw = searchfuncs[mode][1]
310 showunforcekw = searchfuncs[mode][1]
311 mode = MODE_KEYWORD
311 mode = MODE_KEYWORD
312 funcarg = query
312 funcarg = query
313 else:
313 else:
314 if mode != MODE_KEYWORD:
314 if mode != MODE_KEYWORD:
315 showforcekw = searchfuncs[MODE_KEYWORD][1]
315 showforcekw = searchfuncs[MODE_KEYWORD][1]
316 else:
316 else:
317 showforcekw = ''
317 showforcekw = ''
318 showunforcekw = ''
318 showunforcekw = ''
319
319
320 searchfunc = searchfuncs[mode]
320 searchfunc = searchfuncs[mode]
321
321
322 tip = web.repo['tip']
322 tip = web.repo['tip']
323 parity = paritygen(web.stripecount)
323 parity = paritygen(web.stripecount)
324
324
325 return tmpl('search', query=query, node=tip.hex(), symrev='tip',
325 return tmpl('search', query=query, node=tip.hex(), symrev='tip',
326 entries=changelist, archives=web.archivelist("tip"),
326 entries=changelist, archives=web.archivelist("tip"),
327 morevars=morevars, lessvars=lessvars,
327 morevars=morevars, lessvars=lessvars,
328 modedesc=searchfunc[1],
328 modedesc=searchfunc[1],
329 showforcekw=showforcekw, showunforcekw=showunforcekw)
329 showforcekw=showforcekw, showunforcekw=showunforcekw)
330
330
331 @webcommand('changelog')
331 @webcommand('changelog')
332 def changelog(web, req, tmpl, shortlog=False):
332 def changelog(web, req, tmpl, shortlog=False):
333 """
333 """
334 /changelog[/{revision}]
334 /changelog[/{revision}]
335 -----------------------
335 -----------------------
336
336
337 Show information about multiple changesets.
337 Show information about multiple changesets.
338
338
339 If the optional ``revision`` URL argument is absent, information about
339 If the optional ``revision`` URL argument is absent, information about
340 all changesets starting at ``tip`` will be rendered. If the ``revision``
340 all changesets starting at ``tip`` will be rendered. If the ``revision``
341 argument is present, changesets will be shown starting from the specified
341 argument is present, changesets will be shown starting from the specified
342 revision.
342 revision.
343
343
344 If ``revision`` is absent, the ``rev`` query string argument may be
344 If ``revision`` is absent, the ``rev`` query string argument may be
345 defined. This will perform a search for changesets.
345 defined. This will perform a search for changesets.
346
346
347 The argument for ``rev`` can be a single revision, a revision set,
347 The argument for ``rev`` can be a single revision, a revision set,
348 or a literal keyword to search for in changeset data (equivalent to
348 or a literal keyword to search for in changeset data (equivalent to
349 :hg:`log -k`).
349 :hg:`log -k`).
350
350
351 The ``revcount`` query string argument defines the maximum numbers of
351 The ``revcount`` query string argument defines the maximum numbers of
352 changesets to render.
352 changesets to render.
353
353
354 For non-searches, the ``changelog`` template will be rendered.
354 For non-searches, the ``changelog`` template will be rendered.
355 """
355 """
356
356
357 query = ''
357 query = ''
358 if 'node' in req.form:
358 if 'node' in req.form:
359 ctx = webutil.changectx(web.repo, req)
359 ctx = webutil.changectx(web.repo, req)
360 symrev = webutil.symrevorshortnode(req, ctx)
360 symrev = webutil.symrevorshortnode(req, ctx)
361 elif 'rev' in req.form:
361 elif 'rev' in req.form:
362 return _search(web, req, tmpl)
362 return _search(web, req, tmpl)
363 else:
363 else:
364 ctx = web.repo['tip']
364 ctx = web.repo['tip']
365 symrev = 'tip'
365 symrev = 'tip'
366
366
367 def changelist():
367 def changelist():
368 revs = []
368 revs = []
369 if pos != -1:
369 if pos != -1:
370 revs = web.repo.changelog.revs(pos, 0)
370 revs = web.repo.changelog.revs(pos, 0)
371 curcount = 0
371 curcount = 0
372 for rev in revs:
372 for rev in revs:
373 curcount += 1
373 curcount += 1
374 if curcount > revcount + 1:
374 if curcount > revcount + 1:
375 break
375 break
376
376
377 entry = webutil.changelistentry(web, web.repo[rev], tmpl)
377 entry = webutil.changelistentry(web, web.repo[rev], tmpl)
378 entry['parity'] = next(parity)
378 entry['parity'] = next(parity)
379 yield entry
379 yield entry
380
380
381 if shortlog:
381 if shortlog:
382 revcount = web.maxshortchanges
382 revcount = web.maxshortchanges
383 else:
383 else:
384 revcount = web.maxchanges
384 revcount = web.maxchanges
385
385
386 if 'revcount' in req.form:
386 if 'revcount' in req.form:
387 try:
387 try:
388 revcount = int(req.form.get('revcount', [revcount])[0])
388 revcount = int(req.form.get('revcount', [revcount])[0])
389 revcount = max(revcount, 1)
389 revcount = max(revcount, 1)
390 tmpl.defaults['sessionvars']['revcount'] = revcount
390 tmpl.defaults['sessionvars']['revcount'] = revcount
391 except ValueError:
391 except ValueError:
392 pass
392 pass
393
393
394 lessvars = copy.copy(tmpl.defaults['sessionvars'])
394 lessvars = copy.copy(tmpl.defaults['sessionvars'])
395 lessvars['revcount'] = max(revcount / 2, 1)
395 lessvars['revcount'] = max(revcount / 2, 1)
396 morevars = copy.copy(tmpl.defaults['sessionvars'])
396 morevars = copy.copy(tmpl.defaults['sessionvars'])
397 morevars['revcount'] = revcount * 2
397 morevars['revcount'] = revcount * 2
398
398
399 count = len(web.repo)
399 count = len(web.repo)
400 pos = ctx.rev()
400 pos = ctx.rev()
401 parity = paritygen(web.stripecount)
401 parity = paritygen(web.stripecount)
402
402
403 changenav = webutil.revnav(web.repo).gen(pos, revcount, count)
403 changenav = webutil.revnav(web.repo).gen(pos, revcount, count)
404
404
405 entries = list(changelist())
405 entries = list(changelist())
406 latestentry = entries[:1]
406 latestentry = entries[:1]
407 if len(entries) > revcount:
407 if len(entries) > revcount:
408 nextentry = entries[-1:]
408 nextentry = entries[-1:]
409 entries = entries[:-1]
409 entries = entries[:-1]
410 else:
410 else:
411 nextentry = []
411 nextentry = []
412
412
413 return tmpl(shortlog and 'shortlog' or 'changelog', changenav=changenav,
413 return tmpl(shortlog and 'shortlog' or 'changelog', changenav=changenav,
414 node=ctx.hex(), rev=pos, symrev=symrev, changesets=count,
414 node=ctx.hex(), rev=pos, symrev=symrev, changesets=count,
415 entries=entries,
415 entries=entries,
416 latestentry=latestentry, nextentry=nextentry,
416 latestentry=latestentry, nextentry=nextentry,
417 archives=web.archivelist("tip"), revcount=revcount,
417 archives=web.archivelist("tip"), revcount=revcount,
418 morevars=morevars, lessvars=lessvars, query=query)
418 morevars=morevars, lessvars=lessvars, query=query)
419
419
420 @webcommand('shortlog')
420 @webcommand('shortlog')
421 def shortlog(web, req, tmpl):
421 def shortlog(web, req, tmpl):
422 """
422 """
423 /shortlog
423 /shortlog
424 ---------
424 ---------
425
425
426 Show basic information about a set of changesets.
426 Show basic information about a set of changesets.
427
427
428 This accepts the same parameters as the ``changelog`` handler. The only
428 This accepts the same parameters as the ``changelog`` handler. The only
429 difference is the ``shortlog`` template will be rendered instead of the
429 difference is the ``shortlog`` template will be rendered instead of the
430 ``changelog`` template.
430 ``changelog`` template.
431 """
431 """
432 return changelog(web, req, tmpl, shortlog=True)
432 return changelog(web, req, tmpl, shortlog=True)
433
433
434 @webcommand('changeset')
434 @webcommand('changeset')
435 def changeset(web, req, tmpl):
435 def changeset(web, req, tmpl):
436 """
436 """
437 /changeset[/{revision}]
437 /changeset[/{revision}]
438 -----------------------
438 -----------------------
439
439
440 Show information about a single changeset.
440 Show information about a single changeset.
441
441
442 A URL path argument is the changeset identifier to show. See ``hg help
442 A URL path argument is the changeset identifier to show. See ``hg help
443 revisions`` for possible values. If not defined, the ``tip`` changeset
443 revisions`` for possible values. If not defined, the ``tip`` changeset
444 will be shown.
444 will be shown.
445
445
446 The ``changeset`` template is rendered. Contents of the ``changesettag``,
446 The ``changeset`` template is rendered. Contents of the ``changesettag``,
447 ``changesetbookmark``, ``filenodelink``, ``filenolink``, and the many
447 ``changesetbookmark``, ``filenodelink``, ``filenolink``, and the many
448 templates related to diffs may all be used to produce the output.
448 templates related to diffs may all be used to produce the output.
449 """
449 """
450 ctx = webutil.changectx(web.repo, req)
450 ctx = webutil.changectx(web.repo, req)
451
451
452 return tmpl('changeset', **webutil.changesetentry(web, req, tmpl, ctx))
452 return tmpl('changeset', **webutil.changesetentry(web, req, tmpl, ctx))
453
453
454 rev = webcommand('rev')(changeset)
454 rev = webcommand('rev')(changeset)
455
455
456 def decodepath(path):
456 def decodepath(path):
457 """Hook for mapping a path in the repository to a path in the
457 """Hook for mapping a path in the repository to a path in the
458 working copy.
458 working copy.
459
459
460 Extensions (e.g., largefiles) can override this to remap files in
460 Extensions (e.g., largefiles) can override this to remap files in
461 the virtual file system presented by the manifest command below."""
461 the virtual file system presented by the manifest command below."""
462 return path
462 return path
463
463
464 @webcommand('manifest')
464 @webcommand('manifest')
465 def manifest(web, req, tmpl):
465 def manifest(web, req, tmpl):
466 """
466 """
467 /manifest[/{revision}[/{path}]]
467 /manifest[/{revision}[/{path}]]
468 -------------------------------
468 -------------------------------
469
469
470 Show information about a directory.
470 Show information about a directory.
471
471
472 If the URL path arguments are omitted, information about the root
472 If the URL path arguments are omitted, information about the root
473 directory for the ``tip`` changeset will be shown.
473 directory for the ``tip`` changeset will be shown.
474
474
475 Because this handler can only show information for directories, it
475 Because this handler can only show information for directories, it
476 is recommended to use the ``file`` handler instead, as it can handle both
476 is recommended to use the ``file`` handler instead, as it can handle both
477 directories and files.
477 directories and files.
478
478
479 The ``manifest`` template will be rendered for this handler.
479 The ``manifest`` template will be rendered for this handler.
480 """
480 """
481 if 'node' in req.form:
481 if 'node' in req.form:
482 ctx = webutil.changectx(web.repo, req)
482 ctx = webutil.changectx(web.repo, req)
483 symrev = webutil.symrevorshortnode(req, ctx)
483 symrev = webutil.symrevorshortnode(req, ctx)
484 else:
484 else:
485 ctx = web.repo['tip']
485 ctx = web.repo['tip']
486 symrev = 'tip'
486 symrev = 'tip'
487 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
487 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
488 mf = ctx.manifest()
488 mf = ctx.manifest()
489 node = ctx.node()
489 node = ctx.node()
490
490
491 files = {}
491 files = {}
492 dirs = {}
492 dirs = {}
493 parity = paritygen(web.stripecount)
493 parity = paritygen(web.stripecount)
494
494
495 if path and path[-1] != "/":
495 if path and path[-1] != "/":
496 path += "/"
496 path += "/"
497 l = len(path)
497 l = len(path)
498 abspath = "/" + path
498 abspath = "/" + path
499
499
500 for full, n in mf.iteritems():
500 for full, n in mf.iteritems():
501 # the virtual path (working copy path) used for the full
501 # the virtual path (working copy path) used for the full
502 # (repository) path
502 # (repository) path
503 f = decodepath(full)
503 f = decodepath(full)
504
504
505 if f[:l] != path:
505 if f[:l] != path:
506 continue
506 continue
507 remain = f[l:]
507 remain = f[l:]
508 elements = remain.split('/')
508 elements = remain.split('/')
509 if len(elements) == 1:
509 if len(elements) == 1:
510 files[remain] = full
510 files[remain] = full
511 else:
511 else:
512 h = dirs # need to retain ref to dirs (root)
512 h = dirs # need to retain ref to dirs (root)
513 for elem in elements[0:-1]:
513 for elem in elements[0:-1]:
514 if elem not in h:
514 if elem not in h:
515 h[elem] = {}
515 h[elem] = {}
516 h = h[elem]
516 h = h[elem]
517 if len(h) > 1:
517 if len(h) > 1:
518 break
518 break
519 h[None] = None # denotes files present
519 h[None] = None # denotes files present
520
520
521 if mf and not files and not dirs:
521 if mf and not files and not dirs:
522 raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
522 raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
523
523
524 def filelist(**map):
524 def filelist(**map):
525 for f in sorted(files):
525 for f in sorted(files):
526 full = files[f]
526 full = files[f]
527
527
528 fctx = ctx.filectx(full)
528 fctx = ctx.filectx(full)
529 yield {"file": full,
529 yield {"file": full,
530 "parity": next(parity),
530 "parity": next(parity),
531 "basename": f,
531 "basename": f,
532 "date": fctx.date(),
532 "date": fctx.date(),
533 "size": fctx.size(),
533 "size": fctx.size(),
534 "permissions": mf.flags(full)}
534 "permissions": mf.flags(full)}
535
535
536 def dirlist(**map):
536 def dirlist(**map):
537 for d in sorted(dirs):
537 for d in sorted(dirs):
538
538
539 emptydirs = []
539 emptydirs = []
540 h = dirs[d]
540 h = dirs[d]
541 while isinstance(h, dict) and len(h) == 1:
541 while isinstance(h, dict) and len(h) == 1:
542 k, v = h.items()[0]
542 k, v = h.items()[0]
543 if v:
543 if v:
544 emptydirs.append(k)
544 emptydirs.append(k)
545 h = v
545 h = v
546
546
547 path = "%s%s" % (abspath, d)
547 path = "%s%s" % (abspath, d)
548 yield {"parity": next(parity),
548 yield {"parity": next(parity),
549 "path": path,
549 "path": path,
550 "emptydirs": "/".join(emptydirs),
550 "emptydirs": "/".join(emptydirs),
551 "basename": d}
551 "basename": d}
552
552
553 return tmpl("manifest",
553 return tmpl("manifest",
554 symrev=symrev,
554 symrev=symrev,
555 path=abspath,
555 path=abspath,
556 up=webutil.up(abspath),
556 up=webutil.up(abspath),
557 upparity=next(parity),
557 upparity=next(parity),
558 fentries=filelist,
558 fentries=filelist,
559 dentries=dirlist,
559 dentries=dirlist,
560 archives=web.archivelist(hex(node)),
560 archives=web.archivelist(hex(node)),
561 **webutil.commonentry(web.repo, ctx))
561 **webutil.commonentry(web.repo, ctx))
562
562
563 @webcommand('tags')
563 @webcommand('tags')
564 def tags(web, req, tmpl):
564 def tags(web, req, tmpl):
565 """
565 """
566 /tags
566 /tags
567 -----
567 -----
568
568
569 Show information about tags.
569 Show information about tags.
570
570
571 No arguments are accepted.
571 No arguments are accepted.
572
572
573 The ``tags`` template is rendered.
573 The ``tags`` template is rendered.
574 """
574 """
575 i = list(reversed(web.repo.tagslist()))
575 i = list(reversed(web.repo.tagslist()))
576 parity = paritygen(web.stripecount)
576 parity = paritygen(web.stripecount)
577
577
578 def entries(notip, latestonly, **map):
578 def entries(notip, latestonly, **map):
579 t = i
579 t = i
580 if notip:
580 if notip:
581 t = [(k, n) for k, n in i if k != "tip"]
581 t = [(k, n) for k, n in i if k != "tip"]
582 if latestonly:
582 if latestonly:
583 t = t[:1]
583 t = t[:1]
584 for k, n in t:
584 for k, n in t:
585 yield {"parity": next(parity),
585 yield {"parity": next(parity),
586 "tag": k,
586 "tag": k,
587 "date": web.repo[n].date(),
587 "date": web.repo[n].date(),
588 "node": hex(n)}
588 "node": hex(n)}
589
589
590 return tmpl("tags",
590 return tmpl("tags",
591 node=hex(web.repo.changelog.tip()),
591 node=hex(web.repo.changelog.tip()),
592 entries=lambda **x: entries(False, False, **x),
592 entries=lambda **x: entries(False, False, **x),
593 entriesnotip=lambda **x: entries(True, False, **x),
593 entriesnotip=lambda **x: entries(True, False, **x),
594 latestentry=lambda **x: entries(True, True, **x))
594 latestentry=lambda **x: entries(True, True, **x))
595
595
596 @webcommand('bookmarks')
596 @webcommand('bookmarks')
597 def bookmarks(web, req, tmpl):
597 def bookmarks(web, req, tmpl):
598 """
598 """
599 /bookmarks
599 /bookmarks
600 ----------
600 ----------
601
601
602 Show information about bookmarks.
602 Show information about bookmarks.
603
603
604 No arguments are accepted.
604 No arguments are accepted.
605
605
606 The ``bookmarks`` template is rendered.
606 The ``bookmarks`` template is rendered.
607 """
607 """
608 i = [b for b in web.repo._bookmarks.items() if b[1] in web.repo]
608 i = [b for b in web.repo._bookmarks.items() if b[1] in web.repo]
609 sortkey = lambda b: (web.repo[b[1]].rev(), b[0])
609 sortkey = lambda b: (web.repo[b[1]].rev(), b[0])
610 i = sorted(i, key=sortkey, reverse=True)
610 i = sorted(i, key=sortkey, reverse=True)
611 parity = paritygen(web.stripecount)
611 parity = paritygen(web.stripecount)
612
612
613 def entries(latestonly, **map):
613 def entries(latestonly, **map):
614 t = i
614 t = i
615 if latestonly:
615 if latestonly:
616 t = i[:1]
616 t = i[:1]
617 for k, n in t:
617 for k, n in t:
618 yield {"parity": next(parity),
618 yield {"parity": next(parity),
619 "bookmark": k,
619 "bookmark": k,
620 "date": web.repo[n].date(),
620 "date": web.repo[n].date(),
621 "node": hex(n)}
621 "node": hex(n)}
622
622
623 if i:
623 if i:
624 latestrev = i[0][1]
624 latestrev = i[0][1]
625 else:
625 else:
626 latestrev = -1
626 latestrev = -1
627
627
628 return tmpl("bookmarks",
628 return tmpl("bookmarks",
629 node=hex(web.repo.changelog.tip()),
629 node=hex(web.repo.changelog.tip()),
630 lastchange=[{"date": web.repo[latestrev].date()}],
630 lastchange=[{"date": web.repo[latestrev].date()}],
631 entries=lambda **x: entries(latestonly=False, **x),
631 entries=lambda **x: entries(latestonly=False, **x),
632 latestentry=lambda **x: entries(latestonly=True, **x))
632 latestentry=lambda **x: entries(latestonly=True, **x))
633
633
634 @webcommand('branches')
634 @webcommand('branches')
635 def branches(web, req, tmpl):
635 def branches(web, req, tmpl):
636 """
636 """
637 /branches
637 /branches
638 ---------
638 ---------
639
639
640 Show information about branches.
640 Show information about branches.
641
641
642 All known branches are contained in the output, even closed branches.
642 All known branches are contained in the output, even closed branches.
643
643
644 No arguments are accepted.
644 No arguments are accepted.
645
645
646 The ``branches`` template is rendered.
646 The ``branches`` template is rendered.
647 """
647 """
648 entries = webutil.branchentries(web.repo, web.stripecount)
648 entries = webutil.branchentries(web.repo, web.stripecount)
649 latestentry = webutil.branchentries(web.repo, web.stripecount, 1)
649 latestentry = webutil.branchentries(web.repo, web.stripecount, 1)
650 return tmpl('branches', node=hex(web.repo.changelog.tip()),
650 return tmpl('branches', node=hex(web.repo.changelog.tip()),
651 entries=entries, latestentry=latestentry)
651 entries=entries, latestentry=latestentry)
652
652
653 @webcommand('summary')
653 @webcommand('summary')
654 def summary(web, req, tmpl):
654 def summary(web, req, tmpl):
655 """
655 """
656 /summary
656 /summary
657 --------
657 --------
658
658
659 Show a summary of repository state.
659 Show a summary of repository state.
660
660
661 Information about the latest changesets, bookmarks, tags, and branches
661 Information about the latest changesets, bookmarks, tags, and branches
662 is captured by this handler.
662 is captured by this handler.
663
663
664 The ``summary`` template is rendered.
664 The ``summary`` template is rendered.
665 """
665 """
666 i = reversed(web.repo.tagslist())
666 i = reversed(web.repo.tagslist())
667
667
668 def tagentries(**map):
668 def tagentries(**map):
669 parity = paritygen(web.stripecount)
669 parity = paritygen(web.stripecount)
670 count = 0
670 count = 0
671 for k, n in i:
671 for k, n in i:
672 if k == "tip": # skip tip
672 if k == "tip": # skip tip
673 continue
673 continue
674
674
675 count += 1
675 count += 1
676 if count > 10: # limit to 10 tags
676 if count > 10: # limit to 10 tags
677 break
677 break
678
678
679 yield tmpl("tagentry",
679 yield tmpl("tagentry",
680 parity=next(parity),
680 parity=next(parity),
681 tag=k,
681 tag=k,
682 node=hex(n),
682 node=hex(n),
683 date=web.repo[n].date())
683 date=web.repo[n].date())
684
684
685 def bookmarks(**map):
685 def bookmarks(**map):
686 parity = paritygen(web.stripecount)
686 parity = paritygen(web.stripecount)
687 marks = [b for b in web.repo._bookmarks.items() if b[1] in web.repo]
687 marks = [b for b in web.repo._bookmarks.items() if b[1] in web.repo]
688 sortkey = lambda b: (web.repo[b[1]].rev(), b[0])
688 sortkey = lambda b: (web.repo[b[1]].rev(), b[0])
689 marks = sorted(marks, key=sortkey, reverse=True)
689 marks = sorted(marks, key=sortkey, reverse=True)
690 for k, n in marks[:10]: # limit to 10 bookmarks
690 for k, n in marks[:10]: # limit to 10 bookmarks
691 yield {'parity': next(parity),
691 yield {'parity': next(parity),
692 'bookmark': k,
692 'bookmark': k,
693 'date': web.repo[n].date(),
693 'date': web.repo[n].date(),
694 'node': hex(n)}
694 'node': hex(n)}
695
695
696 def changelist(**map):
696 def changelist(**map):
697 parity = paritygen(web.stripecount, offset=start - end)
697 parity = paritygen(web.stripecount, offset=start - end)
698 l = [] # build a list in forward order for efficiency
698 l = [] # build a list in forward order for efficiency
699 revs = []
699 revs = []
700 if start < end:
700 if start < end:
701 revs = web.repo.changelog.revs(start, end - 1)
701 revs = web.repo.changelog.revs(start, end - 1)
702 for i in revs:
702 for i in revs:
703 ctx = web.repo[i]
703 ctx = web.repo[i]
704
704
705 l.append(tmpl(
705 l.append(tmpl(
706 'shortlogentry',
706 'shortlogentry',
707 parity=next(parity),
707 parity=next(parity),
708 **webutil.commonentry(web.repo, ctx)))
708 **webutil.commonentry(web.repo, ctx)))
709
709
710 for entry in reversed(l):
710 for entry in reversed(l):
711 yield entry
711 yield entry
712
712
713 tip = web.repo['tip']
713 tip = web.repo['tip']
714 count = len(web.repo)
714 count = len(web.repo)
715 start = max(0, count - web.maxchanges)
715 start = max(0, count - web.maxchanges)
716 end = min(count, start + web.maxchanges)
716 end = min(count, start + web.maxchanges)
717
717
718 return tmpl("summary",
718 return tmpl("summary",
719 desc=web.config("web", "description", "unknown"),
719 desc=web.config("web", "description", "unknown"),
720 owner=get_contact(web.config) or "unknown",
720 owner=get_contact(web.config) or "unknown",
721 lastchange=tip.date(),
721 lastchange=tip.date(),
722 tags=tagentries,
722 tags=tagentries,
723 bookmarks=bookmarks,
723 bookmarks=bookmarks,
724 branches=webutil.branchentries(web.repo, web.stripecount, 10),
724 branches=webutil.branchentries(web.repo, web.stripecount, 10),
725 shortlog=changelist,
725 shortlog=changelist,
726 node=tip.hex(),
726 node=tip.hex(),
727 symrev='tip',
727 symrev='tip',
728 archives=web.archivelist("tip"),
728 archives=web.archivelist("tip"),
729 labels=web.configlist('web', 'labels'))
729 labels=web.configlist('web', 'labels'))
730
730
731 @webcommand('filediff')
731 @webcommand('filediff')
732 def filediff(web, req, tmpl):
732 def filediff(web, req, tmpl):
733 """
733 """
734 /diff/{revision}/{path}
734 /diff/{revision}/{path}
735 -----------------------
735 -----------------------
736
736
737 Show how a file changed in a particular commit.
737 Show how a file changed in a particular commit.
738
738
739 The ``filediff`` template is rendered.
739 The ``filediff`` template is rendered.
740
740
741 This handler is registered under both the ``/diff`` and ``/filediff``
741 This handler is registered under both the ``/diff`` and ``/filediff``
742 paths. ``/diff`` is used in modern code.
742 paths. ``/diff`` is used in modern code.
743 """
743 """
744 fctx, ctx = None, None
744 fctx, ctx = None, None
745 try:
745 try:
746 fctx = webutil.filectx(web.repo, req)
746 fctx = webutil.filectx(web.repo, req)
747 except LookupError:
747 except LookupError:
748 ctx = webutil.changectx(web.repo, req)
748 ctx = webutil.changectx(web.repo, req)
749 path = webutil.cleanpath(web.repo, req.form['file'][0])
749 path = webutil.cleanpath(web.repo, req.form['file'][0])
750 if path not in ctx.files():
750 if path not in ctx.files():
751 raise
751 raise
752
752
753 if fctx is not None:
753 if fctx is not None:
754 path = fctx.path()
754 path = fctx.path()
755 ctx = fctx.changectx()
755 ctx = fctx.changectx()
756
756
757 parity = paritygen(web.stripecount)
757 parity = paritygen(web.stripecount)
758 style = web.config('web', 'style', 'paper')
758 style = web.config('web', 'style', 'paper')
759 if 'style' in req.form:
759 if 'style' in req.form:
760 style = req.form['style'][0]
760 style = req.form['style'][0]
761
761
762 diffs = webutil.diffs(web.repo, tmpl, ctx, None, [path], parity, style)
762 diffs = webutil.diffs(web.repo, tmpl, ctx, None, [path], parity, style)
763 if fctx is not None:
763 if fctx is not None:
764 rename = webutil.renamelink(fctx)
764 rename = webutil.renamelink(fctx)
765 ctx = fctx
765 ctx = fctx
766 else:
766 else:
767 rename = []
767 rename = []
768 ctx = ctx
768 ctx = ctx
769 return tmpl("filediff",
769 return tmpl("filediff",
770 file=path,
770 file=path,
771 symrev=webutil.symrevorshortnode(req, ctx),
771 symrev=webutil.symrevorshortnode(req, ctx),
772 rename=rename,
772 rename=rename,
773 diff=diffs,
773 diff=diffs,
774 **webutil.commonentry(web.repo, ctx))
774 **webutil.commonentry(web.repo, ctx))
775
775
776 diff = webcommand('diff')(filediff)
776 diff = webcommand('diff')(filediff)
777
777
778 @webcommand('comparison')
778 @webcommand('comparison')
779 def comparison(web, req, tmpl):
779 def comparison(web, req, tmpl):
780 """
780 """
781 /comparison/{revision}/{path}
781 /comparison/{revision}/{path}
782 -----------------------------
782 -----------------------------
783
783
784 Show a comparison between the old and new versions of a file from changes
784 Show a comparison between the old and new versions of a file from changes
785 made on a particular revision.
785 made on a particular revision.
786
786
787 This is similar to the ``diff`` handler. However, this form features
787 This is similar to the ``diff`` handler. However, this form features
788 a split or side-by-side diff rather than a unified diff.
788 a split or side-by-side diff rather than a unified diff.
789
789
790 The ``context`` query string argument can be used to control the lines of
790 The ``context`` query string argument can be used to control the lines of
791 context in the diff.
791 context in the diff.
792
792
793 The ``filecomparison`` template is rendered.
793 The ``filecomparison`` template is rendered.
794 """
794 """
795 ctx = webutil.changectx(web.repo, req)
795 ctx = webutil.changectx(web.repo, req)
796 if 'file' not in req.form:
796 if 'file' not in req.form:
797 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
797 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
798 path = webutil.cleanpath(web.repo, req.form['file'][0])
798 path = webutil.cleanpath(web.repo, req.form['file'][0])
799
799
800 parsecontext = lambda v: v == 'full' and -1 or int(v)
800 parsecontext = lambda v: v == 'full' and -1 or int(v)
801 if 'context' in req.form:
801 if 'context' in req.form:
802 context = parsecontext(req.form['context'][0])
802 context = parsecontext(req.form['context'][0])
803 else:
803 else:
804 context = parsecontext(web.config('web', 'comparisoncontext', '5'))
804 context = parsecontext(web.config('web', 'comparisoncontext', '5'))
805
805
806 def filelines(f):
806 def filelines(f):
807 if util.binary(f.data()):
807 if util.binary(f.data()):
808 mt = mimetypes.guess_type(f.path())[0]
808 mt = mimetypes.guess_type(f.path())[0]
809 if not mt:
809 if not mt:
810 mt = 'application/octet-stream'
810 mt = 'application/octet-stream'
811 return [_('(binary file %s, hash: %s)') % (mt, hex(f.filenode()))]
811 return [_('(binary file %s, hash: %s)') % (mt, hex(f.filenode()))]
812 return f.data().splitlines()
812 return f.data().splitlines()
813
813
814 fctx = None
814 fctx = None
815 parent = ctx.p1()
815 parent = ctx.p1()
816 leftrev = parent.rev()
816 leftrev = parent.rev()
817 leftnode = parent.node()
817 leftnode = parent.node()
818 rightrev = ctx.rev()
818 rightrev = ctx.rev()
819 rightnode = ctx.node()
819 rightnode = ctx.node()
820 if path in ctx:
820 if path in ctx:
821 fctx = ctx[path]
821 fctx = ctx[path]
822 rightlines = filelines(fctx)
822 rightlines = filelines(fctx)
823 if path not in parent:
823 if path not in parent:
824 leftlines = ()
824 leftlines = ()
825 else:
825 else:
826 pfctx = parent[path]
826 pfctx = parent[path]
827 leftlines = filelines(pfctx)
827 leftlines = filelines(pfctx)
828 else:
828 else:
829 rightlines = ()
829 rightlines = ()
830 pfctx = ctx.parents()[0][path]
830 pfctx = ctx.parents()[0][path]
831 leftlines = filelines(pfctx)
831 leftlines = filelines(pfctx)
832
832
833 comparison = webutil.compare(tmpl, context, leftlines, rightlines)
833 comparison = webutil.compare(tmpl, context, leftlines, rightlines)
834 if fctx is not None:
834 if fctx is not None:
835 rename = webutil.renamelink(fctx)
835 rename = webutil.renamelink(fctx)
836 ctx = fctx
836 ctx = fctx
837 else:
837 else:
838 rename = []
838 rename = []
839 ctx = ctx
839 ctx = ctx
840 return tmpl('filecomparison',
840 return tmpl('filecomparison',
841 file=path,
841 file=path,
842 symrev=webutil.symrevorshortnode(req, ctx),
842 symrev=webutil.symrevorshortnode(req, ctx),
843 rename=rename,
843 rename=rename,
844 leftrev=leftrev,
844 leftrev=leftrev,
845 leftnode=hex(leftnode),
845 leftnode=hex(leftnode),
846 rightrev=rightrev,
846 rightrev=rightrev,
847 rightnode=hex(rightnode),
847 rightnode=hex(rightnode),
848 comparison=comparison,
848 comparison=comparison,
849 **webutil.commonentry(web.repo, ctx))
849 **webutil.commonentry(web.repo, ctx))
850
850
851 @webcommand('annotate')
851 @webcommand('annotate')
852 def annotate(web, req, tmpl):
852 def annotate(web, req, tmpl):
853 """
853 """
854 /annotate/{revision}/{path}
854 /annotate/{revision}/{path}
855 ---------------------------
855 ---------------------------
856
856
857 Show changeset information for each line in a file.
857 Show changeset information for each line in a file.
858
858
859 The ``fileannotate`` template is rendered.
859 The ``fileannotate`` template is rendered.
860 """
860 """
861 fctx = webutil.filectx(web.repo, req)
861 fctx = webutil.filectx(web.repo, req)
862 f = fctx.path()
862 f = fctx.path()
863 parity = paritygen(web.stripecount)
863 parity = paritygen(web.stripecount)
864 diffopts = patch.difffeatureopts(web.repo.ui, untrusted=True,
864 diffopts = patch.difffeatureopts(web.repo.ui, untrusted=True,
865 section='annotate', whitespace=True)
865 section='annotate', whitespace=True)
866
866
867 def parents(f):
867 def parents(f):
868 for p in f.parents():
868 for p in f.parents():
869 yield {
869 yield {
870 "node": p.hex(),
870 "node": p.hex(),
871 "rev": p.rev(),
871 "rev": p.rev(),
872 }
872 }
873
873
874 def annotate(**map):
874 def annotate(**map):
875 if util.binary(fctx.data()):
875 if util.binary(fctx.data()):
876 mt = (mimetypes.guess_type(fctx.path())[0]
876 mt = (mimetypes.guess_type(fctx.path())[0]
877 or 'application/octet-stream')
877 or 'application/octet-stream')
878 lines = [((fctx.filectx(fctx.filerev()), 1), '(binary:%s)' % mt)]
878 lines = [((fctx.filectx(fctx.filerev()), 1), '(binary:%s)' % mt)]
879 else:
879 else:
880 lines = fctx.annotate(follow=True, linenumber=True,
880 lines = fctx.annotate(follow=True, linenumber=True,
881 diffopts=diffopts)
881 diffopts=diffopts)
882 previousrev = None
882 previousrev = None
883 blockparitygen = paritygen(1)
883 for lineno, ((f, targetline), l) in enumerate(lines):
884 for lineno, ((f, targetline), l) in enumerate(lines):
884 rev = f.rev()
885 rev = f.rev()
885 blockhead = rev != previousrev or None
886 if rev != previousrev:
887 blockhead = True
888 blockparity = next(blockparitygen)
889 else:
890 blockhead = None
886 previousrev = rev
891 previousrev = rev
887 yield {"parity": next(parity),
892 yield {"parity": next(parity),
888 "node": f.hex(),
893 "node": f.hex(),
889 "rev": rev,
894 "rev": rev,
890 "author": f.user(),
895 "author": f.user(),
891 "parents": parents(f),
896 "parents": parents(f),
892 "desc": f.description(),
897 "desc": f.description(),
893 "extra": f.extra(),
898 "extra": f.extra(),
894 "file": f.path(),
899 "file": f.path(),
895 "blockhead": blockhead,
900 "blockhead": blockhead,
901 "blockparity": blockparity,
896 "targetline": targetline,
902 "targetline": targetline,
897 "line": l,
903 "line": l,
898 "lineno": lineno + 1,
904 "lineno": lineno + 1,
899 "lineid": "l%d" % (lineno + 1),
905 "lineid": "l%d" % (lineno + 1),
900 "linenumber": "% 6d" % (lineno + 1),
906 "linenumber": "% 6d" % (lineno + 1),
901 "revdate": f.date()}
907 "revdate": f.date()}
902
908
903 return tmpl("fileannotate",
909 return tmpl("fileannotate",
904 file=f,
910 file=f,
905 annotate=annotate,
911 annotate=annotate,
906 path=webutil.up(f),
912 path=webutil.up(f),
907 symrev=webutil.symrevorshortnode(req, fctx),
913 symrev=webutil.symrevorshortnode(req, fctx),
908 rename=webutil.renamelink(fctx),
914 rename=webutil.renamelink(fctx),
909 permissions=fctx.manifest().flags(f),
915 permissions=fctx.manifest().flags(f),
910 **webutil.commonentry(web.repo, fctx))
916 **webutil.commonentry(web.repo, fctx))
911
917
912 @webcommand('filelog')
918 @webcommand('filelog')
913 def filelog(web, req, tmpl):
919 def filelog(web, req, tmpl):
914 """
920 """
915 /filelog/{revision}/{path}
921 /filelog/{revision}/{path}
916 --------------------------
922 --------------------------
917
923
918 Show information about the history of a file in the repository.
924 Show information about the history of a file in the repository.
919
925
920 The ``revcount`` query string argument can be defined to control the
926 The ``revcount`` query string argument can be defined to control the
921 maximum number of entries to show.
927 maximum number of entries to show.
922
928
923 The ``filelog`` template will be rendered.
929 The ``filelog`` template will be rendered.
924 """
930 """
925
931
926 try:
932 try:
927 fctx = webutil.filectx(web.repo, req)
933 fctx = webutil.filectx(web.repo, req)
928 f = fctx.path()
934 f = fctx.path()
929 fl = fctx.filelog()
935 fl = fctx.filelog()
930 except error.LookupError:
936 except error.LookupError:
931 f = webutil.cleanpath(web.repo, req.form['file'][0])
937 f = webutil.cleanpath(web.repo, req.form['file'][0])
932 fl = web.repo.file(f)
938 fl = web.repo.file(f)
933 numrevs = len(fl)
939 numrevs = len(fl)
934 if not numrevs: # file doesn't exist at all
940 if not numrevs: # file doesn't exist at all
935 raise
941 raise
936 rev = webutil.changectx(web.repo, req).rev()
942 rev = webutil.changectx(web.repo, req).rev()
937 first = fl.linkrev(0)
943 first = fl.linkrev(0)
938 if rev < first: # current rev is from before file existed
944 if rev < first: # current rev is from before file existed
939 raise
945 raise
940 frev = numrevs - 1
946 frev = numrevs - 1
941 while fl.linkrev(frev) > rev:
947 while fl.linkrev(frev) > rev:
942 frev -= 1
948 frev -= 1
943 fctx = web.repo.filectx(f, fl.linkrev(frev))
949 fctx = web.repo.filectx(f, fl.linkrev(frev))
944
950
945 revcount = web.maxshortchanges
951 revcount = web.maxshortchanges
946 if 'revcount' in req.form:
952 if 'revcount' in req.form:
947 try:
953 try:
948 revcount = int(req.form.get('revcount', [revcount])[0])
954 revcount = int(req.form.get('revcount', [revcount])[0])
949 revcount = max(revcount, 1)
955 revcount = max(revcount, 1)
950 tmpl.defaults['sessionvars']['revcount'] = revcount
956 tmpl.defaults['sessionvars']['revcount'] = revcount
951 except ValueError:
957 except ValueError:
952 pass
958 pass
953
959
954 lessvars = copy.copy(tmpl.defaults['sessionvars'])
960 lessvars = copy.copy(tmpl.defaults['sessionvars'])
955 lessvars['revcount'] = max(revcount / 2, 1)
961 lessvars['revcount'] = max(revcount / 2, 1)
956 morevars = copy.copy(tmpl.defaults['sessionvars'])
962 morevars = copy.copy(tmpl.defaults['sessionvars'])
957 morevars['revcount'] = revcount * 2
963 morevars['revcount'] = revcount * 2
958
964
959 count = fctx.filerev() + 1
965 count = fctx.filerev() + 1
960 start = max(0, fctx.filerev() - revcount + 1) # first rev on this page
966 start = max(0, fctx.filerev() - revcount + 1) # first rev on this page
961 end = min(count, start + revcount) # last rev on this page
967 end = min(count, start + revcount) # last rev on this page
962 parity = paritygen(web.stripecount, offset=start - end)
968 parity = paritygen(web.stripecount, offset=start - end)
963
969
964 def entries():
970 def entries():
965 l = []
971 l = []
966
972
967 repo = web.repo
973 repo = web.repo
968 revs = fctx.filelog().revs(start, end - 1)
974 revs = fctx.filelog().revs(start, end - 1)
969 for i in revs:
975 for i in revs:
970 iterfctx = fctx.filectx(i)
976 iterfctx = fctx.filectx(i)
971
977
972 l.append(dict(
978 l.append(dict(
973 parity=next(parity),
979 parity=next(parity),
974 filerev=i,
980 filerev=i,
975 file=f,
981 file=f,
976 rename=webutil.renamelink(iterfctx),
982 rename=webutil.renamelink(iterfctx),
977 **webutil.commonentry(repo, iterfctx)))
983 **webutil.commonentry(repo, iterfctx)))
978 for e in reversed(l):
984 for e in reversed(l):
979 yield e
985 yield e
980
986
981 entries = list(entries())
987 entries = list(entries())
982 latestentry = entries[:1]
988 latestentry = entries[:1]
983
989
984 revnav = webutil.filerevnav(web.repo, fctx.path())
990 revnav = webutil.filerevnav(web.repo, fctx.path())
985 nav = revnav.gen(end - 1, revcount, count)
991 nav = revnav.gen(end - 1, revcount, count)
986 return tmpl("filelog",
992 return tmpl("filelog",
987 file=f,
993 file=f,
988 nav=nav,
994 nav=nav,
989 symrev=webutil.symrevorshortnode(req, fctx),
995 symrev=webutil.symrevorshortnode(req, fctx),
990 entries=entries,
996 entries=entries,
991 latestentry=latestentry,
997 latestentry=latestentry,
992 revcount=revcount,
998 revcount=revcount,
993 morevars=morevars,
999 morevars=morevars,
994 lessvars=lessvars,
1000 lessvars=lessvars,
995 **webutil.commonentry(web.repo, fctx))
1001 **webutil.commonentry(web.repo, fctx))
996
1002
997 @webcommand('archive')
1003 @webcommand('archive')
998 def archive(web, req, tmpl):
1004 def archive(web, req, tmpl):
999 """
1005 """
1000 /archive/{revision}.{format}[/{path}]
1006 /archive/{revision}.{format}[/{path}]
1001 -------------------------------------
1007 -------------------------------------
1002
1008
1003 Obtain an archive of repository content.
1009 Obtain an archive of repository content.
1004
1010
1005 The content and type of the archive is defined by a URL path parameter.
1011 The content and type of the archive is defined by a URL path parameter.
1006 ``format`` is the file extension of the archive type to be generated. e.g.
1012 ``format`` is the file extension of the archive type to be generated. e.g.
1007 ``zip`` or ``tar.bz2``. Not all archive types may be allowed by your
1013 ``zip`` or ``tar.bz2``. Not all archive types may be allowed by your
1008 server configuration.
1014 server configuration.
1009
1015
1010 The optional ``path`` URL parameter controls content to include in the
1016 The optional ``path`` URL parameter controls content to include in the
1011 archive. If omitted, every file in the specified revision is present in the
1017 archive. If omitted, every file in the specified revision is present in the
1012 archive. If included, only the specified file or contents of the specified
1018 archive. If included, only the specified file or contents of the specified
1013 directory will be included in the archive.
1019 directory will be included in the archive.
1014
1020
1015 No template is used for this handler. Raw, binary content is generated.
1021 No template is used for this handler. Raw, binary content is generated.
1016 """
1022 """
1017
1023
1018 type_ = req.form.get('type', [None])[0]
1024 type_ = req.form.get('type', [None])[0]
1019 allowed = web.configlist("web", "allow_archive")
1025 allowed = web.configlist("web", "allow_archive")
1020 key = req.form['node'][0]
1026 key = req.form['node'][0]
1021
1027
1022 if type_ not in web.archives:
1028 if type_ not in web.archives:
1023 msg = 'Unsupported archive type: %s' % type_
1029 msg = 'Unsupported archive type: %s' % type_
1024 raise ErrorResponse(HTTP_NOT_FOUND, msg)
1030 raise ErrorResponse(HTTP_NOT_FOUND, msg)
1025
1031
1026 if not ((type_ in allowed or
1032 if not ((type_ in allowed or
1027 web.configbool("web", "allow" + type_, False))):
1033 web.configbool("web", "allow" + type_, False))):
1028 msg = 'Archive type not allowed: %s' % type_
1034 msg = 'Archive type not allowed: %s' % type_
1029 raise ErrorResponse(HTTP_FORBIDDEN, msg)
1035 raise ErrorResponse(HTTP_FORBIDDEN, msg)
1030
1036
1031 reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame))
1037 reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame))
1032 cnode = web.repo.lookup(key)
1038 cnode = web.repo.lookup(key)
1033 arch_version = key
1039 arch_version = key
1034 if cnode == key or key == 'tip':
1040 if cnode == key or key == 'tip':
1035 arch_version = short(cnode)
1041 arch_version = short(cnode)
1036 name = "%s-%s" % (reponame, arch_version)
1042 name = "%s-%s" % (reponame, arch_version)
1037
1043
1038 ctx = webutil.changectx(web.repo, req)
1044 ctx = webutil.changectx(web.repo, req)
1039 pats = []
1045 pats = []
1040 matchfn = scmutil.match(ctx, [])
1046 matchfn = scmutil.match(ctx, [])
1041 file = req.form.get('file', None)
1047 file = req.form.get('file', None)
1042 if file:
1048 if file:
1043 pats = ['path:' + file[0]]
1049 pats = ['path:' + file[0]]
1044 matchfn = scmutil.match(ctx, pats, default='path')
1050 matchfn = scmutil.match(ctx, pats, default='path')
1045 if pats:
1051 if pats:
1046 files = [f for f in ctx.manifest().keys() if matchfn(f)]
1052 files = [f for f in ctx.manifest().keys() if matchfn(f)]
1047 if not files:
1053 if not files:
1048 raise ErrorResponse(HTTP_NOT_FOUND,
1054 raise ErrorResponse(HTTP_NOT_FOUND,
1049 'file(s) not found: %s' % file[0])
1055 'file(s) not found: %s' % file[0])
1050
1056
1051 mimetype, artype, extension, encoding = web.archivespecs[type_]
1057 mimetype, artype, extension, encoding = web.archivespecs[type_]
1052 headers = [
1058 headers = [
1053 ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
1059 ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
1054 ]
1060 ]
1055 if encoding:
1061 if encoding:
1056 headers.append(('Content-Encoding', encoding))
1062 headers.append(('Content-Encoding', encoding))
1057 req.headers.extend(headers)
1063 req.headers.extend(headers)
1058 req.respond(HTTP_OK, mimetype)
1064 req.respond(HTTP_OK, mimetype)
1059
1065
1060 archival.archive(web.repo, req, cnode, artype, prefix=name,
1066 archival.archive(web.repo, req, cnode, artype, prefix=name,
1061 matchfn=matchfn,
1067 matchfn=matchfn,
1062 subrepos=web.configbool("web", "archivesubrepos"))
1068 subrepos=web.configbool("web", "archivesubrepos"))
1063 return []
1069 return []
1064
1070
1065
1071
1066 @webcommand('static')
1072 @webcommand('static')
1067 def static(web, req, tmpl):
1073 def static(web, req, tmpl):
1068 fname = req.form['file'][0]
1074 fname = req.form['file'][0]
1069 # a repo owner may set web.static in .hg/hgrc to get any file
1075 # a repo owner may set web.static in .hg/hgrc to get any file
1070 # readable by the user running the CGI script
1076 # readable by the user running the CGI script
1071 static = web.config("web", "static", None, untrusted=False)
1077 static = web.config("web", "static", None, untrusted=False)
1072 if not static:
1078 if not static:
1073 tp = web.templatepath or templater.templatepaths()
1079 tp = web.templatepath or templater.templatepaths()
1074 if isinstance(tp, str):
1080 if isinstance(tp, str):
1075 tp = [tp]
1081 tp = [tp]
1076 static = [os.path.join(p, 'static') for p in tp]
1082 static = [os.path.join(p, 'static') for p in tp]
1077 staticfile(static, fname, req)
1083 staticfile(static, fname, req)
1078 return []
1084 return []
1079
1085
1080 @webcommand('graph')
1086 @webcommand('graph')
1081 def graph(web, req, tmpl):
1087 def graph(web, req, tmpl):
1082 """
1088 """
1083 /graph[/{revision}]
1089 /graph[/{revision}]
1084 -------------------
1090 -------------------
1085
1091
1086 Show information about the graphical topology of the repository.
1092 Show information about the graphical topology of the repository.
1087
1093
1088 Information rendered by this handler can be used to create visual
1094 Information rendered by this handler can be used to create visual
1089 representations of repository topology.
1095 representations of repository topology.
1090
1096
1091 The ``revision`` URL parameter controls the starting changeset.
1097 The ``revision`` URL parameter controls the starting changeset.
1092
1098
1093 The ``revcount`` query string argument can define the number of changesets
1099 The ``revcount`` query string argument can define the number of changesets
1094 to show information for.
1100 to show information for.
1095
1101
1096 This handler will render the ``graph`` template.
1102 This handler will render the ``graph`` template.
1097 """
1103 """
1098
1104
1099 if 'node' in req.form:
1105 if 'node' in req.form:
1100 ctx = webutil.changectx(web.repo, req)
1106 ctx = webutil.changectx(web.repo, req)
1101 symrev = webutil.symrevorshortnode(req, ctx)
1107 symrev = webutil.symrevorshortnode(req, ctx)
1102 else:
1108 else:
1103 ctx = web.repo['tip']
1109 ctx = web.repo['tip']
1104 symrev = 'tip'
1110 symrev = 'tip'
1105 rev = ctx.rev()
1111 rev = ctx.rev()
1106
1112
1107 bg_height = 39
1113 bg_height = 39
1108 revcount = web.maxshortchanges
1114 revcount = web.maxshortchanges
1109 if 'revcount' in req.form:
1115 if 'revcount' in req.form:
1110 try:
1116 try:
1111 revcount = int(req.form.get('revcount', [revcount])[0])
1117 revcount = int(req.form.get('revcount', [revcount])[0])
1112 revcount = max(revcount, 1)
1118 revcount = max(revcount, 1)
1113 tmpl.defaults['sessionvars']['revcount'] = revcount
1119 tmpl.defaults['sessionvars']['revcount'] = revcount
1114 except ValueError:
1120 except ValueError:
1115 pass
1121 pass
1116
1122
1117 lessvars = copy.copy(tmpl.defaults['sessionvars'])
1123 lessvars = copy.copy(tmpl.defaults['sessionvars'])
1118 lessvars['revcount'] = max(revcount / 2, 1)
1124 lessvars['revcount'] = max(revcount / 2, 1)
1119 morevars = copy.copy(tmpl.defaults['sessionvars'])
1125 morevars = copy.copy(tmpl.defaults['sessionvars'])
1120 morevars['revcount'] = revcount * 2
1126 morevars['revcount'] = revcount * 2
1121
1127
1122 count = len(web.repo)
1128 count = len(web.repo)
1123 pos = rev
1129 pos = rev
1124
1130
1125 uprev = min(max(0, count - 1), rev + revcount)
1131 uprev = min(max(0, count - 1), rev + revcount)
1126 downrev = max(0, rev - revcount)
1132 downrev = max(0, rev - revcount)
1127 changenav = webutil.revnav(web.repo).gen(pos, revcount, count)
1133 changenav = webutil.revnav(web.repo).gen(pos, revcount, count)
1128
1134
1129 tree = []
1135 tree = []
1130 if pos != -1:
1136 if pos != -1:
1131 allrevs = web.repo.changelog.revs(pos, 0)
1137 allrevs = web.repo.changelog.revs(pos, 0)
1132 revs = []
1138 revs = []
1133 for i in allrevs:
1139 for i in allrevs:
1134 revs.append(i)
1140 revs.append(i)
1135 if len(revs) >= revcount:
1141 if len(revs) >= revcount:
1136 break
1142 break
1137
1143
1138 # We have to feed a baseset to dagwalker as it is expecting smartset
1144 # We have to feed a baseset to dagwalker as it is expecting smartset
1139 # object. This does not have a big impact on hgweb performance itself
1145 # object. This does not have a big impact on hgweb performance itself
1140 # since hgweb graphing code is not itself lazy yet.
1146 # since hgweb graphing code is not itself lazy yet.
1141 dag = graphmod.dagwalker(web.repo, revset.baseset(revs))
1147 dag = graphmod.dagwalker(web.repo, revset.baseset(revs))
1142 # As we said one line above... not lazy.
1148 # As we said one line above... not lazy.
1143 tree = list(graphmod.colored(dag, web.repo))
1149 tree = list(graphmod.colored(dag, web.repo))
1144
1150
1145 def getcolumns(tree):
1151 def getcolumns(tree):
1146 cols = 0
1152 cols = 0
1147 for (id, type, ctx, vtx, edges) in tree:
1153 for (id, type, ctx, vtx, edges) in tree:
1148 if type != graphmod.CHANGESET:
1154 if type != graphmod.CHANGESET:
1149 continue
1155 continue
1150 cols = max(cols, max([edge[0] for edge in edges] or [0]),
1156 cols = max(cols, max([edge[0] for edge in edges] or [0]),
1151 max([edge[1] for edge in edges] or [0]))
1157 max([edge[1] for edge in edges] or [0]))
1152 return cols
1158 return cols
1153
1159
1154 def graphdata(usetuples, encodestr):
1160 def graphdata(usetuples, encodestr):
1155 data = []
1161 data = []
1156
1162
1157 row = 0
1163 row = 0
1158 for (id, type, ctx, vtx, edges) in tree:
1164 for (id, type, ctx, vtx, edges) in tree:
1159 if type != graphmod.CHANGESET:
1165 if type != graphmod.CHANGESET:
1160 continue
1166 continue
1161 node = str(ctx)
1167 node = str(ctx)
1162 age = encodestr(templatefilters.age(ctx.date()))
1168 age = encodestr(templatefilters.age(ctx.date()))
1163 desc = templatefilters.firstline(encodestr(ctx.description()))
1169 desc = templatefilters.firstline(encodestr(ctx.description()))
1164 desc = cgi.escape(templatefilters.nonempty(desc))
1170 desc = cgi.escape(templatefilters.nonempty(desc))
1165 user = cgi.escape(templatefilters.person(encodestr(ctx.user())))
1171 user = cgi.escape(templatefilters.person(encodestr(ctx.user())))
1166 branch = cgi.escape(encodestr(ctx.branch()))
1172 branch = cgi.escape(encodestr(ctx.branch()))
1167 try:
1173 try:
1168 branchnode = web.repo.branchtip(branch)
1174 branchnode = web.repo.branchtip(branch)
1169 except error.RepoLookupError:
1175 except error.RepoLookupError:
1170 branchnode = None
1176 branchnode = None
1171 branch = branch, branchnode == ctx.node()
1177 branch = branch, branchnode == ctx.node()
1172
1178
1173 if usetuples:
1179 if usetuples:
1174 data.append((node, vtx, edges, desc, user, age, branch,
1180 data.append((node, vtx, edges, desc, user, age, branch,
1175 [cgi.escape(encodestr(x)) for x in ctx.tags()],
1181 [cgi.escape(encodestr(x)) for x in ctx.tags()],
1176 [cgi.escape(encodestr(x))
1182 [cgi.escape(encodestr(x))
1177 for x in ctx.bookmarks()]))
1183 for x in ctx.bookmarks()]))
1178 else:
1184 else:
1179 edgedata = [{'col': edge[0], 'nextcol': edge[1],
1185 edgedata = [{'col': edge[0], 'nextcol': edge[1],
1180 'color': (edge[2] - 1) % 6 + 1,
1186 'color': (edge[2] - 1) % 6 + 1,
1181 'width': edge[3], 'bcolor': edge[4]}
1187 'width': edge[3], 'bcolor': edge[4]}
1182 for edge in edges]
1188 for edge in edges]
1183
1189
1184 data.append(
1190 data.append(
1185 {'node': node,
1191 {'node': node,
1186 'col': vtx[0],
1192 'col': vtx[0],
1187 'color': (vtx[1] - 1) % 6 + 1,
1193 'color': (vtx[1] - 1) % 6 + 1,
1188 'edges': edgedata,
1194 'edges': edgedata,
1189 'row': row,
1195 'row': row,
1190 'nextrow': row + 1,
1196 'nextrow': row + 1,
1191 'desc': desc,
1197 'desc': desc,
1192 'user': user,
1198 'user': user,
1193 'age': age,
1199 'age': age,
1194 'bookmarks': webutil.nodebookmarksdict(
1200 'bookmarks': webutil.nodebookmarksdict(
1195 web.repo, ctx.node()),
1201 web.repo, ctx.node()),
1196 'branches': webutil.nodebranchdict(web.repo, ctx),
1202 'branches': webutil.nodebranchdict(web.repo, ctx),
1197 'inbranch': webutil.nodeinbranch(web.repo, ctx),
1203 'inbranch': webutil.nodeinbranch(web.repo, ctx),
1198 'tags': webutil.nodetagsdict(web.repo, ctx.node())})
1204 'tags': webutil.nodetagsdict(web.repo, ctx.node())})
1199
1205
1200 row += 1
1206 row += 1
1201
1207
1202 return data
1208 return data
1203
1209
1204 cols = getcolumns(tree)
1210 cols = getcolumns(tree)
1205 rows = len(tree)
1211 rows = len(tree)
1206 canvasheight = (rows + 1) * bg_height - 27
1212 canvasheight = (rows + 1) * bg_height - 27
1207
1213
1208 return tmpl('graph', rev=rev, symrev=symrev, revcount=revcount,
1214 return tmpl('graph', rev=rev, symrev=symrev, revcount=revcount,
1209 uprev=uprev,
1215 uprev=uprev,
1210 lessvars=lessvars, morevars=morevars, downrev=downrev,
1216 lessvars=lessvars, morevars=morevars, downrev=downrev,
1211 cols=cols, rows=rows,
1217 cols=cols, rows=rows,
1212 canvaswidth=(cols + 1) * bg_height,
1218 canvaswidth=(cols + 1) * bg_height,
1213 truecanvasheight=rows * bg_height,
1219 truecanvasheight=rows * bg_height,
1214 canvasheight=canvasheight, bg_height=bg_height,
1220 canvasheight=canvasheight, bg_height=bg_height,
1215 # {jsdata} will be passed to |json, so it must be in utf-8
1221 # {jsdata} will be passed to |json, so it must be in utf-8
1216 jsdata=lambda **x: graphdata(True, encoding.fromlocal),
1222 jsdata=lambda **x: graphdata(True, encoding.fromlocal),
1217 nodes=lambda **x: graphdata(False, str),
1223 nodes=lambda **x: graphdata(False, str),
1218 node=ctx.hex(), changenav=changenav)
1224 node=ctx.hex(), changenav=changenav)
1219
1225
1220 def _getdoc(e):
1226 def _getdoc(e):
1221 doc = e[0].__doc__
1227 doc = e[0].__doc__
1222 if doc:
1228 if doc:
1223 doc = _(doc).partition('\n')[0]
1229 doc = _(doc).partition('\n')[0]
1224 else:
1230 else:
1225 doc = _('(no help text available)')
1231 doc = _('(no help text available)')
1226 return doc
1232 return doc
1227
1233
1228 @webcommand('help')
1234 @webcommand('help')
1229 def help(web, req, tmpl):
1235 def help(web, req, tmpl):
1230 """
1236 """
1231 /help[/{topic}]
1237 /help[/{topic}]
1232 ---------------
1238 ---------------
1233
1239
1234 Render help documentation.
1240 Render help documentation.
1235
1241
1236 This web command is roughly equivalent to :hg:`help`. If a ``topic``
1242 This web command is roughly equivalent to :hg:`help`. If a ``topic``
1237 is defined, that help topic will be rendered. If not, an index of
1243 is defined, that help topic will be rendered. If not, an index of
1238 available help topics will be rendered.
1244 available help topics will be rendered.
1239
1245
1240 The ``help`` template will be rendered when requesting help for a topic.
1246 The ``help`` template will be rendered when requesting help for a topic.
1241 ``helptopics`` will be rendered for the index of help topics.
1247 ``helptopics`` will be rendered for the index of help topics.
1242 """
1248 """
1243 from .. import commands, help as helpmod # avoid cycle
1249 from .. import commands, help as helpmod # avoid cycle
1244
1250
1245 topicname = req.form.get('node', [None])[0]
1251 topicname = req.form.get('node', [None])[0]
1246 if not topicname:
1252 if not topicname:
1247 def topics(**map):
1253 def topics(**map):
1248 for entries, summary, _doc in helpmod.helptable:
1254 for entries, summary, _doc in helpmod.helptable:
1249 yield {'topic': entries[0], 'summary': summary}
1255 yield {'topic': entries[0], 'summary': summary}
1250
1256
1251 early, other = [], []
1257 early, other = [], []
1252 primary = lambda s: s.partition('|')[0]
1258 primary = lambda s: s.partition('|')[0]
1253 for c, e in commands.table.iteritems():
1259 for c, e in commands.table.iteritems():
1254 doc = _getdoc(e)
1260 doc = _getdoc(e)
1255 if 'DEPRECATED' in doc or c.startswith('debug'):
1261 if 'DEPRECATED' in doc or c.startswith('debug'):
1256 continue
1262 continue
1257 cmd = primary(c)
1263 cmd = primary(c)
1258 if cmd.startswith('^'):
1264 if cmd.startswith('^'):
1259 early.append((cmd[1:], doc))
1265 early.append((cmd[1:], doc))
1260 else:
1266 else:
1261 other.append((cmd, doc))
1267 other.append((cmd, doc))
1262
1268
1263 early.sort()
1269 early.sort()
1264 other.sort()
1270 other.sort()
1265
1271
1266 def earlycommands(**map):
1272 def earlycommands(**map):
1267 for c, doc in early:
1273 for c, doc in early:
1268 yield {'topic': c, 'summary': doc}
1274 yield {'topic': c, 'summary': doc}
1269
1275
1270 def othercommands(**map):
1276 def othercommands(**map):
1271 for c, doc in other:
1277 for c, doc in other:
1272 yield {'topic': c, 'summary': doc}
1278 yield {'topic': c, 'summary': doc}
1273
1279
1274 return tmpl('helptopics', topics=topics, earlycommands=earlycommands,
1280 return tmpl('helptopics', topics=topics, earlycommands=earlycommands,
1275 othercommands=othercommands, title='Index')
1281 othercommands=othercommands, title='Index')
1276
1282
1277 # Render an index of sub-topics.
1283 # Render an index of sub-topics.
1278 if topicname in helpmod.subtopics:
1284 if topicname in helpmod.subtopics:
1279 topics = []
1285 topics = []
1280 for entries, summary, _doc in helpmod.subtopics[topicname]:
1286 for entries, summary, _doc in helpmod.subtopics[topicname]:
1281 topics.append({
1287 topics.append({
1282 'topic': '%s.%s' % (topicname, entries[0]),
1288 'topic': '%s.%s' % (topicname, entries[0]),
1283 'basename': entries[0],
1289 'basename': entries[0],
1284 'summary': summary,
1290 'summary': summary,
1285 })
1291 })
1286
1292
1287 return tmpl('helptopics', topics=topics, title=topicname,
1293 return tmpl('helptopics', topics=topics, title=topicname,
1288 subindex=True)
1294 subindex=True)
1289
1295
1290 u = webutil.wsgiui()
1296 u = webutil.wsgiui()
1291 u.verbose = True
1297 u.verbose = True
1292
1298
1293 # Render a page from a sub-topic.
1299 # Render a page from a sub-topic.
1294 if '.' in topicname:
1300 if '.' in topicname:
1295 # TODO implement support for rendering sections, like
1301 # TODO implement support for rendering sections, like
1296 # `hg help` works.
1302 # `hg help` works.
1297 topic, subtopic = topicname.split('.', 1)
1303 topic, subtopic = topicname.split('.', 1)
1298 if topic not in helpmod.subtopics:
1304 if topic not in helpmod.subtopics:
1299 raise ErrorResponse(HTTP_NOT_FOUND)
1305 raise ErrorResponse(HTTP_NOT_FOUND)
1300 else:
1306 else:
1301 topic = topicname
1307 topic = topicname
1302 subtopic = None
1308 subtopic = None
1303
1309
1304 try:
1310 try:
1305 doc = helpmod.help_(u, topic, subtopic=subtopic)
1311 doc = helpmod.help_(u, topic, subtopic=subtopic)
1306 except error.UnknownCommand:
1312 except error.UnknownCommand:
1307 raise ErrorResponse(HTTP_NOT_FOUND)
1313 raise ErrorResponse(HTTP_NOT_FOUND)
1308 return tmpl('help', topic=topicname, doc=doc)
1314 return tmpl('help', topic=topicname, doc=doc)
1309
1315
1310 # tell hggettext to extract docstrings from these functions:
1316 # tell hggettext to extract docstrings from these functions:
1311 i18nfunctions = commands.values()
1317 i18nfunctions = commands.values()
@@ -1,272 +1,272 b''
1 default = 'shortlog'
1 default = 'shortlog'
2
2
3 mimetype = 'text/html; charset={encoding}'
3 mimetype = 'text/html; charset={encoding}'
4 header = header.tmpl
4 header = header.tmpl
5 footer = footer.tmpl
5 footer = footer.tmpl
6 search = search.tmpl
6 search = search.tmpl
7
7
8 changelog = shortlog.tmpl
8 changelog = shortlog.tmpl
9 shortlog = shortlog.tmpl
9 shortlog = shortlog.tmpl
10 shortlogentry = shortlogentry.tmpl
10 shortlogentry = shortlogentry.tmpl
11 graph = graph.tmpl
11 graph = graph.tmpl
12 help = help.tmpl
12 help = help.tmpl
13 helptopics = helptopics.tmpl
13 helptopics = helptopics.tmpl
14
14
15 helpentry = '
15 helpentry = '
16 <tr><td>
16 <tr><td>
17 <a href="{url|urlescape}help/{topic|escape}{sessionvars%urlparameter}">
17 <a href="{url|urlescape}help/{topic|escape}{sessionvars%urlparameter}">
18 {if(basename, '{basename|escape}', '{topic|escape}')}
18 {if(basename, '{basename|escape}', '{topic|escape}')}
19 </a>
19 </a>
20 </td><td>
20 </td><td>
21 {summary|escape}
21 {summary|escape}
22 </td></tr>'
22 </td></tr>'
23
23
24 naventry = '<a href="{url|urlescape}log/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
24 naventry = '<a href="{url|urlescape}log/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
25 navshortentry = '<a href="{url|urlescape}shortlog/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
25 navshortentry = '<a href="{url|urlescape}shortlog/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
26 navgraphentry = '<a href="{url|urlescape}graph/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
26 navgraphentry = '<a href="{url|urlescape}graph/{node|short}{sessionvars%urlparameter}">{label|escape}</a> '
27 filenaventry = '<a href="{url|urlescape}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{label|escape}</a> '
27 filenaventry = '<a href="{url|urlescape}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{label|escape}</a> '
28 filedifflink = '<a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a> '
28 filedifflink = '<a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a> '
29 filenodelink = '<a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a> '
29 filenodelink = '<a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a> '
30 filenolink = '{file|escape} '
30 filenolink = '{file|escape} '
31 fileellipses = '...'
31 fileellipses = '...'
32 diffstatlink = diffstat.tmpl
32 diffstatlink = diffstat.tmpl
33 diffstatnolink = diffstat.tmpl
33 diffstatnolink = diffstat.tmpl
34 changelogentry = shortlogentry.tmpl
34 changelogentry = shortlogentry.tmpl
35 searchentry = shortlogentry.tmpl
35 searchentry = shortlogentry.tmpl
36 changeset = changeset.tmpl
36 changeset = changeset.tmpl
37 manifest = manifest.tmpl
37 manifest = manifest.tmpl
38
38
39 nav = '{before%naventry} {after%naventry}'
39 nav = '{before%naventry} {after%naventry}'
40 navshort = '{before%navshortentry}{after%navshortentry}'
40 navshort = '{before%navshortentry}{after%navshortentry}'
41 navgraph = '{before%navgraphentry}{after%navgraphentry}'
41 navgraph = '{before%navgraphentry}{after%navgraphentry}'
42 filenav = '{before%filenaventry}{after%filenaventry}'
42 filenav = '{before%filenaventry}{after%filenaventry}'
43
43
44 direntry = '
44 direntry = '
45 <tr class="fileline">
45 <tr class="fileline">
46 <td class="name">
46 <td class="name">
47 <a href="{url|urlescape}file/{symrev}{path|urlescape}{sessionvars%urlparameter}">
47 <a href="{url|urlescape}file/{symrev}{path|urlescape}{sessionvars%urlparameter}">
48 <img src="{staticurl|urlescape}coal-folder.png" alt="dir."/> {basename|escape}/
48 <img src="{staticurl|urlescape}coal-folder.png" alt="dir."/> {basename|escape}/
49 </a>
49 </a>
50 <a href="{url|urlescape}file/{symrev}{path|urlescape}/{emptydirs|urlescape}{sessionvars%urlparameter}">
50 <a href="{url|urlescape}file/{symrev}{path|urlescape}/{emptydirs|urlescape}{sessionvars%urlparameter}">
51 {emptydirs|escape}
51 {emptydirs|escape}
52 </a>
52 </a>
53 </td>
53 </td>
54 <td class="size"></td>
54 <td class="size"></td>
55 <td class="permissions">drwxr-xr-x</td>
55 <td class="permissions">drwxr-xr-x</td>
56 </tr>'
56 </tr>'
57
57
58 fileentry = '
58 fileentry = '
59 <tr class="fileline">
59 <tr class="fileline">
60 <td class="filename">
60 <td class="filename">
61 <a href="{url|urlescape}file/{symrev}/{file|urlescape}{sessionvars%urlparameter}">
61 <a href="{url|urlescape}file/{symrev}/{file|urlescape}{sessionvars%urlparameter}">
62 <img src="{staticurl|urlescape}coal-file.png" alt="file"/> {basename|escape}
62 <img src="{staticurl|urlescape}coal-file.png" alt="file"/> {basename|escape}
63 </a>
63 </a>
64 </td>
64 </td>
65 <td class="size">{size}</td>
65 <td class="size">{size}</td>
66 <td class="permissions">{permissions|permissions}</td>
66 <td class="permissions">{permissions|permissions}</td>
67 </tr>'
67 </tr>'
68
68
69 filerevision = filerevision.tmpl
69 filerevision = filerevision.tmpl
70 fileannotate = fileannotate.tmpl
70 fileannotate = fileannotate.tmpl
71 filediff = filediff.tmpl
71 filediff = filediff.tmpl
72 filecomparison = filecomparison.tmpl
72 filecomparison = filecomparison.tmpl
73 filelog = filelog.tmpl
73 filelog = filelog.tmpl
74 fileline = '
74 fileline = '
75 <span id="{lineid}">{strip(line|escape, '\r\n')}</span><a href="#{lineid}"></a>'
75 <span id="{lineid}">{strip(line|escape, '\r\n')}</span><a href="#{lineid}"></a>'
76 filelogentry = filelogentry.tmpl
76 filelogentry = filelogentry.tmpl
77
77
78 annotateline = '
78 annotateline = '
79 <tr id="{lineid}"{ifeq(node, originalnode, ' class="thisrev"')}>
79 <tr id="{lineid}"{ifeq(node, originalnode, ' class="thisrev"')}>
80 <td class="annotate">
80 <td class="annotate parity{blockparity}">
81 {if(blockhead,
81 {if(blockhead,
82 '<a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#l{targetline}">
82 '<a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#l{targetline}">
83 {rev}
83 {rev}
84 </a>')}
84 </a>')}
85 <div class="annotate-info">
85 <div class="annotate-info">
86 <div>
86 <div>
87 <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#l{targetline}">
87 <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#l{targetline}">
88 {node|short}</a>
88 {node|short}</a>
89 {desc|escape|firstline}
89 {desc|escape|firstline}
90 </div>
90 </div>
91 <div><em>{author|obfuscate}</em></div>
91 <div><em>{author|obfuscate}</em></div>
92 <div>parents: {parents%annotateparent}</div>
92 <div>parents: {parents%annotateparent}</div>
93 <a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a>
93 <a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a>
94 <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a>
94 <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">changeset</a>
95 </div>
95 </div>
96 </td>
96 </td>
97 <td class="source"><a href="#{lineid}">{linenumber}</a> {line|escape}</td>
97 <td class="source"><a href="#{lineid}">{linenumber}</a> {line|escape}</td>
98 </tr>'
98 </tr>'
99 annotateparent = '
99 annotateparent = '
100 <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{rev}</a>'
100 <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{rev}</a>'
101 diffblock = '<div class="bottomline inc-lineno"><pre class="sourcelines wrap">{lines}</pre></div>'
101 diffblock = '<div class="bottomline inc-lineno"><pre class="sourcelines wrap">{lines}</pre></div>'
102 difflineplus = '
102 difflineplus = '
103 <span id="{lineid}" class="plusline">{strip(line|escape, '\r\n')}</span><a href="#{lineid}"></a>'
103 <span id="{lineid}" class="plusline">{strip(line|escape, '\r\n')}</span><a href="#{lineid}"></a>'
104 difflineminus = '
104 difflineminus = '
105 <span id="{lineid}" class="minusline">{strip(line|escape, '\r\n')}</span><a href="#{lineid}"></a>'
105 <span id="{lineid}" class="minusline">{strip(line|escape, '\r\n')}</span><a href="#{lineid}"></a>'
106 difflineat = '
106 difflineat = '
107 <span id="{lineid}" class="atline">{strip(line|escape, '\r\n')}</span><a href="#{lineid}"></a>'
107 <span id="{lineid}" class="atline">{strip(line|escape, '\r\n')}</span><a href="#{lineid}"></a>'
108 diffline = '
108 diffline = '
109 <span id="{lineid}">{strip(line|escape, '\r\n')}</span><a href="#{lineid}"></a>'
109 <span id="{lineid}">{strip(line|escape, '\r\n')}</span><a href="#{lineid}"></a>'
110
110
111 comparisonblock ='
111 comparisonblock ='
112 <tbody class="block">
112 <tbody class="block">
113 {lines}
113 {lines}
114 </tbody>'
114 </tbody>'
115 comparisonline = '
115 comparisonline = '
116 <tr id="{lineid}">
116 <tr id="{lineid}">
117 <td class="source {type}"><a href="#{lineid}">{leftlinenumber}</a> {leftline|escape}</td>
117 <td class="source {type}"><a href="#{lineid}">{leftlinenumber}</a> {leftline|escape}</td>
118 <td class="source {type}"><a href="#{lineid}">{rightlinenumber}</a> {rightline|escape}</td>
118 <td class="source {type}"><a href="#{lineid}">{rightlinenumber}</a> {rightline|escape}</td>
119 </tr>'
119 </tr>'
120
120
121 changelogparent = '
121 changelogparent = '
122 <tr>
122 <tr>
123 <th class="parent">parent {rev}:</th>
123 <th class="parent">parent {rev}:</th>
124 <td class="parent"><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
124 <td class="parent"><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
125 </tr>'
125 </tr>'
126
126
127 changesetparent = '<a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a> '
127 changesetparent = '<a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a> '
128
128
129 changesetparentdiff = '
129 changesetparentdiff = '
130 {changesetparent}
130 {changesetparent}
131 {ifeq(node, basenode, '(current diff)', '({difffrom})')}'
131 {ifeq(node, basenode, '(current diff)', '({difffrom})')}'
132
132
133 difffrom = '<a href="{url|urlescape}rev/{node|short}:{originalnode|short}{sessionvars%urlparameter}">diff</a>'
133 difffrom = '<a href="{url|urlescape}rev/{node|short}:{originalnode|short}{sessionvars%urlparameter}">diff</a>'
134
134
135 filerevparent = '<a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{rename%filerename}{node|short}</a> '
135 filerevparent = '<a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{rename%filerename}{node|short}</a> '
136 filerevchild = '<a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a> '
136 filerevchild = '<a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a> '
137
137
138 filerename = '{file|escape}@'
138 filerename = '{file|escape}@'
139 filelogrename = '
139 filelogrename = '
140 <span class="base">
140 <span class="base">
141 base
141 base
142 <a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
142 <a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
143 {file|escape}@{node|short}
143 {file|escape}@{node|short}
144 </a>
144 </a>
145 </span>'
145 </span>'
146 fileannotateparent = '
146 fileannotateparent = '
147 <tr>
147 <tr>
148 <td class="metatag">parent:</td>
148 <td class="metatag">parent:</td>
149 <td>
149 <td>
150 <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
150 <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
151 {rename%filerename}{node|short}
151 {rename%filerename}{node|short}
152 </a>
152 </a>
153 </td>
153 </td>
154 </tr>'
154 </tr>'
155 changesetchild = ' <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>'
155 changesetchild = ' <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>'
156 changelogchild = '
156 changelogchild = '
157 <tr>
157 <tr>
158 <th class="child">child</th>
158 <th class="child">child</th>
159 <td class="child">
159 <td class="child">
160 <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">
160 <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">
161 {node|short}
161 {node|short}
162 </a>
162 </a>
163 </td>
163 </td>
164 </tr>'
164 </tr>'
165 fileannotatechild = '
165 fileannotatechild = '
166 <tr>
166 <tr>
167 <td class="metatag">child:</td>
167 <td class="metatag">child:</td>
168 <td>
168 <td>
169 <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
169 <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
170 {node|short}
170 {node|short}
171 </a>
171 </a>
172 </td>
172 </td>
173 </tr>'
173 </tr>'
174 tags = tags.tmpl
174 tags = tags.tmpl
175 tagentry = '
175 tagentry = '
176 <tr class="tagEntry">
176 <tr class="tagEntry">
177 <td>
177 <td>
178 <a href="{url|urlescape}rev/{tag|revescape}{sessionvars%urlparameter}">
178 <a href="{url|urlescape}rev/{tag|revescape}{sessionvars%urlparameter}">
179 {tag|escape}
179 {tag|escape}
180 </a>
180 </a>
181 </td>
181 </td>
182 <td class="node">
182 <td class="node">
183 <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">
183 <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">
184 {node|short}
184 {node|short}
185 </a>
185 </a>
186 </td>
186 </td>
187 </tr>'
187 </tr>'
188 bookmarks = bookmarks.tmpl
188 bookmarks = bookmarks.tmpl
189 bookmarkentry = '
189 bookmarkentry = '
190 <tr class="tagEntry">
190 <tr class="tagEntry">
191 <td>
191 <td>
192 <a href="{url|urlescape}rev/{bookmark|revescape}{sessionvars%urlparameter}">
192 <a href="{url|urlescape}rev/{bookmark|revescape}{sessionvars%urlparameter}">
193 {bookmark|escape}
193 {bookmark|escape}
194 </a>
194 </a>
195 </td>
195 </td>
196 <td class="node">
196 <td class="node">
197 <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">
197 <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">
198 {node|short}
198 {node|short}
199 </a>
199 </a>
200 </td>
200 </td>
201 </tr>'
201 </tr>'
202 branches = branches.tmpl
202 branches = branches.tmpl
203 branchentry = '
203 branchentry = '
204 <tr class="tagEntry">
204 <tr class="tagEntry">
205 <td>
205 <td>
206 <a href="{url|urlescape}shortlog/{branch|revescape}{sessionvars%urlparameter}" class="{status}">
206 <a href="{url|urlescape}shortlog/{branch|revescape}{sessionvars%urlparameter}" class="{status}">
207 {branch|escape}
207 {branch|escape}
208 </a>
208 </a>
209 </td>
209 </td>
210 <td class="node">
210 <td class="node">
211 <a href="{url|urlescape}shortlog/{node|short}{sessionvars%urlparameter}" class="{status}">
211 <a href="{url|urlescape}shortlog/{node|short}{sessionvars%urlparameter}" class="{status}">
212 {node|short}
212 {node|short}
213 </a>
213 </a>
214 </td>
214 </td>
215 </tr>'
215 </tr>'
216 changelogtag = '<span class="tag">{name|escape}</span> '
216 changelogtag = '<span class="tag">{name|escape}</span> '
217 changesettag = '<span class="tag">{tag|escape}</span> '
217 changesettag = '<span class="tag">{tag|escape}</span> '
218 changesetbookmark = '<span class="tag">{bookmark|escape}</span> '
218 changesetbookmark = '<span class="tag">{bookmark|escape}</span> '
219 changelogbranchhead = '<span class="branchhead">{name|escape}</span> '
219 changelogbranchhead = '<span class="branchhead">{name|escape}</span> '
220 changelogbranchname = '<span class="branchname">{name|escape}</span> '
220 changelogbranchname = '<span class="branchname">{name|escape}</span> '
221
221
222 filediffparent = '
222 filediffparent = '
223 <tr>
223 <tr>
224 <th class="parent">parent {rev}:</th>
224 <th class="parent">parent {rev}:</th>
225 <td class="parent"><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
225 <td class="parent"><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
226 </tr>'
226 </tr>'
227 filelogparent = '
227 filelogparent = '
228 <tr>
228 <tr>
229 <th>parent {rev}:</th>
229 <th>parent {rev}:</th>
230 <td><a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
230 <td><a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
231 </tr>'
231 </tr>'
232 filediffchild = '
232 filediffchild = '
233 <tr>
233 <tr>
234 <th class="child">child {rev}:</th>
234 <th class="child">child {rev}:</th>
235 <td class="child"><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>
235 <td class="child"><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>
236 </td>
236 </td>
237 </tr>'
237 </tr>'
238 filelogchild = '
238 filelogchild = '
239 <tr>
239 <tr>
240 <th>child {rev}:</th>
240 <th>child {rev}:</th>
241 <td><a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
241 <td><a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td>
242 </tr>'
242 </tr>'
243
243
244 indexentry = '
244 indexentry = '
245 <tr>
245 <tr>
246 <td><a href="{url|urlescape}{sessionvars%urlparameter}">{name|escape}</a></td>
246 <td><a href="{url|urlescape}{sessionvars%urlparameter}">{name|escape}</a></td>
247 <td>{description}</td>
247 <td>{description}</td>
248 <td>{contact|obfuscate}</td>
248 <td>{contact|obfuscate}</td>
249 <td class="age">{lastchange|rfc822date}</td>
249 <td class="age">{lastchange|rfc822date}</td>
250 <td class="indexlinks">{archives%indexarchiveentry}</td>
250 <td class="indexlinks">{archives%indexarchiveentry}</td>
251 <td>
251 <td>
252 {if(isdirectory, '',
252 {if(isdirectory, '',
253 '<a href="{url|urlescape}atom-log" title="subscribe to repository atom feed">
253 '<a href="{url|urlescape}atom-log" title="subscribe to repository atom feed">
254 <img class="atom-logo" src="{staticurl|urlescape}feed-icon-14x14.png" alt="subscribe to repository atom feed">
254 <img class="atom-logo" src="{staticurl|urlescape}feed-icon-14x14.png" alt="subscribe to repository atom feed">
255 </a>'
255 </a>'
256 )}
256 )}
257 </td>
257 </td>
258 </tr>\n'
258 </tr>\n'
259 indexarchiveentry = '<a href="{url|urlescape}archive/{node|short}{extension|urlescape}">&nbsp;&darr;{type|escape}</a>'
259 indexarchiveentry = '<a href="{url|urlescape}archive/{node|short}{extension|urlescape}">&nbsp;&darr;{type|escape}</a>'
260 index = index.tmpl
260 index = index.tmpl
261 archiveentry = '
261 archiveentry = '
262 <li>
262 <li>
263 <a href="{url|urlescape}archive/{symrev}{extension|urlescape}{ifeq(path,'/','',path|urlescape)}">{type|escape}</a>
263 <a href="{url|urlescape}archive/{symrev}{extension|urlescape}{ifeq(path,'/','',path|urlescape)}">{type|escape}</a>
264 </li>'
264 </li>'
265 notfound = notfound.tmpl
265 notfound = notfound.tmpl
266 error = error.tmpl
266 error = error.tmpl
267 urlparameter = '{separator}{name}={value|urlescape}'
267 urlparameter = '{separator}{name}={value|urlescape}'
268 hiddenformentry = '<input type="hidden" name="{name}" value="{value|escape}" />'
268 hiddenformentry = '<input type="hidden" name="{name}" value="{value|escape}" />'
269 breadcrumb = '&gt; <a href="{url|urlescape}">{name|escape}</a> '
269 breadcrumb = '&gt; <a href="{url|urlescape}">{name|escape}</a> '
270
270
271 searchhint = 'Find changesets by keywords (author, files, the commit message), revision
271 searchhint = 'Find changesets by keywords (author, files, the commit message), revision
272 number or hash, or <a href="{url|urlescape}help/revsets">revset expression</a>.'
272 number or hash, or <a href="{url|urlescape}help/revsets">revset expression</a>.'
@@ -1,1022 +1,1022 b''
1 #require pygments serve
1 #require pygments serve
2
2
3 $ cat <<EOF >> $HGRCPATH
3 $ cat <<EOF >> $HGRCPATH
4 > [extensions]
4 > [extensions]
5 > highlight =
5 > highlight =
6 > [web]
6 > [web]
7 > pygments_style = friendly
7 > pygments_style = friendly
8 > highlightfiles = **.py and size('<100KB')
8 > highlightfiles = **.py and size('<100KB')
9 > EOF
9 > EOF
10 $ hg init test
10 $ hg init test
11 $ cd test
11 $ cd test
12
12
13 $ filterhtml () {
13 $ filterhtml () {
14 > sed -e "s/class=\"k\"/class=\"kn\"/g" \
14 > sed -e "s/class=\"k\"/class=\"kn\"/g" \
15 > -e "s/class=\"mf\"/class=\"mi\"/g" \
15 > -e "s/class=\"mf\"/class=\"mi\"/g" \
16 > -e "s/class=\"\([cs]\)[h12]\"/class=\"\1\"/g"
16 > -e "s/class=\"\([cs]\)[h12]\"/class=\"\1\"/g"
17 > }
17 > }
18
18
19 create random Python file to exercise Pygments
19 create random Python file to exercise Pygments
20
20
21 $ cat <<EOF > primes.py
21 $ cat <<EOF > primes.py
22 > #!/usr/bin/env python
22 > #!/usr/bin/env python
23 >
23 >
24 > """Fun with generators. Corresponding Haskell implementation:
24 > """Fun with generators. Corresponding Haskell implementation:
25 >
25 >
26 > primes = 2 : sieve [3, 5..]
26 > primes = 2 : sieve [3, 5..]
27 > where sieve (p:ns) = p : sieve [n | n <- ns, mod n p /= 0]
27 > where sieve (p:ns) = p : sieve [n | n <- ns, mod n p /= 0]
28 > """
28 > """
29 >
29 >
30 > from itertools import dropwhile, ifilter, islice, count, chain
30 > from itertools import dropwhile, ifilter, islice, count, chain
31 >
31 >
32 > def primes():
32 > def primes():
33 > """Generate all primes."""
33 > """Generate all primes."""
34 > def sieve(ns):
34 > def sieve(ns):
35 > p = ns.next()
35 > p = ns.next()
36 > # It is important to yield *here* in order to stop the
36 > # It is important to yield *here* in order to stop the
37 > # infinite recursion.
37 > # infinite recursion.
38 > yield p
38 > yield p
39 > ns = ifilter(lambda n: n % p != 0, ns)
39 > ns = ifilter(lambda n: n % p != 0, ns)
40 > for n in sieve(ns):
40 > for n in sieve(ns):
41 > yield n
41 > yield n
42 >
42 >
43 > odds = ifilter(lambda i: i % 2 == 1, count())
43 > odds = ifilter(lambda i: i % 2 == 1, count())
44 > return chain([2], sieve(dropwhile(lambda n: n < 3, odds)))
44 > return chain([2], sieve(dropwhile(lambda n: n < 3, odds)))
45 >
45 >
46 > if __name__ == "__main__":
46 > if __name__ == "__main__":
47 > import sys
47 > import sys
48 > try:
48 > try:
49 > n = int(sys.argv[1])
49 > n = int(sys.argv[1])
50 > except (ValueError, IndexError):
50 > except (ValueError, IndexError):
51 > n = 10
51 > n = 10
52 > p = primes()
52 > p = primes()
53 > print "The first %d primes: %s" % (n, list(islice(p, n)))
53 > print "The first %d primes: %s" % (n, list(islice(p, n)))
54 > EOF
54 > EOF
55 $ echo >> primes.py # to test html markup with an empty line just before EOF
55 $ echo >> primes.py # to test html markup with an empty line just before EOF
56 $ hg ci -Ama
56 $ hg ci -Ama
57 adding primes.py
57 adding primes.py
58
58
59 hg serve
59 hg serve
60
60
61 $ hg serve -p $HGPORT -d -n test --pid-file=hg.pid -A access.log -E errors.log
61 $ hg serve -p $HGPORT -d -n test --pid-file=hg.pid -A access.log -E errors.log
62 $ cat hg.pid >> $DAEMON_PIDS
62 $ cat hg.pid >> $DAEMON_PIDS
63
63
64 hgweb filerevision, html
64 hgweb filerevision, html
65
65
66 $ (get-with-headers.py localhost:$HGPORT 'file/tip/primes.py') | filterhtml
66 $ (get-with-headers.py localhost:$HGPORT 'file/tip/primes.py') | filterhtml
67 200 Script output follows
67 200 Script output follows
68
68
69 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
69 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
70 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
70 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
71 <head>
71 <head>
72 <link rel="icon" href="/static/hgicon.png" type="image/png" />
72 <link rel="icon" href="/static/hgicon.png" type="image/png" />
73 <meta name="robots" content="index, nofollow" />
73 <meta name="robots" content="index, nofollow" />
74 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
74 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
75 <script type="text/javascript" src="/static/mercurial.js"></script>
75 <script type="text/javascript" src="/static/mercurial.js"></script>
76
76
77 <link rel="stylesheet" href="/highlightcss" type="text/css" />
77 <link rel="stylesheet" href="/highlightcss" type="text/css" />
78 <title>test: 06824edf55d0 primes.py</title>
78 <title>test: 06824edf55d0 primes.py</title>
79 </head>
79 </head>
80 <body>
80 <body>
81
81
82 <div class="container">
82 <div class="container">
83 <div class="menu">
83 <div class="menu">
84 <div class="logo">
84 <div class="logo">
85 <a href="https://mercurial-scm.org/">
85 <a href="https://mercurial-scm.org/">
86 <img src="/static/hglogo.png" alt="mercurial" /></a>
86 <img src="/static/hglogo.png" alt="mercurial" /></a>
87 </div>
87 </div>
88 <ul>
88 <ul>
89 <li><a href="/shortlog/tip">log</a></li>
89 <li><a href="/shortlog/tip">log</a></li>
90 <li><a href="/graph/tip">graph</a></li>
90 <li><a href="/graph/tip">graph</a></li>
91 <li><a href="/tags">tags</a></li>
91 <li><a href="/tags">tags</a></li>
92 <li><a href="/bookmarks">bookmarks</a></li>
92 <li><a href="/bookmarks">bookmarks</a></li>
93 <li><a href="/branches">branches</a></li>
93 <li><a href="/branches">branches</a></li>
94 </ul>
94 </ul>
95 <ul>
95 <ul>
96 <li><a href="/rev/tip">changeset</a></li>
96 <li><a href="/rev/tip">changeset</a></li>
97 <li><a href="/file/tip/">browse</a></li>
97 <li><a href="/file/tip/">browse</a></li>
98 </ul>
98 </ul>
99 <ul>
99 <ul>
100 <li class="active">file</li>
100 <li class="active">file</li>
101 <li><a href="/file/tip/primes.py">latest</a></li>
101 <li><a href="/file/tip/primes.py">latest</a></li>
102 <li><a href="/diff/tip/primes.py">diff</a></li>
102 <li><a href="/diff/tip/primes.py">diff</a></li>
103 <li><a href="/comparison/tip/primes.py">comparison</a></li>
103 <li><a href="/comparison/tip/primes.py">comparison</a></li>
104 <li><a href="/annotate/tip/primes.py">annotate</a></li>
104 <li><a href="/annotate/tip/primes.py">annotate</a></li>
105 <li><a href="/log/tip/primes.py">file log</a></li>
105 <li><a href="/log/tip/primes.py">file log</a></li>
106 <li><a href="/raw-file/tip/primes.py">raw</a></li>
106 <li><a href="/raw-file/tip/primes.py">raw</a></li>
107 </ul>
107 </ul>
108 <ul>
108 <ul>
109 <li><a href="/help">help</a></li>
109 <li><a href="/help">help</a></li>
110 </ul>
110 </ul>
111 </div>
111 </div>
112
112
113 <div class="main">
113 <div class="main">
114 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
114 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
115 <h3>
115 <h3>
116 view primes.py @ 0:<a href="/rev/06824edf55d0">06824edf55d0</a>
116 view primes.py @ 0:<a href="/rev/06824edf55d0">06824edf55d0</a>
117 <span class="tag">tip</span>
117 <span class="tag">tip</span>
118 </h3>
118 </h3>
119
119
120 <form class="search" action="/log">
120 <form class="search" action="/log">
121
121
122 <p><input name="rev" id="search1" type="text" size="30" /></p>
122 <p><input name="rev" id="search1" type="text" size="30" /></p>
123 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
123 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
124 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
124 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
125 </form>
125 </form>
126
126
127 <div class="description">a</div>
127 <div class="description">a</div>
128
128
129 <table id="changesetEntry">
129 <table id="changesetEntry">
130 <tr>
130 <tr>
131 <th class="author">author</th>
131 <th class="author">author</th>
132 <td class="author">&#116;&#101;&#115;&#116;</td>
132 <td class="author">&#116;&#101;&#115;&#116;</td>
133 </tr>
133 </tr>
134 <tr>
134 <tr>
135 <th class="date">date</th>
135 <th class="date">date</th>
136 <td class="date age">Thu, 01 Jan 1970 00:00:00 +0000</td>
136 <td class="date age">Thu, 01 Jan 1970 00:00:00 +0000</td>
137 </tr>
137 </tr>
138 <tr>
138 <tr>
139 <th class="author">parents</th>
139 <th class="author">parents</th>
140 <td class="author"></td>
140 <td class="author"></td>
141 </tr>
141 </tr>
142 <tr>
142 <tr>
143 <th class="author">children</th>
143 <th class="author">children</th>
144 <td class="author"></td>
144 <td class="author"></td>
145 </tr>
145 </tr>
146 </table>
146 </table>
147
147
148 <div class="overflow">
148 <div class="overflow">
149 <div class="sourcefirst linewraptoggle">line wrap: <a class="linewraplink" href="javascript:toggleLinewrap()">on</a></div>
149 <div class="sourcefirst linewraptoggle">line wrap: <a class="linewraplink" href="javascript:toggleLinewrap()">on</a></div>
150 <div class="sourcefirst"> line source</div>
150 <div class="sourcefirst"> line source</div>
151 <pre class="sourcelines stripes4 wrap bottomline">
151 <pre class="sourcelines stripes4 wrap bottomline">
152 <span id="l1"><span class="c">#!/usr/bin/env python</span></span><a href="#l1"></a>
152 <span id="l1"><span class="c">#!/usr/bin/env python</span></span><a href="#l1"></a>
153 <span id="l2"></span><a href="#l2"></a>
153 <span id="l2"></span><a href="#l2"></a>
154 <span id="l3"><span class="sd">&quot;&quot;&quot;Fun with generators. Corresponding Haskell implementation:</span></span><a href="#l3"></a>
154 <span id="l3"><span class="sd">&quot;&quot;&quot;Fun with generators. Corresponding Haskell implementation:</span></span><a href="#l3"></a>
155 <span id="l4"></span><a href="#l4"></a>
155 <span id="l4"></span><a href="#l4"></a>
156 <span id="l5"><span class="sd">primes = 2 : sieve [3, 5..]</span></span><a href="#l5"></a>
156 <span id="l5"><span class="sd">primes = 2 : sieve [3, 5..]</span></span><a href="#l5"></a>
157 <span id="l6"><span class="sd"> where sieve (p:ns) = p : sieve [n | n &lt;- ns, mod n p /= 0]</span></span><a href="#l6"></a>
157 <span id="l6"><span class="sd"> where sieve (p:ns) = p : sieve [n | n &lt;- ns, mod n p /= 0]</span></span><a href="#l6"></a>
158 <span id="l7"><span class="sd">&quot;&quot;&quot;</span></span><a href="#l7"></a>
158 <span id="l7"><span class="sd">&quot;&quot;&quot;</span></span><a href="#l7"></a>
159 <span id="l8"></span><a href="#l8"></a>
159 <span id="l8"></span><a href="#l8"></a>
160 <span id="l9"><span class="kn">from</span> <span class="nn">itertools</span> <span class="kn">import</span> <span class="n">dropwhile</span><span class="p">,</span> <span class="n">ifilter</span><span class="p">,</span> <span class="n">islice</span><span class="p">,</span> <span class="n">count</span><span class="p">,</span> <span class="n">chain</span></span><a href="#l9"></a>
160 <span id="l9"><span class="kn">from</span> <span class="nn">itertools</span> <span class="kn">import</span> <span class="n">dropwhile</span><span class="p">,</span> <span class="n">ifilter</span><span class="p">,</span> <span class="n">islice</span><span class="p">,</span> <span class="n">count</span><span class="p">,</span> <span class="n">chain</span></span><a href="#l9"></a>
161 <span id="l10"></span><a href="#l10"></a>
161 <span id="l10"></span><a href="#l10"></a>
162 <span id="l11"><span class="kn">def</span> <span class="nf">primes</span><span class="p">():</span></span><a href="#l11"></a>
162 <span id="l11"><span class="kn">def</span> <span class="nf">primes</span><span class="p">():</span></span><a href="#l11"></a>
163 <span id="l12"> <span class="sd">&quot;&quot;&quot;Generate all primes.&quot;&quot;&quot;</span></span><a href="#l12"></a>
163 <span id="l12"> <span class="sd">&quot;&quot;&quot;Generate all primes.&quot;&quot;&quot;</span></span><a href="#l12"></a>
164 <span id="l13"> <span class="kn">def</span> <span class="nf">sieve</span><span class="p">(</span><span class="n">ns</span><span class="p">):</span></span><a href="#l13"></a>
164 <span id="l13"> <span class="kn">def</span> <span class="nf">sieve</span><span class="p">(</span><span class="n">ns</span><span class="p">):</span></span><a href="#l13"></a>
165 <span id="l14"> <span class="n">p</span> <span class="o">=</span> <span class="n">ns</span><span class="o">.</span><span class="n">next</span><span class="p">()</span></span><a href="#l14"></a>
165 <span id="l14"> <span class="n">p</span> <span class="o">=</span> <span class="n">ns</span><span class="o">.</span><span class="n">next</span><span class="p">()</span></span><a href="#l14"></a>
166 <span id="l15"> <span class="c"># It is important to yield *here* in order to stop the</span></span><a href="#l15"></a>
166 <span id="l15"> <span class="c"># It is important to yield *here* in order to stop the</span></span><a href="#l15"></a>
167 <span id="l16"> <span class="c"># infinite recursion.</span></span><a href="#l16"></a>
167 <span id="l16"> <span class="c"># infinite recursion.</span></span><a href="#l16"></a>
168 <span id="l17"> <span class="kn">yield</span> <span class="n">p</span></span><a href="#l17"></a>
168 <span id="l17"> <span class="kn">yield</span> <span class="n">p</span></span><a href="#l17"></a>
169 <span id="l18"> <span class="n">ns</span> <span class="o">=</span> <span class="n">ifilter</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">n</span><span class="p">:</span> <span class="n">n</span> <span class="o">%</span> <span class="n">p</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">ns</span><span class="p">)</span></span><a href="#l18"></a>
169 <span id="l18"> <span class="n">ns</span> <span class="o">=</span> <span class="n">ifilter</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">n</span><span class="p">:</span> <span class="n">n</span> <span class="o">%</span> <span class="n">p</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">ns</span><span class="p">)</span></span><a href="#l18"></a>
170 <span id="l19"> <span class="kn">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="n">sieve</span><span class="p">(</span><span class="n">ns</span><span class="p">):</span></span><a href="#l19"></a>
170 <span id="l19"> <span class="kn">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="n">sieve</span><span class="p">(</span><span class="n">ns</span><span class="p">):</span></span><a href="#l19"></a>
171 <span id="l20"> <span class="kn">yield</span> <span class="n">n</span></span><a href="#l20"></a>
171 <span id="l20"> <span class="kn">yield</span> <span class="n">n</span></span><a href="#l20"></a>
172 <span id="l21"></span><a href="#l21"></a>
172 <span id="l21"></span><a href="#l21"></a>
173 <span id="l22"> <span class="n">odds</span> <span class="o">=</span> <span class="n">ifilter</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">i</span><span class="p">:</span> <span class="n">i</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">1</span><span class="p">,</span> <span class="n">count</span><span class="p">())</span></span><a href="#l22"></a>
173 <span id="l22"> <span class="n">odds</span> <span class="o">=</span> <span class="n">ifilter</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">i</span><span class="p">:</span> <span class="n">i</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">1</span><span class="p">,</span> <span class="n">count</span><span class="p">())</span></span><a href="#l22"></a>
174 <span id="l23"> <span class="kn">return</span> <span class="n">chain</span><span class="p">([</span><span class="mi">2</span><span class="p">],</span> <span class="n">sieve</span><span class="p">(</span><span class="n">dropwhile</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">n</span><span class="p">:</span> <span class="n">n</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">,</span> <span class="n">odds</span><span class="p">)))</span></span><a href="#l23"></a>
174 <span id="l23"> <span class="kn">return</span> <span class="n">chain</span><span class="p">([</span><span class="mi">2</span><span class="p">],</span> <span class="n">sieve</span><span class="p">(</span><span class="n">dropwhile</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">n</span><span class="p">:</span> <span class="n">n</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">,</span> <span class="n">odds</span><span class="p">)))</span></span><a href="#l23"></a>
175 <span id="l24"></span><a href="#l24"></a>
175 <span id="l24"></span><a href="#l24"></a>
176 <span id="l25"><span class="kn">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">&quot;__main__&quot;</span><span class="p">:</span></span><a href="#l25"></a>
176 <span id="l25"><span class="kn">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">&quot;__main__&quot;</span><span class="p">:</span></span><a href="#l25"></a>
177 <span id="l26"> <span class="kn">import</span> <span class="nn">sys</span></span><a href="#l26"></a>
177 <span id="l26"> <span class="kn">import</span> <span class="nn">sys</span></span><a href="#l26"></a>
178 <span id="l27"> <span class="kn">try</span><span class="p">:</span></span><a href="#l27"></a>
178 <span id="l27"> <span class="kn">try</span><span class="p">:</span></span><a href="#l27"></a>
179 <span id="l28"> <span class="n">n</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span></span><a href="#l28"></a>
179 <span id="l28"> <span class="n">n</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span></span><a href="#l28"></a>
180 <span id="l29"> <span class="kn">except</span> <span class="p">(</span><span class="ne">ValueError</span><span class="p">,</span> <span class="ne">IndexError</span><span class="p">):</span></span><a href="#l29"></a>
180 <span id="l29"> <span class="kn">except</span> <span class="p">(</span><span class="ne">ValueError</span><span class="p">,</span> <span class="ne">IndexError</span><span class="p">):</span></span><a href="#l29"></a>
181 <span id="l30"> <span class="n">n</span> <span class="o">=</span> <span class="mi">10</span></span><a href="#l30"></a>
181 <span id="l30"> <span class="n">n</span> <span class="o">=</span> <span class="mi">10</span></span><a href="#l30"></a>
182 <span id="l31"> <span class="n">p</span> <span class="o">=</span> <span class="n">primes</span><span class="p">()</span></span><a href="#l31"></a>
182 <span id="l31"> <span class="n">p</span> <span class="o">=</span> <span class="n">primes</span><span class="p">()</span></span><a href="#l31"></a>
183 <span id="l32"> <span class="kn">print</span> <span class="s">&quot;The first </span><span class="si">%d</span><span class="s"> primes: </span><span class="si">%s</span><span class="s">&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="nb">list</span><span class="p">(</span><span class="n">islice</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">n</span><span class="p">)))</span></span><a href="#l32"></a>
183 <span id="l32"> <span class="kn">print</span> <span class="s">&quot;The first </span><span class="si">%d</span><span class="s"> primes: </span><span class="si">%s</span><span class="s">&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="nb">list</span><span class="p">(</span><span class="n">islice</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">n</span><span class="p">)))</span></span><a href="#l32"></a>
184 <span id="l33"></span><a href="#l33"></a></pre>
184 <span id="l33"></span><a href="#l33"></a></pre>
185 </div>
185 </div>
186 </div>
186 </div>
187 </div>
187 </div>
188
188
189 <script type="text/javascript">process_dates()</script>
189 <script type="text/javascript">process_dates()</script>
190
190
191
191
192 </body>
192 </body>
193 </html>
193 </html>
194
194
195
195
196 hgweb fileannotate, html
196 hgweb fileannotate, html
197
197
198 $ (get-with-headers.py localhost:$HGPORT 'annotate/tip/primes.py') | filterhtml
198 $ (get-with-headers.py localhost:$HGPORT 'annotate/tip/primes.py') | filterhtml
199 200 Script output follows
199 200 Script output follows
200
200
201 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
201 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
202 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
202 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
203 <head>
203 <head>
204 <link rel="icon" href="/static/hgicon.png" type="image/png" />
204 <link rel="icon" href="/static/hgicon.png" type="image/png" />
205 <meta name="robots" content="index, nofollow" />
205 <meta name="robots" content="index, nofollow" />
206 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
206 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
207 <script type="text/javascript" src="/static/mercurial.js"></script>
207 <script type="text/javascript" src="/static/mercurial.js"></script>
208
208
209 <link rel="stylesheet" href="/highlightcss" type="text/css" />
209 <link rel="stylesheet" href="/highlightcss" type="text/css" />
210 <title>test: primes.py annotate</title>
210 <title>test: primes.py annotate</title>
211 </head>
211 </head>
212 <body>
212 <body>
213
213
214 <div class="container">
214 <div class="container">
215 <div class="menu">
215 <div class="menu">
216 <div class="logo">
216 <div class="logo">
217 <a href="https://mercurial-scm.org/">
217 <a href="https://mercurial-scm.org/">
218 <img src="/static/hglogo.png" alt="mercurial" /></a>
218 <img src="/static/hglogo.png" alt="mercurial" /></a>
219 </div>
219 </div>
220 <ul>
220 <ul>
221 <li><a href="/shortlog/tip">log</a></li>
221 <li><a href="/shortlog/tip">log</a></li>
222 <li><a href="/graph/tip">graph</a></li>
222 <li><a href="/graph/tip">graph</a></li>
223 <li><a href="/tags">tags</a></li>
223 <li><a href="/tags">tags</a></li>
224 <li><a href="/bookmarks">bookmarks</a></li>
224 <li><a href="/bookmarks">bookmarks</a></li>
225 <li><a href="/branches">branches</a></li>
225 <li><a href="/branches">branches</a></li>
226 </ul>
226 </ul>
227
227
228 <ul>
228 <ul>
229 <li><a href="/rev/tip">changeset</a></li>
229 <li><a href="/rev/tip">changeset</a></li>
230 <li><a href="/file/tip/">browse</a></li>
230 <li><a href="/file/tip/">browse</a></li>
231 </ul>
231 </ul>
232 <ul>
232 <ul>
233 <li><a href="/file/tip/primes.py">file</a></li>
233 <li><a href="/file/tip/primes.py">file</a></li>
234 <li><a href="/file/tip/primes.py">latest</a></li>
234 <li><a href="/file/tip/primes.py">latest</a></li>
235 <li><a href="/diff/tip/primes.py">diff</a></li>
235 <li><a href="/diff/tip/primes.py">diff</a></li>
236 <li><a href="/comparison/tip/primes.py">comparison</a></li>
236 <li><a href="/comparison/tip/primes.py">comparison</a></li>
237 <li class="active">annotate</li>
237 <li class="active">annotate</li>
238 <li><a href="/log/tip/primes.py">file log</a></li>
238 <li><a href="/log/tip/primes.py">file log</a></li>
239 <li><a href="/raw-annotate/tip/primes.py">raw</a></li>
239 <li><a href="/raw-annotate/tip/primes.py">raw</a></li>
240 </ul>
240 </ul>
241 <ul>
241 <ul>
242 <li><a href="/help">help</a></li>
242 <li><a href="/help">help</a></li>
243 </ul>
243 </ul>
244 </div>
244 </div>
245
245
246 <div class="main">
246 <div class="main">
247 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
247 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
248 <h3>
248 <h3>
249 annotate primes.py @ 0:<a href="/rev/06824edf55d0">06824edf55d0</a>
249 annotate primes.py @ 0:<a href="/rev/06824edf55d0">06824edf55d0</a>
250 <span class="tag">tip</span>
250 <span class="tag">tip</span>
251 </h3>
251 </h3>
252
252
253 <form class="search" action="/log">
253 <form class="search" action="/log">
254
254
255 <p><input name="rev" id="search1" type="text" size="30" /></p>
255 <p><input name="rev" id="search1" type="text" size="30" /></p>
256 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
256 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
257 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
257 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
258 </form>
258 </form>
259
259
260 <div class="description">a</div>
260 <div class="description">a</div>
261
261
262 <table id="changesetEntry">
262 <table id="changesetEntry">
263 <tr>
263 <tr>
264 <th class="author">author</th>
264 <th class="author">author</th>
265 <td class="author">&#116;&#101;&#115;&#116;</td>
265 <td class="author">&#116;&#101;&#115;&#116;</td>
266 </tr>
266 </tr>
267 <tr>
267 <tr>
268 <th class="date">date</th>
268 <th class="date">date</th>
269 <td class="date age">Thu, 01 Jan 1970 00:00:00 +0000</td>
269 <td class="date age">Thu, 01 Jan 1970 00:00:00 +0000</td>
270 </tr>
270 </tr>
271 <tr>
271 <tr>
272 <th class="author">parents</th>
272 <th class="author">parents</th>
273 <td class="author"></td>
273 <td class="author"></td>
274 </tr>
274 </tr>
275 <tr>
275 <tr>
276 <th class="author">children</th>
276 <th class="author">children</th>
277 <td class="author"></td>
277 <td class="author"></td>
278 </tr>
278 </tr>
279 </table>
279 </table>
280
280
281 <div class="overflow">
281 <div class="overflow">
282 <table class="bigtable">
282 <table class="bigtable">
283 <thead>
283 <thead>
284 <tr>
284 <tr>
285 <th class="annotate">rev</th>
285 <th class="annotate">rev</th>
286 <th class="line">&nbsp;&nbsp;line source</th>
286 <th class="line">&nbsp;&nbsp;line source</th>
287 </tr>
287 </tr>
288 </thead>
288 </thead>
289 <tbody class="stripes2">
289 <tbody class="stripes2">
290
290
291 <tr id="l1" class="thisrev">
291 <tr id="l1" class="thisrev">
292 <td class="annotate">
292 <td class="annotate parity0">
293 <a href="/annotate/06824edf55d0/primes.py#l1">
293 <a href="/annotate/06824edf55d0/primes.py#l1">
294 0
294 0
295 </a>
295 </a>
296 <div class="annotate-info">
296 <div class="annotate-info">
297 <div>
297 <div>
298 <a href="/annotate/06824edf55d0/primes.py#l1">
298 <a href="/annotate/06824edf55d0/primes.py#l1">
299 06824edf55d0</a>
299 06824edf55d0</a>
300 a
300 a
301 </div>
301 </div>
302 <div><em>&#116;&#101;&#115;&#116;</em></div>
302 <div><em>&#116;&#101;&#115;&#116;</em></div>
303 <div>parents: </div>
303 <div>parents: </div>
304 <a href="/diff/06824edf55d0/primes.py">diff</a>
304 <a href="/diff/06824edf55d0/primes.py">diff</a>
305 <a href="/rev/06824edf55d0">changeset</a>
305 <a href="/rev/06824edf55d0">changeset</a>
306 </div>
306 </div>
307 </td>
307 </td>
308 <td class="source"><a href="#l1"> 1</a> <span class="c">#!/usr/bin/env python</span></td>
308 <td class="source"><a href="#l1"> 1</a> <span class="c">#!/usr/bin/env python</span></td>
309 </tr>
309 </tr>
310 <tr id="l2" class="thisrev">
310 <tr id="l2" class="thisrev">
311 <td class="annotate">
311 <td class="annotate parity0">
312
312
313 <div class="annotate-info">
313 <div class="annotate-info">
314 <div>
314 <div>
315 <a href="/annotate/06824edf55d0/primes.py#l2">
315 <a href="/annotate/06824edf55d0/primes.py#l2">
316 06824edf55d0</a>
316 06824edf55d0</a>
317 a
317 a
318 </div>
318 </div>
319 <div><em>&#116;&#101;&#115;&#116;</em></div>
319 <div><em>&#116;&#101;&#115;&#116;</em></div>
320 <div>parents: </div>
320 <div>parents: </div>
321 <a href="/diff/06824edf55d0/primes.py">diff</a>
321 <a href="/diff/06824edf55d0/primes.py">diff</a>
322 <a href="/rev/06824edf55d0">changeset</a>
322 <a href="/rev/06824edf55d0">changeset</a>
323 </div>
323 </div>
324 </td>
324 </td>
325 <td class="source"><a href="#l2"> 2</a> </td>
325 <td class="source"><a href="#l2"> 2</a> </td>
326 </tr>
326 </tr>
327 <tr id="l3" class="thisrev">
327 <tr id="l3" class="thisrev">
328 <td class="annotate">
328 <td class="annotate parity0">
329
329
330 <div class="annotate-info">
330 <div class="annotate-info">
331 <div>
331 <div>
332 <a href="/annotate/06824edf55d0/primes.py#l3">
332 <a href="/annotate/06824edf55d0/primes.py#l3">
333 06824edf55d0</a>
333 06824edf55d0</a>
334 a
334 a
335 </div>
335 </div>
336 <div><em>&#116;&#101;&#115;&#116;</em></div>
336 <div><em>&#116;&#101;&#115;&#116;</em></div>
337 <div>parents: </div>
337 <div>parents: </div>
338 <a href="/diff/06824edf55d0/primes.py">diff</a>
338 <a href="/diff/06824edf55d0/primes.py">diff</a>
339 <a href="/rev/06824edf55d0">changeset</a>
339 <a href="/rev/06824edf55d0">changeset</a>
340 </div>
340 </div>
341 </td>
341 </td>
342 <td class="source"><a href="#l3"> 3</a> <span class="sd">&quot;&quot;&quot;Fun with generators. Corresponding Haskell implementation:</span></td>
342 <td class="source"><a href="#l3"> 3</a> <span class="sd">&quot;&quot;&quot;Fun with generators. Corresponding Haskell implementation:</span></td>
343 </tr>
343 </tr>
344 <tr id="l4" class="thisrev">
344 <tr id="l4" class="thisrev">
345 <td class="annotate">
345 <td class="annotate parity0">
346
346
347 <div class="annotate-info">
347 <div class="annotate-info">
348 <div>
348 <div>
349 <a href="/annotate/06824edf55d0/primes.py#l4">
349 <a href="/annotate/06824edf55d0/primes.py#l4">
350 06824edf55d0</a>
350 06824edf55d0</a>
351 a
351 a
352 </div>
352 </div>
353 <div><em>&#116;&#101;&#115;&#116;</em></div>
353 <div><em>&#116;&#101;&#115;&#116;</em></div>
354 <div>parents: </div>
354 <div>parents: </div>
355 <a href="/diff/06824edf55d0/primes.py">diff</a>
355 <a href="/diff/06824edf55d0/primes.py">diff</a>
356 <a href="/rev/06824edf55d0">changeset</a>
356 <a href="/rev/06824edf55d0">changeset</a>
357 </div>
357 </div>
358 </td>
358 </td>
359 <td class="source"><a href="#l4"> 4</a> </td>
359 <td class="source"><a href="#l4"> 4</a> </td>
360 </tr>
360 </tr>
361 <tr id="l5" class="thisrev">
361 <tr id="l5" class="thisrev">
362 <td class="annotate">
362 <td class="annotate parity0">
363
363
364 <div class="annotate-info">
364 <div class="annotate-info">
365 <div>
365 <div>
366 <a href="/annotate/06824edf55d0/primes.py#l5">
366 <a href="/annotate/06824edf55d0/primes.py#l5">
367 06824edf55d0</a>
367 06824edf55d0</a>
368 a
368 a
369 </div>
369 </div>
370 <div><em>&#116;&#101;&#115;&#116;</em></div>
370 <div><em>&#116;&#101;&#115;&#116;</em></div>
371 <div>parents: </div>
371 <div>parents: </div>
372 <a href="/diff/06824edf55d0/primes.py">diff</a>
372 <a href="/diff/06824edf55d0/primes.py">diff</a>
373 <a href="/rev/06824edf55d0">changeset</a>
373 <a href="/rev/06824edf55d0">changeset</a>
374 </div>
374 </div>
375 </td>
375 </td>
376 <td class="source"><a href="#l5"> 5</a> <span class="sd">primes = 2 : sieve [3, 5..]</span></td>
376 <td class="source"><a href="#l5"> 5</a> <span class="sd">primes = 2 : sieve [3, 5..]</span></td>
377 </tr>
377 </tr>
378 <tr id="l6" class="thisrev">
378 <tr id="l6" class="thisrev">
379 <td class="annotate">
379 <td class="annotate parity0">
380
380
381 <div class="annotate-info">
381 <div class="annotate-info">
382 <div>
382 <div>
383 <a href="/annotate/06824edf55d0/primes.py#l6">
383 <a href="/annotate/06824edf55d0/primes.py#l6">
384 06824edf55d0</a>
384 06824edf55d0</a>
385 a
385 a
386 </div>
386 </div>
387 <div><em>&#116;&#101;&#115;&#116;</em></div>
387 <div><em>&#116;&#101;&#115;&#116;</em></div>
388 <div>parents: </div>
388 <div>parents: </div>
389 <a href="/diff/06824edf55d0/primes.py">diff</a>
389 <a href="/diff/06824edf55d0/primes.py">diff</a>
390 <a href="/rev/06824edf55d0">changeset</a>
390 <a href="/rev/06824edf55d0">changeset</a>
391 </div>
391 </div>
392 </td>
392 </td>
393 <td class="source"><a href="#l6"> 6</a> <span class="sd"> where sieve (p:ns) = p : sieve [n | n &lt;- ns, mod n p /= 0]</span></td>
393 <td class="source"><a href="#l6"> 6</a> <span class="sd"> where sieve (p:ns) = p : sieve [n | n &lt;- ns, mod n p /= 0]</span></td>
394 </tr>
394 </tr>
395 <tr id="l7" class="thisrev">
395 <tr id="l7" class="thisrev">
396 <td class="annotate">
396 <td class="annotate parity0">
397
397
398 <div class="annotate-info">
398 <div class="annotate-info">
399 <div>
399 <div>
400 <a href="/annotate/06824edf55d0/primes.py#l7">
400 <a href="/annotate/06824edf55d0/primes.py#l7">
401 06824edf55d0</a>
401 06824edf55d0</a>
402 a
402 a
403 </div>
403 </div>
404 <div><em>&#116;&#101;&#115;&#116;</em></div>
404 <div><em>&#116;&#101;&#115;&#116;</em></div>
405 <div>parents: </div>
405 <div>parents: </div>
406 <a href="/diff/06824edf55d0/primes.py">diff</a>
406 <a href="/diff/06824edf55d0/primes.py">diff</a>
407 <a href="/rev/06824edf55d0">changeset</a>
407 <a href="/rev/06824edf55d0">changeset</a>
408 </div>
408 </div>
409 </td>
409 </td>
410 <td class="source"><a href="#l7"> 7</a> <span class="sd">&quot;&quot;&quot;</span></td>
410 <td class="source"><a href="#l7"> 7</a> <span class="sd">&quot;&quot;&quot;</span></td>
411 </tr>
411 </tr>
412 <tr id="l8" class="thisrev">
412 <tr id="l8" class="thisrev">
413 <td class="annotate">
413 <td class="annotate parity0">
414
414
415 <div class="annotate-info">
415 <div class="annotate-info">
416 <div>
416 <div>
417 <a href="/annotate/06824edf55d0/primes.py#l8">
417 <a href="/annotate/06824edf55d0/primes.py#l8">
418 06824edf55d0</a>
418 06824edf55d0</a>
419 a
419 a
420 </div>
420 </div>
421 <div><em>&#116;&#101;&#115;&#116;</em></div>
421 <div><em>&#116;&#101;&#115;&#116;</em></div>
422 <div>parents: </div>
422 <div>parents: </div>
423 <a href="/diff/06824edf55d0/primes.py">diff</a>
423 <a href="/diff/06824edf55d0/primes.py">diff</a>
424 <a href="/rev/06824edf55d0">changeset</a>
424 <a href="/rev/06824edf55d0">changeset</a>
425 </div>
425 </div>
426 </td>
426 </td>
427 <td class="source"><a href="#l8"> 8</a> </td>
427 <td class="source"><a href="#l8"> 8</a> </td>
428 </tr>
428 </tr>
429 <tr id="l9" class="thisrev">
429 <tr id="l9" class="thisrev">
430 <td class="annotate">
430 <td class="annotate parity0">
431
431
432 <div class="annotate-info">
432 <div class="annotate-info">
433 <div>
433 <div>
434 <a href="/annotate/06824edf55d0/primes.py#l9">
434 <a href="/annotate/06824edf55d0/primes.py#l9">
435 06824edf55d0</a>
435 06824edf55d0</a>
436 a
436 a
437 </div>
437 </div>
438 <div><em>&#116;&#101;&#115;&#116;</em></div>
438 <div><em>&#116;&#101;&#115;&#116;</em></div>
439 <div>parents: </div>
439 <div>parents: </div>
440 <a href="/diff/06824edf55d0/primes.py">diff</a>
440 <a href="/diff/06824edf55d0/primes.py">diff</a>
441 <a href="/rev/06824edf55d0">changeset</a>
441 <a href="/rev/06824edf55d0">changeset</a>
442 </div>
442 </div>
443 </td>
443 </td>
444 <td class="source"><a href="#l9"> 9</a> <span class="kn">from</span> <span class="nn">itertools</span> <span class="kn">import</span> <span class="n">dropwhile</span><span class="p">,</span> <span class="n">ifilter</span><span class="p">,</span> <span class="n">islice</span><span class="p">,</span> <span class="n">count</span><span class="p">,</span> <span class="n">chain</span></td>
444 <td class="source"><a href="#l9"> 9</a> <span class="kn">from</span> <span class="nn">itertools</span> <span class="kn">import</span> <span class="n">dropwhile</span><span class="p">,</span> <span class="n">ifilter</span><span class="p">,</span> <span class="n">islice</span><span class="p">,</span> <span class="n">count</span><span class="p">,</span> <span class="n">chain</span></td>
445 </tr>
445 </tr>
446 <tr id="l10" class="thisrev">
446 <tr id="l10" class="thisrev">
447 <td class="annotate">
447 <td class="annotate parity0">
448
448
449 <div class="annotate-info">
449 <div class="annotate-info">
450 <div>
450 <div>
451 <a href="/annotate/06824edf55d0/primes.py#l10">
451 <a href="/annotate/06824edf55d0/primes.py#l10">
452 06824edf55d0</a>
452 06824edf55d0</a>
453 a
453 a
454 </div>
454 </div>
455 <div><em>&#116;&#101;&#115;&#116;</em></div>
455 <div><em>&#116;&#101;&#115;&#116;</em></div>
456 <div>parents: </div>
456 <div>parents: </div>
457 <a href="/diff/06824edf55d0/primes.py">diff</a>
457 <a href="/diff/06824edf55d0/primes.py">diff</a>
458 <a href="/rev/06824edf55d0">changeset</a>
458 <a href="/rev/06824edf55d0">changeset</a>
459 </div>
459 </div>
460 </td>
460 </td>
461 <td class="source"><a href="#l10"> 10</a> </td>
461 <td class="source"><a href="#l10"> 10</a> </td>
462 </tr>
462 </tr>
463 <tr id="l11" class="thisrev">
463 <tr id="l11" class="thisrev">
464 <td class="annotate">
464 <td class="annotate parity0">
465
465
466 <div class="annotate-info">
466 <div class="annotate-info">
467 <div>
467 <div>
468 <a href="/annotate/06824edf55d0/primes.py#l11">
468 <a href="/annotate/06824edf55d0/primes.py#l11">
469 06824edf55d0</a>
469 06824edf55d0</a>
470 a
470 a
471 </div>
471 </div>
472 <div><em>&#116;&#101;&#115;&#116;</em></div>
472 <div><em>&#116;&#101;&#115;&#116;</em></div>
473 <div>parents: </div>
473 <div>parents: </div>
474 <a href="/diff/06824edf55d0/primes.py">diff</a>
474 <a href="/diff/06824edf55d0/primes.py">diff</a>
475 <a href="/rev/06824edf55d0">changeset</a>
475 <a href="/rev/06824edf55d0">changeset</a>
476 </div>
476 </div>
477 </td>
477 </td>
478 <td class="source"><a href="#l11"> 11</a> <span class="kn">def</span> <span class="nf">primes</span><span class="p">():</span></td>
478 <td class="source"><a href="#l11"> 11</a> <span class="kn">def</span> <span class="nf">primes</span><span class="p">():</span></td>
479 </tr>
479 </tr>
480 <tr id="l12" class="thisrev">
480 <tr id="l12" class="thisrev">
481 <td class="annotate">
481 <td class="annotate parity0">
482
482
483 <div class="annotate-info">
483 <div class="annotate-info">
484 <div>
484 <div>
485 <a href="/annotate/06824edf55d0/primes.py#l12">
485 <a href="/annotate/06824edf55d0/primes.py#l12">
486 06824edf55d0</a>
486 06824edf55d0</a>
487 a
487 a
488 </div>
488 </div>
489 <div><em>&#116;&#101;&#115;&#116;</em></div>
489 <div><em>&#116;&#101;&#115;&#116;</em></div>
490 <div>parents: </div>
490 <div>parents: </div>
491 <a href="/diff/06824edf55d0/primes.py">diff</a>
491 <a href="/diff/06824edf55d0/primes.py">diff</a>
492 <a href="/rev/06824edf55d0">changeset</a>
492 <a href="/rev/06824edf55d0">changeset</a>
493 </div>
493 </div>
494 </td>
494 </td>
495 <td class="source"><a href="#l12"> 12</a> <span class="sd">&quot;&quot;&quot;Generate all primes.&quot;&quot;&quot;</span></td>
495 <td class="source"><a href="#l12"> 12</a> <span class="sd">&quot;&quot;&quot;Generate all primes.&quot;&quot;&quot;</span></td>
496 </tr>
496 </tr>
497 <tr id="l13" class="thisrev">
497 <tr id="l13" class="thisrev">
498 <td class="annotate">
498 <td class="annotate parity0">
499
499
500 <div class="annotate-info">
500 <div class="annotate-info">
501 <div>
501 <div>
502 <a href="/annotate/06824edf55d0/primes.py#l13">
502 <a href="/annotate/06824edf55d0/primes.py#l13">
503 06824edf55d0</a>
503 06824edf55d0</a>
504 a
504 a
505 </div>
505 </div>
506 <div><em>&#116;&#101;&#115;&#116;</em></div>
506 <div><em>&#116;&#101;&#115;&#116;</em></div>
507 <div>parents: </div>
507 <div>parents: </div>
508 <a href="/diff/06824edf55d0/primes.py">diff</a>
508 <a href="/diff/06824edf55d0/primes.py">diff</a>
509 <a href="/rev/06824edf55d0">changeset</a>
509 <a href="/rev/06824edf55d0">changeset</a>
510 </div>
510 </div>
511 </td>
511 </td>
512 <td class="source"><a href="#l13"> 13</a> <span class="kn">def</span> <span class="nf">sieve</span><span class="p">(</span><span class="n">ns</span><span class="p">):</span></td>
512 <td class="source"><a href="#l13"> 13</a> <span class="kn">def</span> <span class="nf">sieve</span><span class="p">(</span><span class="n">ns</span><span class="p">):</span></td>
513 </tr>
513 </tr>
514 <tr id="l14" class="thisrev">
514 <tr id="l14" class="thisrev">
515 <td class="annotate">
515 <td class="annotate parity0">
516
516
517 <div class="annotate-info">
517 <div class="annotate-info">
518 <div>
518 <div>
519 <a href="/annotate/06824edf55d0/primes.py#l14">
519 <a href="/annotate/06824edf55d0/primes.py#l14">
520 06824edf55d0</a>
520 06824edf55d0</a>
521 a
521 a
522 </div>
522 </div>
523 <div><em>&#116;&#101;&#115;&#116;</em></div>
523 <div><em>&#116;&#101;&#115;&#116;</em></div>
524 <div>parents: </div>
524 <div>parents: </div>
525 <a href="/diff/06824edf55d0/primes.py">diff</a>
525 <a href="/diff/06824edf55d0/primes.py">diff</a>
526 <a href="/rev/06824edf55d0">changeset</a>
526 <a href="/rev/06824edf55d0">changeset</a>
527 </div>
527 </div>
528 </td>
528 </td>
529 <td class="source"><a href="#l14"> 14</a> <span class="n">p</span> <span class="o">=</span> <span class="n">ns</span><span class="o">.</span><span class="n">next</span><span class="p">()</span></td>
529 <td class="source"><a href="#l14"> 14</a> <span class="n">p</span> <span class="o">=</span> <span class="n">ns</span><span class="o">.</span><span class="n">next</span><span class="p">()</span></td>
530 </tr>
530 </tr>
531 <tr id="l15" class="thisrev">
531 <tr id="l15" class="thisrev">
532 <td class="annotate">
532 <td class="annotate parity0">
533
533
534 <div class="annotate-info">
534 <div class="annotate-info">
535 <div>
535 <div>
536 <a href="/annotate/06824edf55d0/primes.py#l15">
536 <a href="/annotate/06824edf55d0/primes.py#l15">
537 06824edf55d0</a>
537 06824edf55d0</a>
538 a
538 a
539 </div>
539 </div>
540 <div><em>&#116;&#101;&#115;&#116;</em></div>
540 <div><em>&#116;&#101;&#115;&#116;</em></div>
541 <div>parents: </div>
541 <div>parents: </div>
542 <a href="/diff/06824edf55d0/primes.py">diff</a>
542 <a href="/diff/06824edf55d0/primes.py">diff</a>
543 <a href="/rev/06824edf55d0">changeset</a>
543 <a href="/rev/06824edf55d0">changeset</a>
544 </div>
544 </div>
545 </td>
545 </td>
546 <td class="source"><a href="#l15"> 15</a> <span class="c"># It is important to yield *here* in order to stop the</span></td>
546 <td class="source"><a href="#l15"> 15</a> <span class="c"># It is important to yield *here* in order to stop the</span></td>
547 </tr>
547 </tr>
548 <tr id="l16" class="thisrev">
548 <tr id="l16" class="thisrev">
549 <td class="annotate">
549 <td class="annotate parity0">
550
550
551 <div class="annotate-info">
551 <div class="annotate-info">
552 <div>
552 <div>
553 <a href="/annotate/06824edf55d0/primes.py#l16">
553 <a href="/annotate/06824edf55d0/primes.py#l16">
554 06824edf55d0</a>
554 06824edf55d0</a>
555 a
555 a
556 </div>
556 </div>
557 <div><em>&#116;&#101;&#115;&#116;</em></div>
557 <div><em>&#116;&#101;&#115;&#116;</em></div>
558 <div>parents: </div>
558 <div>parents: </div>
559 <a href="/diff/06824edf55d0/primes.py">diff</a>
559 <a href="/diff/06824edf55d0/primes.py">diff</a>
560 <a href="/rev/06824edf55d0">changeset</a>
560 <a href="/rev/06824edf55d0">changeset</a>
561 </div>
561 </div>
562 </td>
562 </td>
563 <td class="source"><a href="#l16"> 16</a> <span class="c"># infinite recursion.</span></td>
563 <td class="source"><a href="#l16"> 16</a> <span class="c"># infinite recursion.</span></td>
564 </tr>
564 </tr>
565 <tr id="l17" class="thisrev">
565 <tr id="l17" class="thisrev">
566 <td class="annotate">
566 <td class="annotate parity0">
567
567
568 <div class="annotate-info">
568 <div class="annotate-info">
569 <div>
569 <div>
570 <a href="/annotate/06824edf55d0/primes.py#l17">
570 <a href="/annotate/06824edf55d0/primes.py#l17">
571 06824edf55d0</a>
571 06824edf55d0</a>
572 a
572 a
573 </div>
573 </div>
574 <div><em>&#116;&#101;&#115;&#116;</em></div>
574 <div><em>&#116;&#101;&#115;&#116;</em></div>
575 <div>parents: </div>
575 <div>parents: </div>
576 <a href="/diff/06824edf55d0/primes.py">diff</a>
576 <a href="/diff/06824edf55d0/primes.py">diff</a>
577 <a href="/rev/06824edf55d0">changeset</a>
577 <a href="/rev/06824edf55d0">changeset</a>
578 </div>
578 </div>
579 </td>
579 </td>
580 <td class="source"><a href="#l17"> 17</a> <span class="kn">yield</span> <span class="n">p</span></td>
580 <td class="source"><a href="#l17"> 17</a> <span class="kn">yield</span> <span class="n">p</span></td>
581 </tr>
581 </tr>
582 <tr id="l18" class="thisrev">
582 <tr id="l18" class="thisrev">
583 <td class="annotate">
583 <td class="annotate parity0">
584
584
585 <div class="annotate-info">
585 <div class="annotate-info">
586 <div>
586 <div>
587 <a href="/annotate/06824edf55d0/primes.py#l18">
587 <a href="/annotate/06824edf55d0/primes.py#l18">
588 06824edf55d0</a>
588 06824edf55d0</a>
589 a
589 a
590 </div>
590 </div>
591 <div><em>&#116;&#101;&#115;&#116;</em></div>
591 <div><em>&#116;&#101;&#115;&#116;</em></div>
592 <div>parents: </div>
592 <div>parents: </div>
593 <a href="/diff/06824edf55d0/primes.py">diff</a>
593 <a href="/diff/06824edf55d0/primes.py">diff</a>
594 <a href="/rev/06824edf55d0">changeset</a>
594 <a href="/rev/06824edf55d0">changeset</a>
595 </div>
595 </div>
596 </td>
596 </td>
597 <td class="source"><a href="#l18"> 18</a> <span class="n">ns</span> <span class="o">=</span> <span class="n">ifilter</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">n</span><span class="p">:</span> <span class="n">n</span> <span class="o">%</span> <span class="n">p</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">ns</span><span class="p">)</span></td>
597 <td class="source"><a href="#l18"> 18</a> <span class="n">ns</span> <span class="o">=</span> <span class="n">ifilter</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">n</span><span class="p">:</span> <span class="n">n</span> <span class="o">%</span> <span class="n">p</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">ns</span><span class="p">)</span></td>
598 </tr>
598 </tr>
599 <tr id="l19" class="thisrev">
599 <tr id="l19" class="thisrev">
600 <td class="annotate">
600 <td class="annotate parity0">
601
601
602 <div class="annotate-info">
602 <div class="annotate-info">
603 <div>
603 <div>
604 <a href="/annotate/06824edf55d0/primes.py#l19">
604 <a href="/annotate/06824edf55d0/primes.py#l19">
605 06824edf55d0</a>
605 06824edf55d0</a>
606 a
606 a
607 </div>
607 </div>
608 <div><em>&#116;&#101;&#115;&#116;</em></div>
608 <div><em>&#116;&#101;&#115;&#116;</em></div>
609 <div>parents: </div>
609 <div>parents: </div>
610 <a href="/diff/06824edf55d0/primes.py">diff</a>
610 <a href="/diff/06824edf55d0/primes.py">diff</a>
611 <a href="/rev/06824edf55d0">changeset</a>
611 <a href="/rev/06824edf55d0">changeset</a>
612 </div>
612 </div>
613 </td>
613 </td>
614 <td class="source"><a href="#l19"> 19</a> <span class="kn">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="n">sieve</span><span class="p">(</span><span class="n">ns</span><span class="p">):</span></td>
614 <td class="source"><a href="#l19"> 19</a> <span class="kn">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="n">sieve</span><span class="p">(</span><span class="n">ns</span><span class="p">):</span></td>
615 </tr>
615 </tr>
616 <tr id="l20" class="thisrev">
616 <tr id="l20" class="thisrev">
617 <td class="annotate">
617 <td class="annotate parity0">
618
618
619 <div class="annotate-info">
619 <div class="annotate-info">
620 <div>
620 <div>
621 <a href="/annotate/06824edf55d0/primes.py#l20">
621 <a href="/annotate/06824edf55d0/primes.py#l20">
622 06824edf55d0</a>
622 06824edf55d0</a>
623 a
623 a
624 </div>
624 </div>
625 <div><em>&#116;&#101;&#115;&#116;</em></div>
625 <div><em>&#116;&#101;&#115;&#116;</em></div>
626 <div>parents: </div>
626 <div>parents: </div>
627 <a href="/diff/06824edf55d0/primes.py">diff</a>
627 <a href="/diff/06824edf55d0/primes.py">diff</a>
628 <a href="/rev/06824edf55d0">changeset</a>
628 <a href="/rev/06824edf55d0">changeset</a>
629 </div>
629 </div>
630 </td>
630 </td>
631 <td class="source"><a href="#l20"> 20</a> <span class="kn">yield</span> <span class="n">n</span></td>
631 <td class="source"><a href="#l20"> 20</a> <span class="kn">yield</span> <span class="n">n</span></td>
632 </tr>
632 </tr>
633 <tr id="l21" class="thisrev">
633 <tr id="l21" class="thisrev">
634 <td class="annotate">
634 <td class="annotate parity0">
635
635
636 <div class="annotate-info">
636 <div class="annotate-info">
637 <div>
637 <div>
638 <a href="/annotate/06824edf55d0/primes.py#l21">
638 <a href="/annotate/06824edf55d0/primes.py#l21">
639 06824edf55d0</a>
639 06824edf55d0</a>
640 a
640 a
641 </div>
641 </div>
642 <div><em>&#116;&#101;&#115;&#116;</em></div>
642 <div><em>&#116;&#101;&#115;&#116;</em></div>
643 <div>parents: </div>
643 <div>parents: </div>
644 <a href="/diff/06824edf55d0/primes.py">diff</a>
644 <a href="/diff/06824edf55d0/primes.py">diff</a>
645 <a href="/rev/06824edf55d0">changeset</a>
645 <a href="/rev/06824edf55d0">changeset</a>
646 </div>
646 </div>
647 </td>
647 </td>
648 <td class="source"><a href="#l21"> 21</a> </td>
648 <td class="source"><a href="#l21"> 21</a> </td>
649 </tr>
649 </tr>
650 <tr id="l22" class="thisrev">
650 <tr id="l22" class="thisrev">
651 <td class="annotate">
651 <td class="annotate parity0">
652
652
653 <div class="annotate-info">
653 <div class="annotate-info">
654 <div>
654 <div>
655 <a href="/annotate/06824edf55d0/primes.py#l22">
655 <a href="/annotate/06824edf55d0/primes.py#l22">
656 06824edf55d0</a>
656 06824edf55d0</a>
657 a
657 a
658 </div>
658 </div>
659 <div><em>&#116;&#101;&#115;&#116;</em></div>
659 <div><em>&#116;&#101;&#115;&#116;</em></div>
660 <div>parents: </div>
660 <div>parents: </div>
661 <a href="/diff/06824edf55d0/primes.py">diff</a>
661 <a href="/diff/06824edf55d0/primes.py">diff</a>
662 <a href="/rev/06824edf55d0">changeset</a>
662 <a href="/rev/06824edf55d0">changeset</a>
663 </div>
663 </div>
664 </td>
664 </td>
665 <td class="source"><a href="#l22"> 22</a> <span class="n">odds</span> <span class="o">=</span> <span class="n">ifilter</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">i</span><span class="p">:</span> <span class="n">i</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">1</span><span class="p">,</span> <span class="n">count</span><span class="p">())</span></td>
665 <td class="source"><a href="#l22"> 22</a> <span class="n">odds</span> <span class="o">=</span> <span class="n">ifilter</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">i</span><span class="p">:</span> <span class="n">i</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">1</span><span class="p">,</span> <span class="n">count</span><span class="p">())</span></td>
666 </tr>
666 </tr>
667 <tr id="l23" class="thisrev">
667 <tr id="l23" class="thisrev">
668 <td class="annotate">
668 <td class="annotate parity0">
669
669
670 <div class="annotate-info">
670 <div class="annotate-info">
671 <div>
671 <div>
672 <a href="/annotate/06824edf55d0/primes.py#l23">
672 <a href="/annotate/06824edf55d0/primes.py#l23">
673 06824edf55d0</a>
673 06824edf55d0</a>
674 a
674 a
675 </div>
675 </div>
676 <div><em>&#116;&#101;&#115;&#116;</em></div>
676 <div><em>&#116;&#101;&#115;&#116;</em></div>
677 <div>parents: </div>
677 <div>parents: </div>
678 <a href="/diff/06824edf55d0/primes.py">diff</a>
678 <a href="/diff/06824edf55d0/primes.py">diff</a>
679 <a href="/rev/06824edf55d0">changeset</a>
679 <a href="/rev/06824edf55d0">changeset</a>
680 </div>
680 </div>
681 </td>
681 </td>
682 <td class="source"><a href="#l23"> 23</a> <span class="kn">return</span> <span class="n">chain</span><span class="p">([</span><span class="mi">2</span><span class="p">],</span> <span class="n">sieve</span><span class="p">(</span><span class="n">dropwhile</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">n</span><span class="p">:</span> <span class="n">n</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">,</span> <span class="n">odds</span><span class="p">)))</span></td>
682 <td class="source"><a href="#l23"> 23</a> <span class="kn">return</span> <span class="n">chain</span><span class="p">([</span><span class="mi">2</span><span class="p">],</span> <span class="n">sieve</span><span class="p">(</span><span class="n">dropwhile</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">n</span><span class="p">:</span> <span class="n">n</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">,</span> <span class="n">odds</span><span class="p">)))</span></td>
683 </tr>
683 </tr>
684 <tr id="l24" class="thisrev">
684 <tr id="l24" class="thisrev">
685 <td class="annotate">
685 <td class="annotate parity0">
686
686
687 <div class="annotate-info">
687 <div class="annotate-info">
688 <div>
688 <div>
689 <a href="/annotate/06824edf55d0/primes.py#l24">
689 <a href="/annotate/06824edf55d0/primes.py#l24">
690 06824edf55d0</a>
690 06824edf55d0</a>
691 a
691 a
692 </div>
692 </div>
693 <div><em>&#116;&#101;&#115;&#116;</em></div>
693 <div><em>&#116;&#101;&#115;&#116;</em></div>
694 <div>parents: </div>
694 <div>parents: </div>
695 <a href="/diff/06824edf55d0/primes.py">diff</a>
695 <a href="/diff/06824edf55d0/primes.py">diff</a>
696 <a href="/rev/06824edf55d0">changeset</a>
696 <a href="/rev/06824edf55d0">changeset</a>
697 </div>
697 </div>
698 </td>
698 </td>
699 <td class="source"><a href="#l24"> 24</a> </td>
699 <td class="source"><a href="#l24"> 24</a> </td>
700 </tr>
700 </tr>
701 <tr id="l25" class="thisrev">
701 <tr id="l25" class="thisrev">
702 <td class="annotate">
702 <td class="annotate parity0">
703
703
704 <div class="annotate-info">
704 <div class="annotate-info">
705 <div>
705 <div>
706 <a href="/annotate/06824edf55d0/primes.py#l25">
706 <a href="/annotate/06824edf55d0/primes.py#l25">
707 06824edf55d0</a>
707 06824edf55d0</a>
708 a
708 a
709 </div>
709 </div>
710 <div><em>&#116;&#101;&#115;&#116;</em></div>
710 <div><em>&#116;&#101;&#115;&#116;</em></div>
711 <div>parents: </div>
711 <div>parents: </div>
712 <a href="/diff/06824edf55d0/primes.py">diff</a>
712 <a href="/diff/06824edf55d0/primes.py">diff</a>
713 <a href="/rev/06824edf55d0">changeset</a>
713 <a href="/rev/06824edf55d0">changeset</a>
714 </div>
714 </div>
715 </td>
715 </td>
716 <td class="source"><a href="#l25"> 25</a> <span class="kn">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">&quot;__main__&quot;</span><span class="p">:</span></td>
716 <td class="source"><a href="#l25"> 25</a> <span class="kn">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">&quot;__main__&quot;</span><span class="p">:</span></td>
717 </tr>
717 </tr>
718 <tr id="l26" class="thisrev">
718 <tr id="l26" class="thisrev">
719 <td class="annotate">
719 <td class="annotate parity0">
720
720
721 <div class="annotate-info">
721 <div class="annotate-info">
722 <div>
722 <div>
723 <a href="/annotate/06824edf55d0/primes.py#l26">
723 <a href="/annotate/06824edf55d0/primes.py#l26">
724 06824edf55d0</a>
724 06824edf55d0</a>
725 a
725 a
726 </div>
726 </div>
727 <div><em>&#116;&#101;&#115;&#116;</em></div>
727 <div><em>&#116;&#101;&#115;&#116;</em></div>
728 <div>parents: </div>
728 <div>parents: </div>
729 <a href="/diff/06824edf55d0/primes.py">diff</a>
729 <a href="/diff/06824edf55d0/primes.py">diff</a>
730 <a href="/rev/06824edf55d0">changeset</a>
730 <a href="/rev/06824edf55d0">changeset</a>
731 </div>
731 </div>
732 </td>
732 </td>
733 <td class="source"><a href="#l26"> 26</a> <span class="kn">import</span> <span class="nn">sys</span></td>
733 <td class="source"><a href="#l26"> 26</a> <span class="kn">import</span> <span class="nn">sys</span></td>
734 </tr>
734 </tr>
735 <tr id="l27" class="thisrev">
735 <tr id="l27" class="thisrev">
736 <td class="annotate">
736 <td class="annotate parity0">
737
737
738 <div class="annotate-info">
738 <div class="annotate-info">
739 <div>
739 <div>
740 <a href="/annotate/06824edf55d0/primes.py#l27">
740 <a href="/annotate/06824edf55d0/primes.py#l27">
741 06824edf55d0</a>
741 06824edf55d0</a>
742 a
742 a
743 </div>
743 </div>
744 <div><em>&#116;&#101;&#115;&#116;</em></div>
744 <div><em>&#116;&#101;&#115;&#116;</em></div>
745 <div>parents: </div>
745 <div>parents: </div>
746 <a href="/diff/06824edf55d0/primes.py">diff</a>
746 <a href="/diff/06824edf55d0/primes.py">diff</a>
747 <a href="/rev/06824edf55d0">changeset</a>
747 <a href="/rev/06824edf55d0">changeset</a>
748 </div>
748 </div>
749 </td>
749 </td>
750 <td class="source"><a href="#l27"> 27</a> <span class="kn">try</span><span class="p">:</span></td>
750 <td class="source"><a href="#l27"> 27</a> <span class="kn">try</span><span class="p">:</span></td>
751 </tr>
751 </tr>
752 <tr id="l28" class="thisrev">
752 <tr id="l28" class="thisrev">
753 <td class="annotate">
753 <td class="annotate parity0">
754
754
755 <div class="annotate-info">
755 <div class="annotate-info">
756 <div>
756 <div>
757 <a href="/annotate/06824edf55d0/primes.py#l28">
757 <a href="/annotate/06824edf55d0/primes.py#l28">
758 06824edf55d0</a>
758 06824edf55d0</a>
759 a
759 a
760 </div>
760 </div>
761 <div><em>&#116;&#101;&#115;&#116;</em></div>
761 <div><em>&#116;&#101;&#115;&#116;</em></div>
762 <div>parents: </div>
762 <div>parents: </div>
763 <a href="/diff/06824edf55d0/primes.py">diff</a>
763 <a href="/diff/06824edf55d0/primes.py">diff</a>
764 <a href="/rev/06824edf55d0">changeset</a>
764 <a href="/rev/06824edf55d0">changeset</a>
765 </div>
765 </div>
766 </td>
766 </td>
767 <td class="source"><a href="#l28"> 28</a> <span class="n">n</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span></td>
767 <td class="source"><a href="#l28"> 28</a> <span class="n">n</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span></td>
768 </tr>
768 </tr>
769 <tr id="l29" class="thisrev">
769 <tr id="l29" class="thisrev">
770 <td class="annotate">
770 <td class="annotate parity0">
771
771
772 <div class="annotate-info">
772 <div class="annotate-info">
773 <div>
773 <div>
774 <a href="/annotate/06824edf55d0/primes.py#l29">
774 <a href="/annotate/06824edf55d0/primes.py#l29">
775 06824edf55d0</a>
775 06824edf55d0</a>
776 a
776 a
777 </div>
777 </div>
778 <div><em>&#116;&#101;&#115;&#116;</em></div>
778 <div><em>&#116;&#101;&#115;&#116;</em></div>
779 <div>parents: </div>
779 <div>parents: </div>
780 <a href="/diff/06824edf55d0/primes.py">diff</a>
780 <a href="/diff/06824edf55d0/primes.py">diff</a>
781 <a href="/rev/06824edf55d0">changeset</a>
781 <a href="/rev/06824edf55d0">changeset</a>
782 </div>
782 </div>
783 </td>
783 </td>
784 <td class="source"><a href="#l29"> 29</a> <span class="kn">except</span> <span class="p">(</span><span class="ne">ValueError</span><span class="p">,</span> <span class="ne">IndexError</span><span class="p">):</span></td>
784 <td class="source"><a href="#l29"> 29</a> <span class="kn">except</span> <span class="p">(</span><span class="ne">ValueError</span><span class="p">,</span> <span class="ne">IndexError</span><span class="p">):</span></td>
785 </tr>
785 </tr>
786 <tr id="l30" class="thisrev">
786 <tr id="l30" class="thisrev">
787 <td class="annotate">
787 <td class="annotate parity0">
788
788
789 <div class="annotate-info">
789 <div class="annotate-info">
790 <div>
790 <div>
791 <a href="/annotate/06824edf55d0/primes.py#l30">
791 <a href="/annotate/06824edf55d0/primes.py#l30">
792 06824edf55d0</a>
792 06824edf55d0</a>
793 a
793 a
794 </div>
794 </div>
795 <div><em>&#116;&#101;&#115;&#116;</em></div>
795 <div><em>&#116;&#101;&#115;&#116;</em></div>
796 <div>parents: </div>
796 <div>parents: </div>
797 <a href="/diff/06824edf55d0/primes.py">diff</a>
797 <a href="/diff/06824edf55d0/primes.py">diff</a>
798 <a href="/rev/06824edf55d0">changeset</a>
798 <a href="/rev/06824edf55d0">changeset</a>
799 </div>
799 </div>
800 </td>
800 </td>
801 <td class="source"><a href="#l30"> 30</a> <span class="n">n</span> <span class="o">=</span> <span class="mi">10</span></td>
801 <td class="source"><a href="#l30"> 30</a> <span class="n">n</span> <span class="o">=</span> <span class="mi">10</span></td>
802 </tr>
802 </tr>
803 <tr id="l31" class="thisrev">
803 <tr id="l31" class="thisrev">
804 <td class="annotate">
804 <td class="annotate parity0">
805
805
806 <div class="annotate-info">
806 <div class="annotate-info">
807 <div>
807 <div>
808 <a href="/annotate/06824edf55d0/primes.py#l31">
808 <a href="/annotate/06824edf55d0/primes.py#l31">
809 06824edf55d0</a>
809 06824edf55d0</a>
810 a
810 a
811 </div>
811 </div>
812 <div><em>&#116;&#101;&#115;&#116;</em></div>
812 <div><em>&#116;&#101;&#115;&#116;</em></div>
813 <div>parents: </div>
813 <div>parents: </div>
814 <a href="/diff/06824edf55d0/primes.py">diff</a>
814 <a href="/diff/06824edf55d0/primes.py">diff</a>
815 <a href="/rev/06824edf55d0">changeset</a>
815 <a href="/rev/06824edf55d0">changeset</a>
816 </div>
816 </div>
817 </td>
817 </td>
818 <td class="source"><a href="#l31"> 31</a> <span class="n">p</span> <span class="o">=</span> <span class="n">primes</span><span class="p">()</span></td>
818 <td class="source"><a href="#l31"> 31</a> <span class="n">p</span> <span class="o">=</span> <span class="n">primes</span><span class="p">()</span></td>
819 </tr>
819 </tr>
820 <tr id="l32" class="thisrev">
820 <tr id="l32" class="thisrev">
821 <td class="annotate">
821 <td class="annotate parity0">
822
822
823 <div class="annotate-info">
823 <div class="annotate-info">
824 <div>
824 <div>
825 <a href="/annotate/06824edf55d0/primes.py#l32">
825 <a href="/annotate/06824edf55d0/primes.py#l32">
826 06824edf55d0</a>
826 06824edf55d0</a>
827 a
827 a
828 </div>
828 </div>
829 <div><em>&#116;&#101;&#115;&#116;</em></div>
829 <div><em>&#116;&#101;&#115;&#116;</em></div>
830 <div>parents: </div>
830 <div>parents: </div>
831 <a href="/diff/06824edf55d0/primes.py">diff</a>
831 <a href="/diff/06824edf55d0/primes.py">diff</a>
832 <a href="/rev/06824edf55d0">changeset</a>
832 <a href="/rev/06824edf55d0">changeset</a>
833 </div>
833 </div>
834 </td>
834 </td>
835 <td class="source"><a href="#l32"> 32</a> <span class="kn">print</span> <span class="s">&quot;The first </span><span class="si">%d</span><span class="s"> primes: </span><span class="si">%s</span><span class="s">&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="nb">list</span><span class="p">(</span><span class="n">islice</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">n</span><span class="p">)))</span></td>
835 <td class="source"><a href="#l32"> 32</a> <span class="kn">print</span> <span class="s">&quot;The first </span><span class="si">%d</span><span class="s"> primes: </span><span class="si">%s</span><span class="s">&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="nb">list</span><span class="p">(</span><span class="n">islice</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">n</span><span class="p">)))</span></td>
836 </tr>
836 </tr>
837 <tr id="l33" class="thisrev">
837 <tr id="l33" class="thisrev">
838 <td class="annotate">
838 <td class="annotate parity0">
839
839
840 <div class="annotate-info">
840 <div class="annotate-info">
841 <div>
841 <div>
842 <a href="/annotate/06824edf55d0/primes.py#l33">
842 <a href="/annotate/06824edf55d0/primes.py#l33">
843 06824edf55d0</a>
843 06824edf55d0</a>
844 a
844 a
845 </div>
845 </div>
846 <div><em>&#116;&#101;&#115;&#116;</em></div>
846 <div><em>&#116;&#101;&#115;&#116;</em></div>
847 <div>parents: </div>
847 <div>parents: </div>
848 <a href="/diff/06824edf55d0/primes.py">diff</a>
848 <a href="/diff/06824edf55d0/primes.py">diff</a>
849 <a href="/rev/06824edf55d0">changeset</a>
849 <a href="/rev/06824edf55d0">changeset</a>
850 </div>
850 </div>
851 </td>
851 </td>
852 <td class="source"><a href="#l33"> 33</a> </td>
852 <td class="source"><a href="#l33"> 33</a> </td>
853 </tr>
853 </tr>
854 </tbody>
854 </tbody>
855 </table>
855 </table>
856 </div>
856 </div>
857 </div>
857 </div>
858 </div>
858 </div>
859
859
860 <script type="text/javascript">process_dates()</script>
860 <script type="text/javascript">process_dates()</script>
861
861
862
862
863 </body>
863 </body>
864 </html>
864 </html>
865
865
866
866
867 hgweb fileannotate, raw
867 hgweb fileannotate, raw
868
868
869 $ (get-with-headers.py localhost:$HGPORT 'annotate/tip/primes.py?style=raw') \
869 $ (get-with-headers.py localhost:$HGPORT 'annotate/tip/primes.py?style=raw') \
870 > | sed "s/test@//" > a
870 > | sed "s/test@//" > a
871 $ echo "200 Script output follows" > b
871 $ echo "200 Script output follows" > b
872 $ echo "" >> b
872 $ echo "" >> b
873 $ echo "" >> b
873 $ echo "" >> b
874 $ hg annotate "primes.py" >> b
874 $ hg annotate "primes.py" >> b
875 $ echo "" >> b
875 $ echo "" >> b
876 $ echo "" >> b
876 $ echo "" >> b
877 $ echo "" >> b
877 $ echo "" >> b
878 $ echo "" >> b
878 $ echo "" >> b
879 $ cmp b a || diff -u b a
879 $ cmp b a || diff -u b a
880
880
881 hgweb filerevision, raw
881 hgweb filerevision, raw
882
882
883 $ (get-with-headers.py localhost:$HGPORT 'file/tip/primes.py?style=raw') \
883 $ (get-with-headers.py localhost:$HGPORT 'file/tip/primes.py?style=raw') \
884 > > a
884 > > a
885 $ echo "200 Script output follows" > b
885 $ echo "200 Script output follows" > b
886 $ echo "" >> b
886 $ echo "" >> b
887 $ hg cat primes.py >> b
887 $ hg cat primes.py >> b
888 $ cmp b a || diff -u b a
888 $ cmp b a || diff -u b a
889
889
890 hgweb highlightcss friendly
890 hgweb highlightcss friendly
891
891
892 $ get-with-headers.py localhost:$HGPORT 'highlightcss' > out
892 $ get-with-headers.py localhost:$HGPORT 'highlightcss' > out
893 $ head -n 4 out
893 $ head -n 4 out
894 200 Script output follows
894 200 Script output follows
895
895
896 /* pygments_style = friendly */
896 /* pygments_style = friendly */
897
897
898 $ rm out
898 $ rm out
899
899
900 errors encountered
900 errors encountered
901
901
902 $ cat errors.log
902 $ cat errors.log
903 $ killdaemons.py
903 $ killdaemons.py
904
904
905 Change the pygments style
905 Change the pygments style
906
906
907 $ cat > .hg/hgrc <<EOF
907 $ cat > .hg/hgrc <<EOF
908 > [web]
908 > [web]
909 > pygments_style = fruity
909 > pygments_style = fruity
910 > EOF
910 > EOF
911
911
912 hg serve again
912 hg serve again
913
913
914 $ hg serve -p $HGPORT -d -n test --pid-file=hg.pid -A access.log -E errors.log
914 $ hg serve -p $HGPORT -d -n test --pid-file=hg.pid -A access.log -E errors.log
915 $ cat hg.pid >> $DAEMON_PIDS
915 $ cat hg.pid >> $DAEMON_PIDS
916
916
917 hgweb highlightcss fruity
917 hgweb highlightcss fruity
918
918
919 $ get-with-headers.py localhost:$HGPORT 'highlightcss' > out
919 $ get-with-headers.py localhost:$HGPORT 'highlightcss' > out
920 $ head -n 4 out
920 $ head -n 4 out
921 200 Script output follows
921 200 Script output follows
922
922
923 /* pygments_style = fruity */
923 /* pygments_style = fruity */
924
924
925 $ rm out
925 $ rm out
926
926
927 errors encountered
927 errors encountered
928
928
929 $ cat errors.log
929 $ cat errors.log
930 $ killdaemons.py
930 $ killdaemons.py
931
931
932 only highlight C source files
932 only highlight C source files
933
933
934 $ cat > .hg/hgrc <<EOF
934 $ cat > .hg/hgrc <<EOF
935 > [web]
935 > [web]
936 > highlightfiles = **.c
936 > highlightfiles = **.c
937 > EOF
937 > EOF
938
938
939 hg serve again
939 hg serve again
940
940
941 $ hg serve -p $HGPORT -d -n test --pid-file=hg.pid -A access.log -E errors.log
941 $ hg serve -p $HGPORT -d -n test --pid-file=hg.pid -A access.log -E errors.log
942 $ cat hg.pid >> $DAEMON_PIDS
942 $ cat hg.pid >> $DAEMON_PIDS
943
943
944 test that fileset in highlightfiles works and primes.py is not highlighted
944 test that fileset in highlightfiles works and primes.py is not highlighted
945
945
946 $ get-with-headers.py localhost:$HGPORT 'file/tip/primes.py' | grep 'id="l11"'
946 $ get-with-headers.py localhost:$HGPORT 'file/tip/primes.py' | grep 'id="l11"'
947 <span id="l11">def primes():</span><a href="#l11"></a>
947 <span id="l11">def primes():</span><a href="#l11"></a>
948
948
949 errors encountered
949 errors encountered
950
950
951 $ cat errors.log
951 $ cat errors.log
952 $ cd ..
952 $ cd ..
953 $ hg init eucjp
953 $ hg init eucjp
954 $ cd eucjp
954 $ cd eucjp
955 $ $PYTHON -c 'print("\265\376")' >> eucjp.txt # Japanese kanji "Kyo"
955 $ $PYTHON -c 'print("\265\376")' >> eucjp.txt # Japanese kanji "Kyo"
956 $ hg ci -Ama
956 $ hg ci -Ama
957 adding eucjp.txt
957 adding eucjp.txt
958 $ hgserveget () {
958 $ hgserveget () {
959 > killdaemons.py
959 > killdaemons.py
960 > echo % HGENCODING="$1" hg serve
960 > echo % HGENCODING="$1" hg serve
961 > HGENCODING="$1" hg serve -p $HGPORT -d -n test --pid-file=hg.pid -E errors.log
961 > HGENCODING="$1" hg serve -p $HGPORT -d -n test --pid-file=hg.pid -E errors.log
962 > cat hg.pid >> $DAEMON_PIDS
962 > cat hg.pid >> $DAEMON_PIDS
963 >
963 >
964 > echo % hgweb filerevision, html
964 > echo % hgweb filerevision, html
965 > get-with-headers.py localhost:$HGPORT "file/tip/$2" \
965 > get-with-headers.py localhost:$HGPORT "file/tip/$2" \
966 > | grep '<div class="parity0 source">'
966 > | grep '<div class="parity0 source">'
967 > echo % errors encountered
967 > echo % errors encountered
968 > cat errors.log
968 > cat errors.log
969 > }
969 > }
970 $ hgserveget euc-jp eucjp.txt
970 $ hgserveget euc-jp eucjp.txt
971 % HGENCODING=euc-jp hg serve
971 % HGENCODING=euc-jp hg serve
972 % hgweb filerevision, html
972 % hgweb filerevision, html
973 % errors encountered
973 % errors encountered
974 $ hgserveget utf-8 eucjp.txt
974 $ hgserveget utf-8 eucjp.txt
975 % HGENCODING=utf-8 hg serve
975 % HGENCODING=utf-8 hg serve
976 % hgweb filerevision, html
976 % hgweb filerevision, html
977 % errors encountered
977 % errors encountered
978 $ hgserveget us-ascii eucjp.txt
978 $ hgserveget us-ascii eucjp.txt
979 % HGENCODING=us-ascii hg serve
979 % HGENCODING=us-ascii hg serve
980 % hgweb filerevision, html
980 % hgweb filerevision, html
981 % errors encountered
981 % errors encountered
982
982
983 We attempt to highlight unknown files by default
983 We attempt to highlight unknown files by default
984
984
985 $ killdaemons.py
985 $ killdaemons.py
986
986
987 $ cat > .hg/hgrc << EOF
987 $ cat > .hg/hgrc << EOF
988 > [web]
988 > [web]
989 > highlightfiles = **
989 > highlightfiles = **
990 > EOF
990 > EOF
991
991
992 $ cat > unknownfile << EOF
992 $ cat > unknownfile << EOF
993 > #!/usr/bin/python
993 > #!/usr/bin/python
994 > def foo():
994 > def foo():
995 > pass
995 > pass
996 > EOF
996 > EOF
997
997
998 $ hg add unknownfile
998 $ hg add unknownfile
999 $ hg commit -m unknown unknownfile
999 $ hg commit -m unknown unknownfile
1000
1000
1001 $ hg serve -p $HGPORT -d -n test --pid-file=hg.pid
1001 $ hg serve -p $HGPORT -d -n test --pid-file=hg.pid
1002 $ cat hg.pid >> $DAEMON_PIDS
1002 $ cat hg.pid >> $DAEMON_PIDS
1003
1003
1004 $ get-with-headers.py localhost:$HGPORT 'file/tip/unknownfile' | grep l2
1004 $ get-with-headers.py localhost:$HGPORT 'file/tip/unknownfile' | grep l2
1005 <span id="l2"><span class="k">def</span> <span class="nf">foo</span><span class="p">():</span></span><a href="#l2"></a>
1005 <span id="l2"><span class="k">def</span> <span class="nf">foo</span><span class="p">():</span></span><a href="#l2"></a>
1006
1006
1007 We can prevent Pygments from falling back to a non filename-based
1007 We can prevent Pygments from falling back to a non filename-based
1008 detection mode
1008 detection mode
1009
1009
1010 $ cat > .hg/hgrc << EOF
1010 $ cat > .hg/hgrc << EOF
1011 > [web]
1011 > [web]
1012 > highlightfiles = **
1012 > highlightfiles = **
1013 > highlightonlymatchfilename = true
1013 > highlightonlymatchfilename = true
1014 > EOF
1014 > EOF
1015
1015
1016 $ killdaemons.py
1016 $ killdaemons.py
1017 $ hg serve -p $HGPORT -d -n test --pid-file=hg.pid
1017 $ hg serve -p $HGPORT -d -n test --pid-file=hg.pid
1018 $ cat hg.pid >> $DAEMON_PIDS
1018 $ cat hg.pid >> $DAEMON_PIDS
1019 $ get-with-headers.py localhost:$HGPORT 'file/tip/unknownfile' | grep l2
1019 $ get-with-headers.py localhost:$HGPORT 'file/tip/unknownfile' | grep l2
1020 <span id="l2">def foo():</span><a href="#l2"></a>
1020 <span id="l2">def foo():</span><a href="#l2"></a>
1021
1021
1022 $ cd ..
1022 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now