39
|
1 # -*- coding: utf-8 -*-
|
|
2 #
|
|
3 # Copyright (C) 2005 Edgewall Software
|
|
4 # Copyright (C) 2005 Christian Boos <cboos@bct-technology.com>
|
|
5 # Copyright (C) 2005 Christopher Lenz <cmlenz@gmx.de>
|
|
6 # All rights reserved.
|
|
7 #
|
|
8 # This software is licensed as described in the file COPYING, which
|
|
9 # you should have received as part of this distribution. The terms
|
|
10 # are also available at http://trac.edgewall.com/license.html.
|
|
11 #
|
|
12 # This software consists of voluntary contributions made by many
|
|
13 # individuals. For the exact contribution history, see the revision
|
|
14 # history and logs, available at http://projects.edgewall.com/trac/.
|
|
15 #
|
|
16 # Author: Christian Boos <cboos@bct-technology.com>
|
|
17 # Christopher Lenz <cmlenz@gmx.de>
|
|
18
|
|
19 import re
|
|
20
|
|
21 from trac.core import *
|
|
22 from trac.config import Option
|
|
23 from trac.mimeview.api import IHTMLPreviewRenderer, content_to_unicode
|
|
24 from trac.util import NaivePopen
|
|
25 from trac.util.markup import Deuglifier
|
|
26
|
|
27 __all__ = ['PHPRenderer']
|
|
28
|
|
29 php_types = ('text/x-php', 'application/x-httpd-php',
|
|
30 'application/x-httpd-php4', 'application/x-httpd-php1')
|
|
31
|
|
32
|
|
33 class PhpDeuglifier(Deuglifier):
|
|
34
|
|
35 def format(self, indata):
|
|
36 # The PHP highlighter produces the end-span tags on the next line
|
|
37 # instead of the line they actually apply to, which causes
|
|
38 # Trac to produce lots of (useless) open-and-immediately-close
|
|
39 # spans beginning each line. This tries to curtail by bubbling
|
|
40 # the first span after a set of 1+ "<br />" to before them.
|
|
41 r_fixeol = re.compile(r"((?:<br />)+)(</(?:font|span)>)")
|
|
42 indata = r_fixeol.sub(lambda m: m.group(2) + m.group(1), indata)
|
|
43
|
|
44 # Now call superclass implementation that handles the dirty work
|
|
45 # of applying css classes.
|
|
46 return Deuglifier.format(self, indata)
|
|
47
|
|
48 def rules(cls):
|
|
49 colors = dict(comment='FF8000', lang='0000BB', keyword='007700',
|
|
50 string='DD0000')
|
|
51 # rules check for <font> for PHP 4 or <span> for PHP 5
|
|
52 color_rules = [
|
|
53 r'(?P<%s><(?:font color="|span style="color: )#%s">)' % c
|
|
54 for c in colors.items()
|
|
55 ]
|
|
56 return color_rules + [ r'(?P<font><font.*?>)', r'(?P<endfont></font>)' ]
|
|
57 rules = classmethod(rules)
|
|
58
|
|
59 class PHPRenderer(Component):
|
|
60 """
|
|
61 Syntax highlighting using the PHP executable if available.
|
|
62 """
|
|
63
|
|
64 implements(IHTMLPreviewRenderer)
|
|
65
|
|
66 path = Option('mimeviewer', 'php_path', 'php',
|
|
67 """Path to the PHP executable (''since 0.9'').""")
|
|
68
|
|
69 # IHTMLPreviewRenderer methods
|
|
70
|
|
71 def get_quality_ratio(self, mimetype):
|
|
72 if mimetype in php_types:
|
|
73 return 4
|
|
74 return 0
|
|
75
|
|
76 def render(self, req, mimetype, content, filename=None, rev=None):
|
|
77 cmdline = self.config.get('mimeviewer', 'php_path')
|
|
78 # -n to ignore php.ini so we're using default colors
|
|
79 cmdline += ' -sn'
|
|
80 self.env.log.debug("PHP command line: %s" % cmdline)
|
|
81
|
|
82 content = content_to_unicode(self.env, content, mimetype)
|
|
83 content = content.encode('utf-8')
|
|
84 np = NaivePopen(cmdline, content, capturestderr=1)
|
|
85 if np.errorlevel or np.err:
|
|
86 err = 'Running (%s) failed: %s, %s.' % (cmdline, np.errorlevel,
|
|
87 np.err)
|
|
88 raise Exception, err
|
|
89 odata = ''.join(np.out.splitlines()[1:-2])
|
|
90 if odata.startswith('X-Powered-By'):
|
|
91 raise TracError, 'You appear to be using the PHP CGI binary. ' \
|
|
92 'Trac requires the CLI version for syntax ' \
|
|
93 'highlighting.'
|
|
94
|
|
95 html = PhpDeuglifier().format(odata.decode('utf-8'))
|
|
96 for line in html.split('<br />'):
|
|
97 # PHP generates _way_ too many non-breaking spaces...
|
|
98 # We don't need them anyway, so replace them by normal spaces
|
|
99 yield line.replace(' ', ' ')
|