Edgewall Software

Ticket #4246: trac-pygments.3.diff

File trac-pygments.3.diff, 29.4 KB (added by cmlenz, 2 years ago)

New patch: moves Pygments support into Trac proper (replacing the plugin)

  • htdocs/css/code.css

     
    5050table.code tbody th :link:hover, table.code tbody th :visited:hover { 
    5151 color: #000; 
    5252} 
    53 table.code tbody td { 
    54  background: #fff; 
     53table.code td { 
    5554 font: normal 11px monospace; 
    5655 overflow: hidden; 
    5756 padding: 1px 2px; 
    5857 vertical-align: top; 
    5958} 
    60 table.code tbody tr.hilite th { 
     59table.code tr.hilite th { 
    6160 background: #ccf; 
    6261} 
    63 table.code tbody tr.hilite td { 
     62table.code tr.hilite td { 
    6463 background: #ddf; 
    6564} 
    6665.image-file { background: #eee; padding: .3em } 
    6766.image-file img { background: url(../imggrid.png) } 
    6867 
    6968/* Default */ 
    70 .code-block span { 
    71  font-family: monospace; 
    72 } 
     69.code-block span { font-family: monospace; } 
    7370 
    7471/* Comments */ 
    7572.code-comment, .css_comment, .c_comment, .c_commentdoc, .c_commentline, 
  • trac/db_default.py

     
    396396                      'trac.attachment', 'trac.db.mysql_backend', 
    397397                      'trac.db.postgres_backend', 'trac.db.sqlite_backend', 
    398398                      'trac.mimeview.enscript', 'trac.mimeview.patch', 
    399                       'trac.mimeview.php', 'trac.mimeview.rst', 
    400                       'trac.mimeview.silvercity', 'trac.mimeview.txtl', 
     399                      'trac.mimeview.php', 'trac.mimeview.pygment', 
     400                      'trac.mimeview.rst', 'trac.mimeview.silvercity', 
     401                      'trac.mimeview.txtl', 
    401402                      'trac.prefs.web_ui', 'trac.search.web_ui', 
    402403                      'trac.ticket.admin', 'trac.ticket.query', 
    403404                      'trac.ticket.report', 'trac.ticket.roadmap', 
  • trac/mimeview/api.py

     
    5959import re 
    6060from StringIO import StringIO 
    6161 
    62 from genshi.core import escape, Markup, Stream 
     62from genshi import escape, Markup, Stream 
     63from genshi.core import TEXT, START, END, START_NS, END_NS 
    6364from genshi.builder import Fragment, tag 
     65from genshi.input import HTMLParser 
    6466 
    6567from trac.config import IntOption, ListOption, Option 
    6668from trac.core import * 
     
    230232    # support text content where Trac should expand tabs into spaces 
    231233    expand_tabs = False 
    232234 
     235    # indicate whether the output of this renderer is source code that can 
     236    # be decorated with annotations 
     237    returns_source = False 
     238 
    233239    def get_quality_ratio(mimetype): 
    234240        """Return the level of support this renderer provides for the `content` 
    235241        of the specified MIME type. The return value must be a number between 
     
    256262        be considered to correspond to lines of text in the original content. 
    257263        """ 
    258264 
     265 
    259266class IHTMLPreviewAnnotator(Interface): 
    260267    """Extension point interface for components that can annotate an XHTML 
    261268    representation of file contents with additional information.""" 
     
    268275        let the user toggle the appearance of the annotation type. 
    269276        """ 
    270277 
    271     def annotate_line(number, content): 
     278    def annotate_row(number, content): 
    272279        """Return the XHTML markup for the table cell that contains the 
    273280        annotation data.""" 
    274281 
     
    438445            try: 
    439446                self.log.debug('Trying to render HTML preview using %s' 
    440447                               % renderer.__class__.__name__) 
     448 
    441449                # check if we need to perform a tab expansion 
    442450                rendered_content = content 
    443451                if getattr(renderer, 'expand_tabs', False): 
     
    446454                                                     full_mimetype) 
    447455                        expanded_content = content.expandtabs(self.tab_width) 
    448456                    rendered_content = expanded_content 
     457 
    449458                result = renderer.render(req, full_mimetype, rendered_content, 
    450459                                         filename, url) 
    451460                if not result: 
    452461                    continue 
    453                 elif isinstance(result, (Fragment, Stream)): 
    454                     return result 
    455                 elif isinstance(result, basestring): 
    456                     return Markup(to_unicode(result)) 
    457                 elif annotations: 
     462 
     463                if not getattr(renderer, 'returns_source', False): 
     464                    if isinstance(result, basestring): 
     465                        if not isinstance(result, unicode): 
     466                            result = to_unicode(result) 
     467                        return Markup(to_unicode(result)) 
     468                    elif isinstance(result, Fragment): 
     469                        return result.generate() 
     470                    else: 
     471                        return result 
     472 
     473                if annotations: 
    458474                    m = req.args.get('marks') 
    459                     return Markup(self._annotate(result, annotations, 
    460                                                  m and Ranges(m))) 
     475                    return self._annotate(result, annotations, m and Ranges(m)) 
    461476                else: 
    462                     buf = StringIO() 
    463                     buf.write('<div class="code"><pre>') 
    464                     for line in result: 
    465                         buf.write(line + '\n') 
    466                     buf.write('</pre></div>') 
    467                     return Markup(buf.getvalue()) 
     477                    return tag.div(class_='code')(tag.pre(result)).generate() 
     478 
    468479            except Exception, e: 
    469480                self.log.warning('HTML preview using %s failed (%s)' 
    470481                                 % (renderer, e), exc_info=True) 
    471482 
    472     def _annotate(self, lines, annotations, marks=None): 
    473         buf = StringIO() 
    474         buf.write('<table class="code"><thead><tr>') 
    475         annotators = [] 
     483    def _annotate(self, stream, annotations, marks=None): 
     484        annotators, annotypes = [], [] 
    476485        for annotator in self.annotators: 
    477             atype, alabel, adesc = annotator.get_annotation_type() 
     486            atype, alabel, _ = annotator.get_annotation_type() 
    478487            if atype in annotations: 
    479                 buf.write('<th class="%s">%s</th>' % (atype, alabel)) 
     488                annotypes.append((atype, alabel)) 
    480489                annotators.append(annotator) 
    481         buf.write('<th class="content">&nbsp;</th>') 
    482         buf.write('</tr></thead><tbody>') 
    483490 
    484         space_re = re.compile('(?P<spaces> (?: +))|' 
    485                               '^(?P<tag><\w+.*?>)?( )') 
    486         def htmlify(match): 
    487             m = match.group('spaces') 
    488             if m: 
    489                 div, mod = divmod(len(m), 2) 
    490                 return div * '&nbsp; ' + mod * '&nbsp;' 
    491             return (match.group('tag') or '') + '&nbsp;' 
     491        if isinstance(stream, list): 
     492            stream = HTMLParser(StringIO('\n'.join(stream))) 
    492493 
    493         num = -1 
    494         for num, line in enumerate(_html_splitlines(lines)): 
    495             cells = [] 
    496             for annotator in annotators: 
    497                 cells.append(annotator.annotate_line(num + 1, line)) 
    498             cells.append('<td>%s</td>\n' % space_re.sub(htmlify, line)) 
    499             if marks and num+1 in marks: 
    500                 buf.write('<tr class="%s">' % ('hilite',) + 
    501                           '\n'.join(cells) + '</tr>') 
    502             else: 
    503                 buf.write('<tr>' + '\n'.join(cells) + '</tr>') 
    504         else: 
    505             if num < 0: 
    506                 return '' 
    507         buf.write('</tbody></table>') 
    508         return buf.getvalue() 
     494        def _head_row(): 
     495            return tag.re( 
     496                [tag.th(alabel, class_=atype) for atype, alabel in annotypes] + 
     497                [tag.th(u'\xa0', class_='content')] 
     498            ) 
    509499 
     500        def _body_rows(): 
     501            for idx, line in enumerate(_group_lines(stream)): 
     502                row = tag.tr() 
     503                if marks and idx + 1 in marks: 
     504                    row(class_='hilite') 
     505                for annotator in annotators: 
     506                    annotator.annotate_row(row, idx + 1, line) 
     507                row.append(tag.td(line)) 
     508                yield row 
     509 
     510        return tag.table(class_='code')( 
     511            tag.thead(_head_row()), 
     512            tag.tbody(_body_rows()) 
     513        ) 
     514 
    510515    def get_max_preview_size(self): 
    511516        """Deprecated: use `max_preview_size` attribute directly.""" 
    512517        return self.max_preview_size 
     
    616621                                                                   ext)) 
    617622        req.end_headers() 
    618623        req.write(content) 
    619         raise RequestDone         
    620          
     624        raise RequestDone 
    621625 
    622 def _html_splitlines(lines): 
    623     """Tracks open and close tags in lines of HTML text and yields lines that 
    624     have no tags spanning more than one line.""" 
    625     open_tag_re = re.compile(r'<(\w+)(\s.*?)?[^/]?>') 
    626     close_tag_re = re.compile(r'</(\w+)>') 
    627     open_tags = [] 
    628     for line in lines: 
    629         # Reopen tags still open from the previous line 
    630         for tag in open_tags: 
    631             line = tag.group(0) + line 
    632         open_tags = [] 
    633626 
    634         # Find all tags opened on this line 
    635         for tag in open_tag_re.finditer(line): 
    636             open_tags.append(tag) 
     627def _group_lines(stream): 
     628    space_re = re.compile('(?P<spaces> (?: +))|^(?P<tag><\w+.*?>)?( )') 
     629    def pad_spaces(match): 
     630        m = match.group('spaces') 
     631        if m: 
     632            div, mod = divmod(len(m), 2) 
     633            return div * u'\xa0 ' + mod * u'\xa0' 
     634        return (match.group('tag') or '') + u'\xa0' 
    637635 
    638         open_tags.reverse() 
     636    def _generate(): 
     637        stack = [] 
     638        def _reverse(): 
     639            for event in reversed(stack): 
     640                if event[0] is START: 
     641                    yield END, event[1][0], event[2] 
     642                else: 
     643                    yield END_NS, event[1][0], event[2] 
    639644 
    640         # Find all tags closed on this line 
    641         for ctag in close_tag_re.finditer(line): 
    642             for otag in open_tags: 
    643                 if otag.group(1) == ctag.group(1): 
    644                     open_tags.remove(otag) 
    645                     break 
     645        for kind, data, pos in stream: 
     646            if kind is TEXT: 
     647                lines = data.splitlines(True) 
     648                for e in stack: 
     649                    yield e 
     650                yield kind, lines.pop(0).rstrip('\n'), pos 
     651                for e in _reverse(): 
     652                    yield e 
     653                if '\n' in data: 
     654                    yield TEXT, '\n', pos 
     655                    for line in lines: 
     656                        for event in stack: 
     657                            yield event 
     658                        yield kind, line.rstrip('\n'), pos 
     659                        if line.endswith('\n'): 
     660                            for e in _reverse(): 
     661                                yield e 
     662                            yield TEXT, '\n', pos 
     663            else: 
     664                if kind is START or kind is START_NS: 
     665                    stack.append((kind, data, pos)) 
     666                elif kind is END or kind is END_NS: 
     667                    stack.pop() 
     668                else: 
     669                    yield kind, data, pos 
    646670 
    647         # Close all tags still open at the end of line, they'll get reopened at 
    648         # the beginning of the next line 
    649         for tag in open_tags: 
    650             line += '</%s>' % tag.group(1) 
     671    buf = [] 
     672    for kind, data, pos in _generate(): 
     673        if kind is TEXT and data == '\n': 
     674            yield Stream(buf[:]) 
     675            del buf[:] 
     676        else: 
     677            if kind is TEXT: 
     678                data = space_re.sub(pad_spaces, data) 
     679            buf.append((kind, data, pos)) 
     680    if buf: 
     681        yield Stream(buf[:]) 
    651682 
    652         yield line 
    653683 
    654  
    655684# -- Default annotators 
    656685 
    657686class LineNumberAnnotator(Component): 
     
    663692    def get_annotation_type(self): 
    664693        return 'lineno', 'Line', 'Line numbers' 
    665694 
    666     def annotate_line(self, number, content): 
    667         return '<th id="L%s"><a href="#L%s">%s</a></th>' % (number, number, 
    668                                                             number) 
     695    def annotate_row(self, row, lineno, content): 
     696        row.append(tag.th(id='L%s' % lineno)( 
     697            tag.a(lineno, href='#L%s' % lineno) 
     698        )) 
    669699 
    670700 
    671701# -- Default renderers 
     
    677707    implements(IHTMLPreviewRenderer) 
    678708 
    679709    expand_tabs = True 
     710    returns_source = True 
    680711 
    681712    TREAT_AS_BINARY = [ 
    682713        'application/pdf', 
     
    696727 
    697728        self.env.log.debug("Using default plain text mimeviewer") 
    698729        content = content_to_unicode(self.env, content, mimetype) 
    699         for line in content.splitlines(): 
    700             yield escape(line) 
     730        for line in content.splitlines(True): 
     731            yield TEXT, line, (None, -1, -1) 
    701732 
    702733 
    703734class ImageRenderer(Component): 
     
    712743    def render(self, req, mimetype, content, filename=None, url=None): 
    713744        if url: 
    714745            return tag.div(tag.img(src=url, alt=filename), 
    715                            class_="image-file") 
     746                           class_='image-file') 
    716747 
    717748 
    718749class WikiTextRenderer(Component): 
  • trac/mimeview/tests/api.py

     
    1515 
    1616from trac.core import * 
    1717from trac.test import EnvironmentStub 
    18 from trac.mimeview.api import get_mimetype, _html_splitlines, \ 
    19                               Mimeview, IContentConverter 
     18from trac.mimeview.api import get_mimetype, IContentConverter, Mimeview 
    2019 
     20 
    2121class GetMimeTypeTestCase(unittest.TestCase): 
    2222 
    2323    def test_from_suffix_using_MIME_MAP(self): 
     
    5353    def test_from_content_using_is_binary(self): 
    5454        self.assertEqual('application/octet-stream', 
    5555                         get_mimetype('xxx', "abc\0xyz")) 
    56          
    5756 
    58 class Converter0(Component): 
    59     implements(IContentConverter) 
    60     def get_supported_conversions(self): 
    61         yield ('key0', 'Format 0', 'c0', 'text/x-sample', 'text/html', 8) 
    6257 
    63 class Converter2(Component): 
    64     implements(IContentConverter) 
    65     def get_supported_conversions(self): 
    66         yield ('key2', 'Format 2', 'c2', 'text/x-sample', 'text/html', 2) 
    67  
    68 class Converter1(Component): 
    69     implements(IContentConverter) 
    70     def get_supported_conversions(self): 
    71         yield ('key1', 'Format 1', 'c1', 'text/x-sample', 'text/html', 4) 
    72  
    7358class MimeviewTestCase(unittest.TestCase): 
    7459 
    7560    def setUp(self): 
    7661        self.env = EnvironmentStub(default_data=True) 
    7762 
    78     def test_html_splitlines_without_markup(self): 
    79         lines = ['line 1', 'line 2'] 
    80         self.assertEqual(lines, list(_html_splitlines(lines))) 
     63        # Make sure we have no external components hanging around in the 
     64        # component registry 
     65        from trac.core import ComponentMeta 
     66        self.old_registry = ComponentMeta._registry 
     67        ComponentMeta._registry = {} 
    8168 
    82     def test_html_splitlines_with_markup(self): 
    83         lines = ['<p><b>Hi', 'How are you</b></p>'] 
    84         result = list(_html_splitlines(lines)) 
    85         self.assertEqual('<p><b>Hi</b></p>', result[0]) 
    86         self.assertEqual('<p><b>How are you</b></p>', result[1]) 
     69    def tearDown(self): 
     70        # Restore the original component registry 
     71        from trac.core import ComponentMeta 
     72        ComponentMeta._registry = self.old_registry 
    8773 
    88     def test_html_splitlines_with_multiline(self): 
    89         """ 
    90         Regression test for http://trac.edgewall.org/ticket/2655 
    91         """ 
    92         lines = ['<span class="p_tripledouble">"""', 
    93                 'a <a href="http://google.com">http://google.com</a>/', 
    94                 'Test', 'Test', '"""</span>'] 
    95         result = list(_html_splitlines(lines)) 
    96         self.assertEqual('<span class="p_tripledouble">"""</span>', result[0]) 
    97         self.assertEqual('<span class="p_tripledouble">a ' 
    98                          '<a href="http://google.com">http://google.com</a>/' 
    99                          '</span>', result[1]) 
    100         self.assertEqual('<span class="p_tripledouble">Test</span>', result[2]) 
    101         self.assertEqual('<span class="p_tripledouble">Test</span>', result[3]) 
    102         self.assertEqual('<span class="p_tripledouble">"""</span>', result[4]) 
    103  
    10474    def test_get_supported_conversions(self): 
     75        class Converter0(Component): 
     76            implements(IContentConverter) 
     77            def get_supported_conversions(self): 
     78                yield 'key0', 'Format 0', 'c0', 'text/x-sample', 'text/html', 8 
     79 
     80        class Converter2(Component): 
     81            implements(IContentConverter) 
     82            def get_supported_conversions(self): 
     83                yield 'key2', 'Format 2', 'c2', 'text/x-sample', 'text/html', 2 
     84 
     85        class Converter1(Component): 
     86            implements(IContentConverter) 
     87            def get_supported_conversions(self): 
     88                yield 'key1', 'Format 1', 'c1', 'text/x-sample', 'text/html', 4 
     89 
    10590        mimeview = Mimeview(self.env) 
    10691        conversions = mimeview.get_supported_conversions('text/x-sample') 
    10792        self.assertEqual(Converter0(self.env), conversions[0][-1]) 
    10893        self.assertEqual(Converter1(self.env), conversions[1][-1]) 
    10994        self.assertEqual(Converter2(self.env), conversions[2][-1]) 
    11095 
     96 
    11197def suite(): 
    11298    suite = unittest.TestSuite() 
    11399    suite.addTest(unittest.makeSuite(GetMimeTypeTestCase, 'test')) 
  • trac/mimeview/silvercity.py

     
    4141    'text/x-chdr':              ('CPP', 3), 
    4242    'text/x-csrc':              ('CPP', 3), 
    4343    'text/x-perl':              ('Perl', 3), 
    44     'text/x-php':               ('HyperText', 3, {'asp.default.language':4}), 
    45     'application/x-httpd-php':  ('HyperText', 3, {'asp.default.language':4}), 
    46     'application/x-httpd-php4': ('HyperText', 3, {'asp.default.language':4}), 
    47     'application/x-httpd-php3': ('HyperText', 3, {'asp.default.language':4}), 
     44    'text/x-php':               ('HyperText', 3, {'asp.default.language': 4}), 
     45    'application/x-httpd-php':  ('HyperText', 3, {'asp.default.language': 4}), 
     46    'application/x-httpd-php4': ('HyperText', 3, {'asp.default.language': 4}), 
     47    'application/x-httpd-php3': ('HyperText', 3, {'asp.default.language': 4}), 
    4848    'text/x-javascript':        ('CPP', 3), # Kludgy. 
    49     'text/x-psp':               ('HyperText', 3, {'asp.default.language':3}), 
     49    'text/x-psp':               ('HyperText', 3, {'asp.default.language': 3}), 
    5050    'text/x-python':            ('Python', 3), 
    5151    'text/x-ruby':              ('Ruby', 3), 
    5252    'text/x-sql':               ('SQL', 3), 
     
    7676        (''since 0.10'').""") 
    7777 
    7878    expand_tabs = True 
     79    returns_source = True 
    7980 
    8081    def __init__(self): 
    8182        self._types = None 
  • trac/mimeview/pygment.py

    <
     
     1# -*- coding: utf-8 -*- 
     2# 
     3# Copyright (C) 2006 Edgewall Software 
     4# Copyright (C) 2006 Matthew Good <matt@matt-good.net> 
     5# All rights reserved. 
     6# 
     7# This software is licensed as described in the file COPYING, which 
     8# you should have received as part of this distribution. The terms 
     9# are also available at http://trac.edgewall.org/wiki/TracLicense. 
     10# 
     11# Author: Matthew Good <matt@matt-good.net> 
     12 
     13"""Syntax highlighting based on Pygments.""" 
     14 
     15from datetime import datetime 
     16import os 
     17from pkg_resources import resource_filename 
     18import re 
     19 
     20from trac.core import * 
     21from trac.config import ListOption, Option 
     22from trac.mimeview.api import IHTMLPreviewRenderer, Mimeview 
     23from trac.prefs import IPreferencePanelProvider 
     24from trac.util.datefmt import http_date, localtz 
     25from trac.web import IRequestHandler 
     26from trac.web.chrome import add_stylesheet 
     27 
     28from genshi import QName, Stream 
     29from genshi.core import Attrs, START, END, TEXT 
     30 
     31try: 
     32    from pygments import get_lexer_by_name 
     33    from pygments.formatters.html import HtmlFormatter 
     34    from pygments.styles import get_style_by_name 
     35    have_pygments = True 
     36except ImportError, e: 
     37    have_pygments = False 
     38else: 
     39    have_pygments = True 
     40 
     41__all__ = ['PygmentsRenderer'] 
     42 
     43 
     44class PygmentsRenderer(Component): 
     45    """Syntax highlighting based on Pygments.""" 
     46 
     47    implements(IHTMLPreviewRenderer, IPreferencePanelProvider, IRequestHandler) 
     48 
     49    default_style = Option('mimeviewer', 'pygments_default_style', 'trac', 
     50        """The default style to use for Pygments syntax highlighting.""") 
     51 
     52    pygments_modes = ListOption('mimeviewer', 'pygments_modes', 
     53        '', doc= 
     54        """List of additional MIME types known by Pygments. 
     55         
     56        For each, a tuple `mimetype:mode:quality` has to be 
     57        specified, where `mimetype` is the MIME type, 
     58        `mode` is the corresponding Pygments mode to be used 
     59        for the conversion and `quality` is the quality ratio 
     60        associated to this conversion. That can also be used 
     61        to override the default quality ratio used by the 
     62        Pygments render.""") 
     63 
     64    expand_tabs = True 
     65    returns_source = True 
     66 
     67    QUALITY_RATIO = 7 
     68 
     69    EXAMPLE = """<!DOCTYPE html> 
     70<html lang="en"> 
     71  <head> 
     72    <title>Hello, world!</title> 
     73    <script> 
     74      $(document).ready(function() { 
     75        $("h1").fadeIn("slow"); 
     76      }); 
     77    </script> 
     78  </head> 
     79  <body> 
     80    <h1>Hello, world!</h1> 
     81  </body> 
     82</html>""" 
     83 
     84    def __init__(self): 
     85        self.log.debug("Pygments installed? %r", have_pygments) 
     86        self._types = None 
     87 
     88    # IHTMLPreviewRenderer implementation 
     89 
     90    def get_quality_ratio(self, mimetype): 
     91        # Extend default MIME type to mode mappings with configured ones 
     92        if self._types is None: 
     93            self._init_types() 
     94        try: 
     95            return self._types[mimetype][1] 
     96        except KeyError: 
     97            return 0 
     98 
     99    def render(self, req, mimetype, content, filename=None, rev=None): 
     100        if self._types is None: 
     101            self._init_types() 
     102        add_stylesheet(req, '/pygments/%s.css' % 
     103                       req.session.get('pygments_style', self.default_style)) 
     104        try: 
     105            mimetype = mimetype.split(';', 1)[0] 
     106            language = self._types[mimetype][0] 
     107            return self._generate(language, content) 
     108        except (KeyError, ValueError): 
     109            raise Exception("No Pygments lexer found for mime-type '%s'." 
     110                            % mimetype) 
     111 
     112    # IPreferencePanelProvider implementation 
     113 
     114    def get_preference_panels(self, req): 
     115        if have_pygments: 
     116            yield ('pygments', 'Pygments Theme') 
     117 
     118    def render_preference_panel(self, req, panel): 
     119        styles = list(get_all_styles()) 
     120 
     121        if req.method == 'POST': 
     122            style = req.args.get('style') 
     123            if style and style in styles: 
     124                req.session['pygments_style'] = style 
     125            req.redirect(req.href.prefs(panel or None)) 
     126 
     127        output = self._generate('html', self.EXAMPLE) 
     128        return 'prefs_pygments.html', { 
     129            'output': output, 
     130            'selection': req.session.get('pygments_style', self.default_style), 
     131            'styles': styles 
     132        } 
     133 
     134    # IRequestHandler implementation 
     135 
     136    def match_request(self, req): 
     137        if have_pygments: 
     138            match = re.match(r'/pygments/(\w+)\.css', req.path_info) 
     139            if match: 
     140                req.args['style'] = match.group(1) 
     141                return True 
     142 
     143    def process_request(self, req): 
     144        style = req.args['style'] 
     145        try: 
     146            style_cls = get_style_by_name(style) 
     147        except ValueError, e: 
     148            raise HTTPNotFound(e) 
     149 
     150        parts = style_cls.__module__.split('.') 
     151        filename = resource_filename('.'.join(parts[:-1]), parts[-1] + '.py') 
     152        mtime = datetime.fromtimestamp(os.path.getmtime(filename), localtz) 
     153        last_modified = http_date(mtime) 
     154        if last_modified == req.get_header('If-Modified-Since'): 
     155            req.send_response(304) 
     156            req.end_headers() 
     157            return 
     158 
     159        formatter = HtmlFormatter(style=style_cls) 
     160        content = u'\n\n'.join([ 
     161            formatter.get_style_defs('div.code pre'), 
     162            formatter.get_style_defs('table.code td') 
     163        ]).encode('utf-8') 
     164 
     165        req.send_response(200) 
     166        req.send_header('Content-Type', 'text/css; charset=utf-8') 
     167        req.send_header('Last-Modified', last_modified) 
     168        req.send_header('Content-Length', len(content)) 
     169        req.write(content) 
     170 
     171    # Internal methods 
     172 
     173    def _init_types(self): 
     174        self._types = {} 
     175        if have_pygments: 
     176            for _, aliases, _, mimetypes in get_all_lexers(): 
     177                for mimetype in mimetypes: 
     178                    self._types[mimetype] = (aliases[0], self.QUALITY_RATIO) 
     179            self._types.update( 
     180                Mimeview(self.env).configured_modes_mapping('pygments') 
     181            ) 
     182 
     183    def _generate(self, language, content): 
     184        lexer = get_lexer_by_name(language) 
     185        return GenshiHtmlFormatter().generate(lexer.get_tokens(content)) 
     186 
     187 
     188def get_all_lexers(): 
     189    from pygments.lexers._mapping import LEXERS 
     190    from pygments.plugin import find_plugin_lexers 
     191 
     192    for item in LEXERS.itervalues(): 
     193        yield item[1:] 
     194    for cls in find_plugin_lexers(): 
     195        yield cls.name, cls.aliases, cls.filenames, cls.mimetypes 
     196 
     197def get_all_styles(): 
     198    from pygments.styles import find_plugin_styles, STYLE_MAP 
     199    for name in STYLE_MAP: 
     200        yield name 
     201    for name, _ in find_plugin_styles(): 
     202        yield name