comparison hgext/highlight.py @ 6193:2344da8eb9b4

highlight: support annotate, and reduce layering violations.
author Brendan Cully <brendan@kublai.com>
date Thu, 28 Feb 2008 21:35:27 -0800
parents 31726c27e40e
children fe54e7501de1
comparison
equal deleted inserted replaced
6192:cd65a67aff31 6193:2344da8eb9b4
36 from mercurial.hgweb import hgweb_mod 36 from mercurial.hgweb import hgweb_mod
37 from mercurial.hgweb.hgweb_mod import hgweb 37 from mercurial.hgweb.hgweb_mod import hgweb
38 from mercurial import util 38 from mercurial import util
39 from mercurial.hgweb.common import paritygen 39 from mercurial.hgweb.common import paritygen
40 from mercurial.node import hex 40 from mercurial.node import hex
41 from mercurial.templatefilters import filters
41 42
42 from pygments import highlight 43 from pygments import highlight
43 from pygments.util import ClassNotFound 44 from pygments.util import ClassNotFound
44 from pygments.lexers import guess_lexer_for_filename, TextLexer 45 from pygments.lexers import guess_lexer_for_filename, TextLexer
45 from pygments.formatters import HtmlFormatter 46 from pygments.formatters import HtmlFormatter
46 47
47 SYNTAX_CSS = ('\n<link rel="stylesheet" href="#staticurl#highlight.css" ' 48 SYNTAX_CSS = ('\n<link rel="stylesheet" href="#staticurl#highlight.css" '
48 'type="text/css" />') 49 'type="text/css" />')
49 50
50 class StripedHtmlFormatter(HtmlFormatter): 51 def pygmentize(self, tmpl, fctx, field):
51 def __init__(self, stripecount, *args, **kwargs):
52 super(StripedHtmlFormatter, self).__init__(*args, **kwargs)
53 self.stripecount = stripecount
54
55 def wrap(self, source, outfile):
56 yield 0, "<div class='highlight'>"
57 yield 0, "<pre>"
58 parity = paritygen(self.stripecount)
59
60 for n, i in source:
61 if n == 1:
62 i = "<div class='parity%s'>%s</div>" % (parity.next(), i)
63 yield n, i
64
65 yield 0, "</pre>"
66 yield 0, "</div>"
67
68
69 def pygments_format(filename, text, forcetext, stripecount, style):
70 if not forcetext:
71 try:
72 lexer = guess_lexer_for_filename(filename, text,
73 encoding=util._encoding)
74 except ClassNotFound:
75 lexer = TextLexer(encoding=util._encoding)
76 else:
77 lexer = TextLexer(encoding=util._encoding)
78
79 formatter = StripedHtmlFormatter(stripecount, style=style,
80 linenos='inline', encoding=util._encoding)
81
82 return highlight(text, lexer, formatter)
83
84
85 def filerevision_pygments(self, tmpl, fctx):
86 """Reimplement hgweb.filerevision to use syntax highlighting"""
87 f = fctx.path()
88 text = fctx.data()
89 fl = fctx.filelog()
90 n = fctx.filenode()
91
92 if util.binary(text):
93 mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
94 text = "(binary:%s)" % mt
95 # don't parse (binary:...) as anything
96 forcetext = True
97 else:
98 # encode to hgweb.encoding for lexers and formatter
99 util._encoding = self.encoding
100 text = util.tolocal(text)
101 forcetext = False
102
103 def lines(text):
104 for line in text.splitlines(True):
105 yield {"line": line}
106
107 style = self.config("web", "pygments_style", "colorful")
108
109 text_formatted = lines(pygments_format(f, text, forcetext,
110 self.stripecount, style))
111
112 # override per-line template
113 tmpl.cache['fileline'] = '#line#'
114
115 # append a <link ...> to the syntax highlighting css 52 # append a <link ...> to the syntax highlighting css
116 old_header = ''.join(tmpl('header')) 53 old_header = ''.join(tmpl('header'))
117 if SYNTAX_CSS not in old_header: 54 if SYNTAX_CSS not in old_header:
118 new_header = old_header + SYNTAX_CSS 55 new_header = old_header + SYNTAX_CSS
119 tmpl.cache['header'] = new_header 56 tmpl.cache['header'] = new_header
120 57
121 return tmpl("filerevision", 58 style = self.config("web", "pygments_style", "colorful")
122 file=f, 59 # To get multi-line strings right, we can't format line-by-line
123 path=hgweb_mod._up(f), # fixme: make public 60 text = fctx.data()
124 text=text_formatted, 61 try:
125 rev=fctx.rev(), 62 lexer = guess_lexer_for_filename(fctx.path(), text,
126 node=hex(fctx.node()), 63 encoding=util._encoding)
127 author=fctx.user(), 64 except ClassNotFound:
128 date=fctx.date(), 65 lexer = TextLexer(encoding=util._encoding)
129 desc=fctx.description(),
130 parent=self.siblings(fctx.parents()),
131 child=self.siblings(fctx.children()),
132 rename=self.renamelink(fl, n),
133 permissions=fctx.manifest().flags(f))
134 66
67 formatter = HtmlFormatter(style=style, encoding=util._encoding)
68
69 colorized = highlight(text, lexer, formatter)
70 # strip wrapping div
71 colorized = colorized[:colorized.find('\n</pre>')]
72 colorized = colorized[colorized.find('<span'):]
73 coloriter = (l for l in colorized.splitlines())
74
75 filters['colorize'] = lambda x: coloriter.next()
76
77 oldl = tmpl.cache[field]
78 newl = oldl.replace('line|escape', 'line|colorize')
79 tmpl.cache[field] = newl
80
81 def filerevision_highlight(self, tmpl, fctx):
82 pygmentize(self, tmpl, fctx, 'fileline')
83
84 return realrevision(self, tmpl, fctx)
85
86 def fileannotate_highlight(self, tmpl, fctx):
87 pygmentize(self, tmpl, fctx, 'annotateline')
88
89 return realannotate(self, tmpl, fctx)
135 90
136 # monkeypatch in the new version 91 # monkeypatch in the new version
137 # should be safer than overriding the method in a derived class 92 # should be safer than overriding the method in a derived class
138 # and then patching the class 93 # and then patching the class
139 hgweb.filerevision = filerevision_pygments 94 realrevision = hgweb.filerevision
95 hgweb.filerevision = filerevision_highlight
96 realannotate = hgweb.fileannotate
97 hgweb.fileannotate = fileannotate_highlight