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