##// END OF EJS Templates
highlight: pass encoding to lexers and formatter...
Christian Ebert -
r5655:fe38b0a3 default
parent child Browse files
Show More
@@ -1,144 +1,147 b''
1 """
1 """
2 This is Mercurial extension for syntax highlighting in the file
2 This is Mercurial extension for syntax highlighting in the file
3 revision view of hgweb.
3 revision view of hgweb.
4
4
5 It depends on the pygments syntax highlighting library:
5 It depends on the pygments syntax highlighting library:
6 http://pygments.org/
6 http://pygments.org/
7
7
8 To enable the extension add this to hgrc:
8 To enable the extension add this to hgrc:
9
9
10 [extensions]
10 [extensions]
11 hgext.highlight =
11 hgext.highlight =
12
12
13 There is a single configuration option:
13 There is a single configuration option:
14
14
15 [web]
15 [web]
16 pygments_style = <style>
16 pygments_style = <style>
17
17
18 The default is 'colorful'. If this is changed the corresponding CSS
18 The default is 'colorful'. If this is changed the corresponding CSS
19 file should be re-generated by running
19 file should be re-generated by running
20
20
21 # pygmentize -f html -S <newstyle>
21 # pygmentize -f html -S <newstyle>
22
22
23
23
24 -- Adam Hupp <adam@hupp.org>
24 -- Adam Hupp <adam@hupp.org>
25
25
26
26
27 """
27 """
28
28
29 from mercurial import demandimport
29 from mercurial import demandimport
30 demandimport.ignore.extend(['pkgutil',
30 demandimport.ignore.extend(['pkgutil',
31 'pkg_resources',
31 'pkg_resources',
32 '__main__',])
32 '__main__',])
33
33
34 import mimetypes
34 import mimetypes
35
35
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
41
42 from pygments import highlight
42 from pygments import highlight
43 from pygments.util import ClassNotFound
43 from pygments.util import ClassNotFound
44 from pygments.lexers import guess_lexer_for_filename, TextLexer
44 from pygments.lexers import guess_lexer_for_filename, TextLexer
45 from pygments.formatters import HtmlFormatter
45 from pygments.formatters import HtmlFormatter
46
46
47 SYNTAX_CSS = ('\n<link rel="stylesheet" href="#staticurl#highlight.css" '
47 SYNTAX_CSS = ('\n<link rel="stylesheet" href="#staticurl#highlight.css" '
48 'type="text/css" />')
48 'type="text/css" />')
49
49
50 class StripedHtmlFormatter(HtmlFormatter):
50 class StripedHtmlFormatter(HtmlFormatter):
51 def __init__(self, stripecount, *args, **kwargs):
51 def __init__(self, stripecount, *args, **kwargs):
52 super(StripedHtmlFormatter, self).__init__(*args, **kwargs)
52 super(StripedHtmlFormatter, self).__init__(*args, **kwargs)
53 self.stripecount = stripecount
53 self.stripecount = stripecount
54
54
55 def wrap(self, source, outfile):
55 def wrap(self, source, outfile):
56 yield 0, "<div class='highlight'>"
56 yield 0, "<div class='highlight'>"
57 yield 0, "<pre>"
57 yield 0, "<pre>"
58 parity = paritygen(self.stripecount)
58 parity = paritygen(self.stripecount)
59
59
60 for n, i in source:
60 for n, i in source:
61 if n == 1:
61 if n == 1:
62 i = "<div class='parity%s'>%s</div>" % (parity.next(), i)
62 i = "<div class='parity%s'>%s</div>" % (parity.next(), i)
63 yield n, i
63 yield n, i
64
64
65 yield 0, "</pre>"
65 yield 0, "</pre>"
66 yield 0, "</div>"
66 yield 0, "</div>"
67
67
68
68
69 def pygments_format(filename, rawtext, forcetext, stripecount, style):
69 def pygments_format(filename, rawtext, forcetext, encoding,
70 stripecount, style):
71 etext = util.tolocal(rawtext)
70 if not forcetext:
72 if not forcetext:
71 try:
73 try:
72 lexer = guess_lexer_for_filename(filename, rawtext)
74 lexer = guess_lexer_for_filename(filename, etext,
75 encoding=util._encoding)
73 except ClassNotFound:
76 except ClassNotFound:
74 lexer = TextLexer()
77 lexer = TextLexer(encoding=util._encoding)
75 else:
78 else:
76 lexer = TextLexer()
79 lexer = TextLexer(encoding=util._encoding)
77
80
78 formatter = StripedHtmlFormatter(stripecount, style=style,
81 formatter = StripedHtmlFormatter(stripecount, style=style,
79 linenos='inline')
82 linenos='inline', encoding=encoding)
80
83
81 return highlight(rawtext, lexer, formatter)
84 return highlight(etext, lexer, formatter)
82
85
83
86
84 def filerevision_pygments(self, tmpl, fctx):
87 def filerevision_pygments(self, tmpl, fctx):
85 """Reimplement hgweb.filerevision to use syntax highlighting"""
88 """Reimplement hgweb.filerevision to use syntax highlighting"""
86 f = fctx.path()
89 f = fctx.path()
87
90
88 rawtext = fctx.data()
91 rawtext = fctx.data()
89 text = rawtext
92 text = rawtext
90
93
91 fl = fctx.filelog()
94 fl = fctx.filelog()
92 n = fctx.filenode()
95 n = fctx.filenode()
93
96
94 mt = mimetypes.guess_type(f)[0]
97 mt = mimetypes.guess_type(f)[0]
95
98
96 if util.binary(text):
99 if util.binary(text):
97 mt = mt or 'application/octet-stream'
100 mt = mt or 'application/octet-stream'
98 text = "(binary:%s)" % mt
101 text = "(binary:%s)" % mt
99
102
100 # don't parse (binary:...) as anything
103 # don't parse (binary:...) as anything
101 forcetext = True
104 forcetext = True
102 else:
105 else:
103 mt = mt or 'text/plain'
106 mt = mt or 'text/plain'
104 forcetext = False
107 forcetext = False
105
108
106 def lines(text):
109 def lines(text):
107 for line in text.splitlines(True):
110 for line in text.splitlines(True):
108 yield {"line": line}
111 yield {"line": line}
109
112
110 style = self.config("web", "pygments_style", "colorful")
113 style = self.config("web", "pygments_style", "colorful")
111
114
112 text_formatted = lines(pygments_format(f, text, forcetext,
115 text_formatted = lines(pygments_format(f, text, forcetext, self.encoding,
113 self.stripecount, style))
116 self.stripecount, style))
114
117
115 # override per-line template
118 # override per-line template
116 tmpl.cache['fileline'] = '#line#'
119 tmpl.cache['fileline'] = '#line#'
117
120
118 # append a <link ...> to the syntax highlighting css
121 # append a <link ...> to the syntax highlighting css
119 old_header = ''.join(tmpl('header'))
122 old_header = ''.join(tmpl('header'))
120 if SYNTAX_CSS not in old_header:
123 if SYNTAX_CSS not in old_header:
121 new_header = old_header + SYNTAX_CSS
124 new_header = old_header + SYNTAX_CSS
122 tmpl.cache['header'] = new_header
125 tmpl.cache['header'] = new_header
123
126
124 yield tmpl("filerevision",
127 yield tmpl("filerevision",
125 file=f,
128 file=f,
126 path=hgweb_mod._up(f), # fixme: make public
129 path=hgweb_mod._up(f), # fixme: make public
127 text=text_formatted,
130 text=text_formatted,
128 raw=rawtext,
131 raw=rawtext,
129 mimetype=mt,
132 mimetype=mt,
130 rev=fctx.rev(),
133 rev=fctx.rev(),
131 node=hex(fctx.node()),
134 node=hex(fctx.node()),
132 author=fctx.user(),
135 author=fctx.user(),
133 date=fctx.date(),
136 date=fctx.date(),
134 desc=fctx.description(),
137 desc=fctx.description(),
135 parent=self.siblings(fctx.parents()),
138 parent=self.siblings(fctx.parents()),
136 child=self.siblings(fctx.children()),
139 child=self.siblings(fctx.children()),
137 rename=self.renamelink(fl, n),
140 rename=self.renamelink(fl, n),
138 permissions=fctx.manifest().flags(f))
141 permissions=fctx.manifest().flags(f))
139
142
140
143
141 # monkeypatch in the new version
144 # monkeypatch in the new version
142 # should be safer than overriding the method in a derived class
145 # should be safer than overriding the method in a derived class
143 # and then patching the class
146 # and then patching the class
144 hgweb.filerevision = filerevision_pygments
147 hgweb.filerevision = filerevision_pygments
General Comments 0
You need to be logged in to leave comments. Login now