src/pyams_utils/pygments.py
changeset 289 c8e21d7dd685
child 318 12e519cc367c
equal deleted inserted replaced
-1:000000000000 289:c8e21d7dd685
       
     1 #
       
     2 # Copyright (c) 2008-2018 Thierry Florac <tflorac AT ulthar.net>
       
     3 # All Rights Reserved.
       
     4 #
       
     5 # This software is subject to the provisions of the Zope Public License,
       
     6 # Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
       
     7 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
       
     8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
       
     9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
       
    10 # FOR A PARTICULAR PURPOSE.
       
    11 #
       
    12 
       
    13 __docformat__ = 'restructuredtext'
       
    14 
       
    15 from fanstatic import get_library_registry
       
    16 from persistent import Persistent
       
    17 from pygments import highlight
       
    18 from pygments.formatters.html import HtmlFormatter
       
    19 from pygments.lexers import get_all_lexers, get_lexer_by_name, guess_lexer
       
    20 from pygments.styles import get_all_styles
       
    21 from pyramid.response import Response
       
    22 from pyramid.view import view_config
       
    23 from zope.container.contained import Contained
       
    24 from zope.interface import Interface, implementer
       
    25 from zope.schema import Bool, Choice
       
    26 from zope.schema.fieldproperty import FieldProperty
       
    27 from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary
       
    28 
       
    29 from pyams_utils.factory import factory_config
       
    30 from pyams_utils.fanstatic import ExternalResource
       
    31 from pyams_utils.list import unique_iter
       
    32 from pyams_utils.vocabulary import vocabulary_config
       
    33 
       
    34 from pyams_utils import _
       
    35 
       
    36 
       
    37 #
       
    38 # Pygments CSS view
       
    39 #
       
    40 
       
    41 for library in get_library_registry().values():
       
    42     break
       
    43 else:
       
    44     try:
       
    45         from pyams_skin import library
       
    46     except ImportError:
       
    47         from pyams_default_theme import library
       
    48 
       
    49 
       
    50 pygments_css = ExternalResource(library, 'get-pygments-style.css', resource_type='css')
       
    51 
       
    52 
       
    53 @view_config(name='get-pygments-style.css')
       
    54 def get_pygments_style_view(request):
       
    55     style = request.params.get('style', 'default')
       
    56     styles = HtmlFormatter(linenos='inline',
       
    57                            nowrap=False,
       
    58                            cssclass='source',
       
    59                            style=style).get_style_defs()
       
    60     return Response(styles, content_type='text/css')
       
    61 
       
    62 
       
    63 #
       
    64 # Pygments lexers
       
    65 #
       
    66 
       
    67 PYGMENTS_LEXERS_VOCABULARY = 'Pygments lexers vocabulary'
       
    68 
       
    69 
       
    70 @vocabulary_config(name=PYGMENTS_LEXERS_VOCABULARY)
       
    71 class PygmentsLexersVocabulary(SimpleVocabulary):
       
    72     """Pygments lexers vocabulary"""
       
    73 
       
    74     def __init__(self, context):
       
    75         terms = [SimpleTerm('auto', title=_("Automatic detection"))]
       
    76         for name, aliases, filetypes, mimetypes in sorted(unique_iter(get_all_lexers(),
       
    77                                                                       key=lambda x: x[0].lower()),
       
    78                                                           key=lambda x: x[0].lower()):
       
    79             terms.append(SimpleTerm(aliases[0] if len(aliases) > 0 else name,
       
    80                                     title='{0}{1}'.format(name,
       
    81                                                           ' ({})'.format(', '.join(filetypes)) if filetypes else '')))
       
    82         super(PygmentsLexersVocabulary, self).__init__(terms)
       
    83 
       
    84 
       
    85 PYGMENTS_STYLES_VOCABULARY = 'Pygments styles vocabulary'
       
    86 
       
    87 
       
    88 @vocabulary_config(name=PYGMENTS_STYLES_VOCABULARY)
       
    89 class PygmentsStylesVocabulary(SimpleVocabulary):
       
    90     """Pygments styles vocabulary"""
       
    91 
       
    92     def __init__(self, context):
       
    93         terms = []
       
    94         for name in sorted(get_all_styles()):
       
    95             terms.append(SimpleTerm(name))
       
    96         super(PygmentsStylesVocabulary, self).__init__(terms)
       
    97 
       
    98 
       
    99 #
       
   100 # Pygments configuration
       
   101 #
       
   102 
       
   103 class IPygmentsCodeConfiguration(Interface):
       
   104     """Pygments html formatter options"""
       
   105 
       
   106     lexer = Choice(title=_("Selected lexer"),
       
   107                    description=_("Lexer used to format source code"),
       
   108                    required=True,
       
   109                    vocabulary=PYGMENTS_LEXERS_VOCABULARY,
       
   110                    default='auto')
       
   111 
       
   112     display_linenos = Bool(title=_("Display line numbers?"),
       
   113                            description=_("If 'no', line numbers will be hidden"),
       
   114                            required=True,
       
   115                            default=True)
       
   116 
       
   117     disable_wrap = Bool(title=_("Lines wrap?"),
       
   118                         description=_("If 'yes', lines wraps will be enabled; line numbers will not be "
       
   119                                       "displayed if lines wrap is enabled..."),
       
   120                         required=True,
       
   121                         default=False)
       
   122 
       
   123     style = Choice(title=_("Color style"),
       
   124                    description=_("Selected color style"),
       
   125                    required=True,
       
   126                    vocabulary=PYGMENTS_STYLES_VOCABULARY,
       
   127                    default='default')
       
   128 
       
   129 
       
   130 @factory_config(provided=IPygmentsCodeConfiguration)
       
   131 @implementer(IPygmentsCodeConfiguration)
       
   132 class PygmentsCodeRendererSettings(Persistent, Contained):
       
   133     """Pygments code renderer settings"""
       
   134 
       
   135     lexer = FieldProperty(IPygmentsCodeConfiguration['lexer'])
       
   136     display_linenos = FieldProperty(IPygmentsCodeConfiguration['display_linenos'])
       
   137     disable_wrap = FieldProperty(IPygmentsCodeConfiguration['disable_wrap'])
       
   138     style = FieldProperty(IPygmentsCodeConfiguration['style'])
       
   139 
       
   140 
       
   141 def render_source(code: str, settings: IPygmentsCodeConfiguration):
       
   142     """Render source with given settings"""
       
   143     if settings.lexer == 'auto':
       
   144         lexer = guess_lexer(code)
       
   145     else:
       
   146         lexer = get_lexer_by_name(settings.lexer)
       
   147     if lexer is not None:
       
   148         formatter = HtmlFormatter(linenos='inline' if settings.display_linenos else None,
       
   149                                   nowrap=settings.disable_wrap,
       
   150                                   cssclass='source',
       
   151                                   style=settings.style)
       
   152         return highlight(code, lexer, formatter)