# HG changeset patch # User Thierry Florac # Date 1543575839 -3600 # Node ID ad3c56ae5af902171b51d603d93fca1381957167 # Parent d11121d14adde85fb4711fb71ac5ccaca7e06539 Added Pygments utilities diff -r d11121d14add -r ad3c56ae5af9 src/pyams_utils/fanstatic.py --- a/src/pyams_utils/fanstatic.py Fri Nov 23 09:34:34 2018 +0100 +++ b/src/pyams_utils/fanstatic.py Fri Nov 30 12:03:59 2018 +0100 @@ -28,6 +28,8 @@ class ExternalResource(Resource): """Fanstatic external resource""" + dependency_nr = 0 + def __init__(self, library, path, defer=False, resource_type=None, **kwargs): set_resource_file_existence_checking(False) try: diff -r d11121d14add -r ad3c56ae5af9 src/pyams_utils/locales/fr/LC_MESSAGES/pyams_utils.mo Binary file src/pyams_utils/locales/fr/LC_MESSAGES/pyams_utils.mo has changed diff -r d11121d14add -r ad3c56ae5af9 src/pyams_utils/locales/fr/LC_MESSAGES/pyams_utils.po --- a/src/pyams_utils/locales/fr/LC_MESSAGES/pyams_utils.po Fri Nov 23 09:34:34 2018 +0100 +++ b/src/pyams_utils/locales/fr/LC_MESSAGES/pyams_utils.po Fri Nov 30 12:03:59 2018 +0100 @@ -4,7 +4,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE 1.0\n" -"POT-Creation-Date: 2018-02-05 13:53+0100\n" +"POT-Creation-Date: 2018-11-30 11:17+0100\n" "PO-Revision-Date: 2015-01-18 01:01+0100\n" "Last-Translator: Thierry Florac \n" "Language-Team: French\n" @@ -15,94 +15,94 @@ "Plural-Forms: nplurals=2; plural=(n > 1);\n" "Generated-By: Lingua 3.7\n" -#: src/pyams_utils/date.py:90 +#: src/pyams_utils/date.py:94 msgid "%d/%m/%Y" msgstr "%d/%m/%Y" -#: src/pyams_utils/date.py:91 +#: src/pyams_utils/date.py:95 msgid "%d/%m/%Y - %H:%M" msgstr "%d/%m/%Y - %H:%M" -#: src/pyams_utils/date.py:93 +#: src/pyams_utils/date.py:97 msgid "on %d/%m/%Y" msgstr "le %d/%m/%Y" -#: src/pyams_utils/date.py:94 +#: src/pyams_utils/date.py:98 msgid "on %d/%m/%Y at %H:%M" msgstr "le %d/%m/%Y à %H:%M" -#: src/pyams_utils/date.py:152 +#: src/pyams_utils/date.py:156 #, c-format msgid "%d months ago" msgstr "Il y a %d mois" -#: src/pyams_utils/date.py:227 +#: src/pyams_utils/date.py:231 #, c-format msgid "%d months" msgstr "%d mois" -#: src/pyams_utils/date.py:154 +#: src/pyams_utils/date.py:158 #, c-format msgid "%d weeks ago" msgstr "Il y a %d semaines" -#: src/pyams_utils/date.py:229 +#: src/pyams_utils/date.py:233 #, c-format msgid "%d weeks" msgstr "%d semaines" -#: src/pyams_utils/date.py:156 +#: src/pyams_utils/date.py:160 #, c-format msgid "%d days ago" msgstr "Il y a %d jours" -#: src/pyams_utils/date.py:158 +#: src/pyams_utils/date.py:162 msgid "the day before yesterday" msgstr "avant-hier" -#: src/pyams_utils/date.py:231 +#: src/pyams_utils/date.py:235 #, c-format msgid "%d days" msgstr "%d jours" -#: src/pyams_utils/date.py:160 +#: src/pyams_utils/date.py:164 msgid "yesterday" msgstr "hier" -#: src/pyams_utils/date.py:236 +#: src/pyams_utils/date.py:240 msgid "24 hours" msgstr "24 heures" -#: src/pyams_utils/date.py:238 +#: src/pyams_utils/date.py:242 #, c-format msgid "%d day and %d hours" msgstr "%d jours et %d heures" -#: src/pyams_utils/date.py:241 +#: src/pyams_utils/date.py:245 #, c-format msgid "%d hours" msgstr "%d heures" -#: src/pyams_utils/date.py:164 +#: src/pyams_utils/date.py:168 #, c-format msgid "%d hours ago" msgstr "Il y a %d heures" -#: src/pyams_utils/date.py:168 +#: src/pyams_utils/date.py:172 msgid "less than 5 minutes ago" msgstr "Il y a moins de 5 minutes" -#: src/pyams_utils/date.py:245 +#: src/pyams_utils/date.py:249 #, c-format msgid "%d minutes" msgstr "%d minutes" -#: src/pyams_utils/date.py:247 +#: src/pyams_utils/date.py:251 #, c-format msgid "%d seconds" msgstr "%d secondes" -#: src/pyams_utils/date.py:166 +#: src/pyams_utils/date.py:170 #, c-format msgid "%d minutes ago" msgstr "Il y a %d minutes" @@ -512,77 +512,59 @@ "Une couleur ne doit contenir que des valeurs hexadécimales correctes " "(nombres ou lettres de 'A' à 'F')" -#: src/pyams_utils/widget/decimal.py:34 +#: src/pyams_utils/pygments.py:106 +msgid "Selected lexer" +msgstr "Format du code" + +#: src/pyams_utils/pygments.py:107 +msgid "Lexer used to format source code" +msgstr "Lexeur utilisé pour analyser le code source" + +#: src/pyams_utils/pygments.py:112 +msgid "Display line numbers?" +msgstr "Numéros de lignes ?" + +#: src/pyams_utils/pygments.py:113 +msgid "If 'no', line numbers will be hidden" +msgstr "Si 'non', les numéros de lignes ne seront pas affichés" + +#: src/pyams_utils/pygments.py:117 +msgid "Lines wrap?" +msgstr "Sauts de lignes ?" + +#: src/pyams_utils/pygments.py:118 +msgid "" +"If 'yes', lines wraps will be enabled; line numbers will not be displayed if " +"lines wrap is enabled..." +msgstr "" +"Si 'oui', les sauts de lignes seront possibles ; dans ce mode, l'affichage des " +"numéros de lignes n'est pas possible..." + +#: src/pyams_utils/pygments.py:123 +msgid "Color style" +msgstr "Style de couleur" + +#: src/pyams_utils/pygments.py:124 +msgid "Selected color style" +msgstr "Style de mise en forme de la syntaxe" + +#: src/pyams_utils/pygments.py:75 +msgid "Automatic detection" +msgstr "Détection automatique" + +#: src/pyams_utils/widget/decimal.py:31 msgid "The entered value is not a valid decimal literal." msgstr "La valeur saisie n'est pas une valeur décimale correcte." -#: src/pyams_utils/zmi/zeo.py:68 -msgid "Add ZEO connection..." -msgstr "Ajouter une connexion ZEO..." - -#: src/pyams_utils/zmi/zeo.py:78 -msgid "Utilities" -msgstr "Utilitaires" - -#: src/pyams_utils/zmi/zeo.py:79 -msgid "Add ZEO connection" -msgstr "Ajout d'une connexion ZEO" - -#: src/pyams_utils/zmi/zeo.py:125 -msgid "Update ZEO connection properties" -msgstr "Propriétés d'une connexion ZEO" - -#: src/pyams_utils/zmi/zeo.py:153 -msgid "Test ZEO connection..." -msgstr "Tester la connexion ZEO..." - -#: src/pyams_utils/zmi/zeo.py:177 -msgid "Test ZEO database connection" -msgstr "Test de la connexion ZEO" - -#: src/pyams_utils/zmi/zeo.py:163 -msgid "Close" -msgstr "Fermer" - -#: src/pyams_utils/zmi/zeo.py:164 -msgid "Test connection" -msgstr "Tester la connexion" - -#: src/pyams_utils/zmi/zeo.py:104 -msgid "Specified connection name is already used!" -msgstr "Ce nom de connexion est déjà utilisé !" - -#: src/pyams_utils/zmi/zeo.py:107 -msgid "A ZEO connection is already registered with this name!" -msgstr "Une connexion ZEO est déjà enregistrées avec ce nom !" - -#: src/pyams_utils/zmi/zeo.py:60 -#, python-format -msgid "ZEO: {0}" -msgstr "ZEO : {0}" - -#: src/pyams_utils/zmi/zeo.py:123 src/pyams_utils/zmi/zeo.py:175 -#, python-format -msgid "ZEO connection: {0}" -msgstr "Connexion ZEO : {0}" - -#: src/pyams_utils/zmi/timezone.py:38 -msgid "Update server timezone properties" -msgstr "Fuseau horaire du serveur" - -#: src/pyams_utils/zmi/intids.py:46 -msgid "Display indexer properties" -msgstr "Propriétés de l'indexeur" - #: src/pyams_utils/interfaces/text.py:34 msgid "Renderer name" msgstr "Nom de l'outil de rendu" -#: src/pyams_utils/interfaces/inherit.py:38 +#: src/pyams_utils/interfaces/inherit.py:37 msgid "Inherit from parent?" msgstr "Hériter du parent ?" -#: src/pyams_utils/interfaces/inherit.py:42 +#: src/pyams_utils/interfaces/inherit.py:41 msgid "Don't inherit from parent?" msgstr "Ne pas hériter du parent ?" @@ -755,6 +737,45 @@ msgid "Not an iterator" msgstr "Ce n'est pas un itérateur" +#~ msgid "Add ZEO connection..." +#~ msgstr "Ajouter une connexion ZEO..." + +#~ msgid "Utilities" +#~ msgstr "Utilitaires" + +#~ msgid "Add ZEO connection" +#~ msgstr "Ajout d'une connexion ZEO" + +#~ msgid "Update ZEO connection properties" +#~ msgstr "Propriétés d'une connexion ZEO" + +#~ msgid "Test ZEO connection..." +#~ msgstr "Tester la connexion ZEO..." + +#~ msgid "Test ZEO database connection" +#~ msgstr "Test de la connexion ZEO" + +#~ msgid "Close" +#~ msgstr "Fermer" + +#~ msgid "Test connection" +#~ msgstr "Tester la connexion" + +#~ msgid "Specified connection name is already used!" +#~ msgstr "Ce nom de connexion est déjà utilisé !" + +#~ msgid "A ZEO connection is already registered with this name!" +#~ msgstr "Une connexion ZEO est déjà enregistrées avec ce nom !" + +#~ msgid "ZEO: {0}" +#~ msgstr "ZEO : {0}" + +#~ msgid "ZEO connection: {0}" +#~ msgstr "Connexion ZEO : {0}" + +#~ msgid "Update server timezone properties" +#~ msgstr "Fuseau horaire du serveur" + #~ msgid "User name on ZEO server" #~ msgstr "" #~ "Code utilisateur nécessaire lorsque le serveur demande une " diff -r d11121d14add -r ad3c56ae5af9 src/pyams_utils/locales/pyams_utils.pot --- a/src/pyams_utils/locales/pyams_utils.pot Fri Nov 23 09:34:34 2018 +0100 +++ b/src/pyams_utils/locales/pyams_utils.pot Fri Nov 30 12:03:59 2018 +0100 @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE 1.0\n" -"POT-Creation-Date: 2018-02-05 13:53+0100\n" +"POT-Creation-Date: 2018-11-30 11:17+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" @@ -16,94 +16,94 @@ "Content-Transfer-Encoding: 8bit\n" "Generated-By: Lingua 3.10.dev0\n" -#: ./src/pyams_utils/date.py:90 +#: ./src/pyams_utils/date.py:94 msgid "%d/%m/%Y" msgstr "" -#: ./src/pyams_utils/date.py:91 +#: ./src/pyams_utils/date.py:95 msgid "%d/%m/%Y - %H:%M" msgstr "" -#: ./src/pyams_utils/date.py:93 +#: ./src/pyams_utils/date.py:97 msgid "on %d/%m/%Y" msgstr "" -#: ./src/pyams_utils/date.py:94 +#: ./src/pyams_utils/date.py:98 msgid "on %d/%m/%Y at %H:%M" msgstr "" -#: ./src/pyams_utils/date.py:152 +#: ./src/pyams_utils/date.py:156 #, c-format msgid "%d months ago" msgstr "" -#: ./src/pyams_utils/date.py:227 +#: ./src/pyams_utils/date.py:231 #, c-format msgid "%d months" msgstr "" -#: ./src/pyams_utils/date.py:154 +#: ./src/pyams_utils/date.py:158 #, c-format msgid "%d weeks ago" msgstr "" -#: ./src/pyams_utils/date.py:229 +#: ./src/pyams_utils/date.py:233 #, c-format msgid "%d weeks" msgstr "" -#: ./src/pyams_utils/date.py:156 +#: ./src/pyams_utils/date.py:160 #, c-format msgid "%d days ago" msgstr "" -#: ./src/pyams_utils/date.py:158 +#: ./src/pyams_utils/date.py:162 msgid "the day before yesterday" msgstr "" -#: ./src/pyams_utils/date.py:231 +#: ./src/pyams_utils/date.py:235 #, c-format msgid "%d days" msgstr "" -#: ./src/pyams_utils/date.py:160 +#: ./src/pyams_utils/date.py:164 msgid "yesterday" msgstr "" -#: ./src/pyams_utils/date.py:236 +#: ./src/pyams_utils/date.py:240 msgid "24 hours" msgstr "" -#: ./src/pyams_utils/date.py:238 +#: ./src/pyams_utils/date.py:242 #, c-format msgid "%d day and %d hours" msgstr "" -#: ./src/pyams_utils/date.py:241 +#: ./src/pyams_utils/date.py:245 #, c-format msgid "%d hours" msgstr "" -#: ./src/pyams_utils/date.py:164 +#: ./src/pyams_utils/date.py:168 #, c-format msgid "%d hours ago" msgstr "" -#: ./src/pyams_utils/date.py:168 +#: ./src/pyams_utils/date.py:172 msgid "less than 5 minutes ago" msgstr "" -#: ./src/pyams_utils/date.py:245 +#: ./src/pyams_utils/date.py:249 #, c-format msgid "%d minutes" msgstr "" -#: ./src/pyams_utils/date.py:247 +#: ./src/pyams_utils/date.py:251 #, c-format msgid "%d seconds" msgstr "" -#: ./src/pyams_utils/date.py:166 +#: ./src/pyams_utils/date.py:170 #, c-format msgid "%d minutes ago" msgstr "" @@ -509,77 +509,57 @@ "letters between 'A' end 'F')" msgstr "" -#: ./src/pyams_utils/widget/decimal.py:34 -msgid "The entered value is not a valid decimal literal." +#: ./src/pyams_utils/pygments.py:106 +msgid "Selected lexer" msgstr "" -#: ./src/pyams_utils/zmi/zeo.py:68 -msgid "Add ZEO connection..." -msgstr "" - -#: ./src/pyams_utils/zmi/zeo.py:78 -msgid "Utilities" +#: ./src/pyams_utils/pygments.py:107 +msgid "Lexer used to format source code" msgstr "" -#: ./src/pyams_utils/zmi/zeo.py:79 -msgid "Add ZEO connection" +#: ./src/pyams_utils/pygments.py:112 +msgid "Display line numbers?" msgstr "" -#: ./src/pyams_utils/zmi/zeo.py:125 -msgid "Update ZEO connection properties" +#: ./src/pyams_utils/pygments.py:113 +msgid "If 'no', line numbers will be hidden" msgstr "" -#: ./src/pyams_utils/zmi/zeo.py:153 -msgid "Test ZEO connection..." -msgstr "" - -#: ./src/pyams_utils/zmi/zeo.py:177 -msgid "Test ZEO database connection" +#: ./src/pyams_utils/pygments.py:117 +msgid "Lines wrap?" msgstr "" -#: ./src/pyams_utils/zmi/zeo.py:163 -msgid "Close" +#: ./src/pyams_utils/pygments.py:118 +msgid "" +"If 'yes', lines wraps will be enabled; line numbers will not be displayed if " +"lines wrap is enabled..." msgstr "" -#: ./src/pyams_utils/zmi/zeo.py:164 -msgid "Test connection" -msgstr "" - -#: ./src/pyams_utils/zmi/zeo.py:104 -msgid "Specified connection name is already used!" -msgstr "" - -#: ./src/pyams_utils/zmi/zeo.py:107 -msgid "A ZEO connection is already registered with this name!" +#: ./src/pyams_utils/pygments.py:123 +msgid "Color style" msgstr "" -#: ./src/pyams_utils/zmi/zeo.py:60 -#, python-format -msgid "ZEO: {0}" +#: ./src/pyams_utils/pygments.py:124 +msgid "Selected color style" msgstr "" -#: ./src/pyams_utils/zmi/zeo.py:123 ./src/pyams_utils/zmi/zeo.py:175 -#, python-format -msgid "ZEO connection: {0}" +#: ./src/pyams_utils/pygments.py:75 +msgid "Automatic detection" msgstr "" -#: ./src/pyams_utils/zmi/timezone.py:38 -msgid "Update server timezone properties" -msgstr "" - -#: ./src/pyams_utils/zmi/intids.py:46 -msgid "Display indexer properties" +#: ./src/pyams_utils/widget/decimal.py:31 +msgid "The entered value is not a valid decimal literal." msgstr "" #: ./src/pyams_utils/interfaces/text.py:34 msgid "Renderer name" msgstr "" -#: ./src/pyams_utils/interfaces/inherit.py:38 +#: ./src/pyams_utils/interfaces/inherit.py:37 msgid "Inherit from parent?" msgstr "" -#: ./src/pyams_utils/interfaces/inherit.py:42 +#: ./src/pyams_utils/interfaces/inherit.py:41 msgid "Don't inherit from parent?" msgstr "" diff -r d11121d14add -r ad3c56ae5af9 src/pyams_utils/pygments.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_utils/pygments.py Fri Nov 30 12:03:59 2018 +0100 @@ -0,0 +1,152 @@ +# +# Copyright (c) 2008-2018 Thierry Florac +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# + +__docformat__ = 'restructuredtext' + +from fanstatic import get_library_registry +from persistent import Persistent +from pygments import highlight +from pygments.formatters.html import HtmlFormatter +from pygments.lexers import get_all_lexers, get_lexer_by_name, guess_lexer +from pygments.styles import get_all_styles +from pyramid.response import Response +from pyramid.view import view_config +from zope.container.contained import Contained +from zope.interface import Interface, implementer +from zope.schema import Bool, Choice +from zope.schema.fieldproperty import FieldProperty +from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary + +from pyams_utils.factory import factory_config +from pyams_utils.fanstatic import ExternalResource +from pyams_utils.list import unique_iter +from pyams_utils.vocabulary import vocabulary_config + +from pyams_utils import _ + + +# +# Pygments CSS view +# + +for library in get_library_registry().values(): + break +else: + try: + from pyams_skin import library + except ImportError: + from pyams_default_theme import library + + +pygments_css = ExternalResource(library, 'get-pygments-style.css', resource_type='css') + + +@view_config(name='get-pygments-style.css') +def get_pygments_style_view(request): + style = request.params.get('style', 'default') + styles = HtmlFormatter(linenos='inline', + nowrap=False, + cssclass='source', + style=style).get_style_defs() + return Response(styles, content_type='text/css') + + +# +# Pygments lexers +# + +PYGMENTS_LEXERS_VOCABULARY = 'Pygments lexers vocabulary' + + +@vocabulary_config(name=PYGMENTS_LEXERS_VOCABULARY) +class PygmentsLexersVocabulary(SimpleVocabulary): + """Pygments lexers vocabulary""" + + def __init__(self, context): + terms = [SimpleTerm('auto', title=_("Automatic detection"))] + for name, aliases, filetypes, mimetypes in sorted(unique_iter(get_all_lexers(), + key=lambda x: x[0].lower()), + key=lambda x: x[0].lower()): + terms.append(SimpleTerm(aliases[0] if len(aliases) > 0 else name, + title='{0}{1}'.format(name, + ' ({})'.format(', '.join(filetypes)) if filetypes else ''))) + super(PygmentsLexersVocabulary, self).__init__(terms) + + +PYGMENTS_STYLES_VOCABULARY = 'Pygments styles vocabulary' + + +@vocabulary_config(name=PYGMENTS_STYLES_VOCABULARY) +class PygmentsStylesVocabulary(SimpleVocabulary): + """Pygments styles vocabulary""" + + def __init__(self, context): + terms = [] + for name in sorted(get_all_styles()): + terms.append(SimpleTerm(name)) + super(PygmentsStylesVocabulary, self).__init__(terms) + + +# +# Pygments configuration +# + +class IPygmentsCodeConfiguration(Interface): + """Pygments html formatter options""" + + lexer = Choice(title=_("Selected lexer"), + description=_("Lexer used to format source code"), + required=True, + vocabulary=PYGMENTS_LEXERS_VOCABULARY, + default='auto') + + display_linenos = Bool(title=_("Display line numbers?"), + description=_("If 'no', line numbers will be hidden"), + required=True, + default=True) + + disable_wrap = Bool(title=_("Lines wrap?"), + description=_("If 'yes', lines wraps will be enabled; line numbers will not be " + "displayed if lines wrap is enabled..."), + required=True, + default=False) + + style = Choice(title=_("Color style"), + description=_("Selected color style"), + required=True, + vocabulary=PYGMENTS_STYLES_VOCABULARY, + default='default') + + +@factory_config(provided=IPygmentsCodeConfiguration) +@implementer(IPygmentsCodeConfiguration) +class PygmentsCodeRendererSettings(Persistent, Contained): + """Pygments code renderer settings""" + + lexer = FieldProperty(IPygmentsCodeConfiguration['lexer']) + display_linenos = FieldProperty(IPygmentsCodeConfiguration['display_linenos']) + disable_wrap = FieldProperty(IPygmentsCodeConfiguration['disable_wrap']) + style = FieldProperty(IPygmentsCodeConfiguration['style']) + + +def render_source(code: str, settings: IPygmentsCodeConfiguration): + """Render source with given settings""" + if settings.lexer == 'auto': + lexer = guess_lexer(code) + else: + lexer = get_lexer_by_name(settings.lexer) + if lexer is not None: + formatter = HtmlFormatter(linenos='inline' if settings.display_linenos else None, + nowrap=settings.disable_wrap, + cssclass='source', + style=settings.style) + return highlight(code, lexer, formatter)