1# ext/pygmentplugin.py
2# Copyright 2006-2025 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 )