|
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) |