diff --git a/mako/lexer.py b/mako/lexer.py
index 34f17dc..15975b4 100644
--- a/mako/lexer.py
+++ b/mako/lexer.py
@@ -247,6 +247,8 @@ def parse(self):
                 continue
             if self.match_python_block():
                 continue
+            if self.match_percent():
+                continue
             if self.match_text():
                 continue
 
@@ -352,14 +354,24 @@ def match_end(self):
         else:
             return True
 
+    def match_percent(self):
+        match = self.match(r"(?<=^)(\s*)%%(%*)", re.M)
+        if match:
+            self.append_node(
+                parsetree.Text, match.group(1) + "%" + match.group(2)
+            )
+            return True
+        else:
+            return False
+
     def match_text(self):
         match = self.match(
             r"""
                 (.*?)         # anything, followed by:
                 (
-                 (?<=\n)(?=[ \t]*(?=%|\#\#)) # an eval or line-based
-                                             # comment preceded by a
-                                             # consumed newline and whitespace
+                 (?<=\n)(?=[ \t]*(?=%|\#\#))  # an eval or line-based
+                                            # comment, preceded by a
+                                            # consumed newline and whitespace
                  |
                  (?=\${)      # an expression
                  |
diff --git a/mako/testing/helpers.py b/mako/testing/helpers.py
index 77cca36..5ae9d38 100644
--- a/mako/testing/helpers.py
+++ b/mako/testing/helpers.py
@@ -19,6 +19,10 @@ def result_lines(result):
     ]
 
 
+def result_raw_lines(result):
+    return [x for x in re.split(r"\r?\n", result) if x.strip() != ""]
+
+
 def make_path(
     filespec: Union[Path, str],
     make_absolute: bool = True,
diff --git a/test/test_lexer.py b/test/test_lexer.py
index f4983a3..66c0a1d 100644
--- a/test/test_lexer.py
+++ b/test/test_lexer.py
@@ -200,9 +200,10 @@ def test_percent_escape(self):
             TemplateNode(
                 {},
                 [
-                    Text("""\n\n""", (1, 1)),
-                    Text("""% some whatever.\n\n""", (3, 2)),
-                    Text("   %% more some whatever\n", (5, 2)),
+                    Text("\n\n%", (1, 1)),
+                    Text(" some whatever.\n\n", (3, 3)),
+                    Text("    %", (5, 1)),
+                    Text(" more some whatever\n", (5, 7)),
                     ControlLine("if", "if foo:", False, (6, 1)),
                     ControlLine("if", "endif", True, (7, 1)),
                     Text("        ", (8, 1)),
@@ -210,6 +211,92 @@ def test_percent_escape(self):
             ),
         )
 
+    def test_percent_escape2(self):
+        template = """%% do something
+%%% do something
+if <some condition>:
+    %%%% do something
+        """
+        node = Lexer(template).parse()
+        self._compare(
+            node,
+            TemplateNode(
+                {},
+                [
+                    Text("%", (1, 1)),
+                    Text(" do something\n", (1, 3)),
+                    Text("%%", (2, 1)),
+                    Text(" do something\nif <some condition>:\n", (2, 4)),
+                    Text("    %%%", (4, 1)),
+                    Text(" do something\n        ", (4, 9)),
+                ],
+            ),
+        )
+
+    def test_percent_escape_with_control_block(self):
+        template = """
+% for i in [1, 2, 3]:
+    %% do something ${i}
+% endfor
+"""
+        node = Lexer(template).parse()
+        self._compare(
+            node,
+            TemplateNode(
+                {},
+                [
+                    Text("\n", (1, 1)),
+                    ControlLine("for", "for i in [1, 2, 3]:", False, (2, 1)),
+                    Text("    %", (3, 1)),
+                    Text(" do something ", (3, 7)),
+                    Expression("i", [], (3, 21)),
+                    Text("\n", (3, 25)),
+                    ControlLine("for", "endfor", True, (4, 1)),
+                ],
+            ),
+        )
+
+    def test_inline_percent(self):
+        template = """
+%% foo
+bar %% baz
+"""
+        node = Lexer(template).parse()
+        self._compare(
+            node,
+            TemplateNode(
+                {},
+                [Text("\n%", (1, 1)), Text(" foo\nbar %% baz\n", (2, 3))],
+            ),
+        )
+
+        def test_inline_percent_with_control_block(self):
+            template = """
+% for i in [1, 2, 3]:
+%% foo
+bar %% baz
+% endfor
+"""
+            node = Lexer(template).parse()
+            self._compare(
+                node,
+                TemplateNode(
+                    {},
+                    [
+                        Text("\n", (1, 1)),
+                        ControlLine(
+                            "for", "for i in [1, 2, 3]:", False, (2, 1)
+                        ),
+                        Text("%", (3, 1)),
+                        Text(" foo\nbar ", (3, 3)),
+                        Text("%", (3, 3)),
+                        Text("%", (3, 3)),
+                        Text(" baz\n", (4, 7)),
+                        ControlLine("for", "endfor", True, (5, 1)),
+                    ],
+                ),
+            )
+
     def test_old_multiline_comment(self):
         template = """#*"""
         node = Lexer(template).parse()
diff --git a/test/test_template.py b/test/test_template.py
index 62fd21d..e03415e 100644
--- a/test/test_template.py
+++ b/test/test_template.py
@@ -15,6 +15,7 @@
 from mako.testing.fixtures import TemplateTest
 from mako.testing.helpers import flatten_result
 from mako.testing.helpers import result_lines
+from mako.testing.helpers import result_raw_lines
 
 
 class ctx:
@@ -1667,3 +1668,52 @@ class FuturesTest(TemplateTest):
     def test_future_import(self):
         t = Template("${ x / y }", future_imports=["division"])
         assert result_lines(t.render(x=12, y=5)) == ["2.4"]
+
+
+class EscapeTest(TemplateTest):
+    def test_percent_escape(self):
+        t = Template(
+            """%% do something
+%%% do something
+if <some condition>:
+    %%%% do something
+"""
+        )
+        assert result_raw_lines(t.render()) == [
+            "% do something",
+            "%% do something",
+            "if <some condition>:",
+            "    %%% do something",
+        ]
+
+    def test_percent_escape2(self):
+        t = Template(
+            """
+% for i in [1, 2, 3]:
+    %% do something ${i}
+% endfor
+"""
+        )
+        assert result_raw_lines(t.render()) == [
+            "    % do something 1",
+            "    % do something 2",
+            "    % do something 3",
+        ]
+
+    def test_inline_percent(self):
+        t = Template(
+            """
+% for i in [1, 2, 3]:
+%% foo
+bar %% baz
+% endfor
+"""
+        )
+        assert result_raw_lines(t.render()) == [
+            "% foo",
+            "bar %% baz",
+            "% foo",
+            "bar %% baz",
+            "% foo",
+            "bar %% baz",
+        ]