1# ext/pygmentplugin.py
2# Copyright 2006-2023 the Mako authors and contributors <see AUTHORS file>
3#
4# This module is part of Mako and is released under
5# the MIT License: http://www.opensource.org/licenses/mit-license.php
6
7from pygments import highlight
8from pygments.formatters.html import HtmlFormatter
9from pygments.lexer import bygroups
10from pygments.lexer import DelegatingLexer
11from pygments.lexer import include
12from pygments.lexer import RegexLexer
13from pygments.lexer import using
14from pygments.lexers.agile import Python3Lexer
15from pygments.lexers.agile import PythonLexer
16from pygments.lexers.web import CssLexer
17from pygments.lexers.web import HtmlLexer
18from pygments.lexers.web import JavascriptLexer
19from pygments.lexers.web import XmlLexer
20from pygments.token import Comment
21from pygments.token import Keyword
22from pygments.token import Name
23from pygments.token import Operator
24from pygments.token import Other
25from pygments.token import String
26from pygments.token import Text
27
28
29class MakoLexer(RegexLexer):
30    name = "Mako"
31    aliases = ["mako"]
32    filenames = ["*.mao"]
33
34    tokens = {
35        "root": [
36            (
37                r"(\s*)(\%)(\s*end(?:\w+))(\n|\Z)",
38                bygroups(Text, Comment.Preproc, Keyword, Other),
39            ),
40            (
41                r"(\s*)(\%(?!%))([^\n]*)(\n|\Z)",
42                bygroups(Text, Comment.Preproc, using(PythonLexer), Other),
43            ),
44            (
45                r"(\s*)(##[^\n]*)(\n|\Z)",
46                bygroups(Text, Comment.Preproc, Other),
47            ),
48            (r"""(?s)<%doc>.*?</%doc>""", Comment.Preproc),
49            (
50                r"(<%)([\w\.\:]+)",
51                bygroups(Comment.Preproc, Name.Builtin),
52                "tag",
53            ),
54            (
55                r"(</%)([\w\.\:]+)(>)",
56                bygroups(Comment.Preproc, Name.Builtin, Comment.Preproc),
57            ),
58            (r"<%(?=([\w\.\:]+))", Comment.Preproc, "ondeftags"),
59            (
60                r"(?s)(<%(?:!?))(.*?)(%>)",
61                bygroups(Comment.Preproc, using(PythonLexer), Comment.Preproc),
62            ),
63            (
64                r"(\$\{)(.*?)(\})",
65                bygroups(Comment.Preproc, using(PythonLexer), Comment.Preproc),
66            ),
67            (
68                r"""(?sx)
69                (.+?)               # anything, followed by:
70                (?:
71                 (?<=\n)(?=%(?!%)|\#\#) |  # an eval or comment line
72                 (?=\#\*) |          # multiline comment
73                 (?=</?%) |         # a python block
74                                    # call start or end
75                 (?=\$\{) |         # a substitution
76                 (?<=\n)(?=\s*%) |
77                                    # - don't consume
78                 (\\\n) |           # an escaped newline
79                 \Z                 # end of string
80                )
81            """,
82                bygroups(Other, Operator),
83            ),
84            (r"\s+", Text),
85        ],
86        "ondeftags": [
87            (r"<%", Comment.Preproc),
88            (r"(?<=<%)(include|inherit|namespace|page)", Name.Builtin),
89            include("tag"),
90        ],
91        "tag": [
92            (r'((?:\w+)\s*=)\s*(".*?")', bygroups(Name.Attribute, String)),
93            (r"/?\s*>", Comment.Preproc, "#pop"),
94            (r"\s+", Text),
95        ],
96        "attr": [
97            ('".*?"', String, "#pop"),
98            ("'.*?'", String, "#pop"),
99            (r"[^\s>]+", String, "#pop"),
100        ],
101    }
102
103
104class MakoHtmlLexer(DelegatingLexer):
105    name = "HTML+Mako"
106    aliases = ["html+mako"]
107
108    def __init__(self, **options):
109        super().__init__(HtmlLexer, MakoLexer, **options)
110
111
112class MakoXmlLexer(DelegatingLexer):
113    name = "XML+Mako"
114    aliases = ["xml+mako"]
115
116    def __init__(self, **options):
117        super().__init__(XmlLexer, MakoLexer, **options)
118
119
120class MakoJavascriptLexer(DelegatingLexer):
121    name = "JavaScript+Mako"
122    aliases = ["js+mako", "javascript+mako"]
123
124    def __init__(self, **options):
125        super().__init__(JavascriptLexer, MakoLexer, **options)
126
127
128class MakoCssLexer(DelegatingLexer):
129    name = "CSS+Mako"
130    aliases = ["css+mako"]
131
132    def __init__(self, **options):
133        super().__init__(CssLexer, MakoLexer, **options)
134
135
136pygments_html_formatter = HtmlFormatter(
137    cssclass="syntax-highlighted", linenos=True
138)
139
140
141def syntax_highlight(filename="", language=None):
142    mako_lexer = MakoLexer()
143    python_lexer = Python3Lexer()
144    if filename.startswith("memory:") or language == "mako":
145        return lambda string: highlight(
146            string, mako_lexer, pygments_html_formatter
147        )
148    return lambda string: highlight(
149        string, python_lexer, pygments_html_formatter
150    )
151