From bf2862fd1340efd766571ac7555c8715fdff2649 Mon Sep 17 00:00:00 2001 From: Sol Rosca Date: Thu, 26 Mar 2026 11:11:42 +0100 Subject: [PATCH 1/3] Add support for middle tags in --extra-block flag --- djhtml/__main__.py | 14 ++++++++++++-- djhtml/modes.py | 10 ++++++++-- djhtml/options.py | 2 +- tests/suite/django.html | 7 +++++++ tests/suite/django.tokens | 7 +++++++ tests/test_suite.py | 6 +++--- 6 files changed, 38 insertions(+), 8 deletions(-) diff --git a/djhtml/__main__.py b/djhtml/__main__.py index 2842ac0..da481d1 100644 --- a/djhtml/__main__.py +++ b/djhtml/__main__.py @@ -48,6 +48,13 @@ def main() -> None: if len(options.input_filenames) > 1 and "-" in options.input_filenames: sys.exit("I’m sorry Dave, I’m afraid I can’t do that.") + extra_blocks = {} + extra_middle_tags = [] + for block_tuple in options.extra_block or (): + if len(block_tuple) >= 2: + extra_blocks[block_tuple[0]] = block_tuple[1] + extra_middle_tags.extend(block_tuple[2:]) + for filename in _generate_filenames(options.input_filenames, suffixes): # Read input file try: @@ -74,9 +81,12 @@ def main() -> None: guess = probabilities.index(max(probabilities)) # Indent input file - extra_blocks = dict(options.extra_block or ()) try: - result = Mode(source, extra_blocks=extra_blocks).indent( + result = Mode( + source, + extra_blocks=extra_blocks, + extra_middle_tags=extra_middle_tags, + ).indent( options.tabwidth or guess or 4 ) except modes.MaxLineLengthExceeded: diff --git a/djhtml/modes.py b/djhtml/modes.py index c3ea11a..6b97e9e 100644 --- a/djhtml/modes.py +++ b/djhtml/modes.py @@ -36,6 +36,7 @@ def __init__( source: str = "", return_mode: BaseMode | None = None, extra_blocks: dict[str, str] | None = None, + extra_middle_tags: list[str] | None = None, ) -> None: """ Instantiate with source text before calling indent(), or @@ -49,6 +50,7 @@ def __init__( self.return_mode = return_mode or self self.token_re = compile_re(self.RAW_TOKENS) self.extra_blocks = extra_blocks or {} + self.extra_middle_tags = extra_middle_tags or [] # To keep track of the current and previous offsets. self.offsets = OffsetDict(relative=0, absolute=0) @@ -252,7 +254,7 @@ def create_token( token = Token.Close(raw_token, mode=DjTXT, **self.offsets) elif self._has_closing_token(name, raw_token, src): token = Token.Open(raw_token, mode=DjTXT, **self.offsets) - elif name in self.CLOSING_AND_OPENING_TAGS: + elif name in self.CLOSING_AND_OPENING_TAGS or name in self.extra_middle_tags: token = Token.CloseAndOpen(raw_token, mode=DjTXT, **self.offsets) else: token = Token.Text(raw_token, mode=DjTXT, **self.offsets) @@ -440,13 +442,15 @@ def __init__( source: str = "", return_mode: BaseMode | None = None, extra_blocks: dict[str, str] | None = None, + extra_middle_tags: list[str] | None = None, ) -> None: - super().__init__(source, return_mode, extra_blocks) + super().__init__(source, return_mode, extra_blocks, extra_middle_tags) self.haskell = False self.haskell_re = re.compile(r"^ *, ([$\w-]+ *=|[$\w-]+;?)") self.variable_re = re.compile(r"^ *([$\w-]+ *=|[$\w-]+;?)") self.previous_line_ended_with_comma = False self.extra_blocks = {} + self.extra_middle_tags = [] def create_token( self, raw_token: str, src: str, line: Line @@ -559,6 +563,7 @@ def __init__( self.return_mode = return_mode self.token_re = compile_re([r"\n", endtag]) self.extra_blocks = {} + self.extra_middle_tags = [] def create_token( self, raw_token: str, src: str, line: Line @@ -594,6 +599,7 @@ def __init__( self.inside_attr = False self.additional_offset = -len(tagname) - 1 if absolute else 0 self.extra_blocks = {} + self.extra_middle_tags = [] def create_token( self, raw_token: str, src: str, line: Line diff --git a/djhtml/options.py b/djhtml/options.py index 2223e71..51ce31e 100644 --- a/djhtml/options.py +++ b/djhtml/options.py @@ -62,7 +62,7 @@ "-b", "--extra-block", action="append", - help="startblock,endblock pair", + help="startblock,endblock[,middletag,...] tuple", type=lambda x: tuple(x.split(",")), ) diff --git a/tests/suite/django.html b/tests/suite/django.html index 73da8db..3d4e06c 100644 --- a/tests/suite/django.html +++ b/tests/suite/django.html @@ -200,3 +200,10 @@

Welcome, {{ request.user }}

{% weird_tag %}
{% endweird %} + + +{% weird_tag %} +
first
+{% weird_middle %} +
second
+{% endweird %} diff --git a/tests/suite/django.tokens b/tests/suite/django.tokens index 2c2d4e6..e53fcf9 100644 --- a/tests/suite/django.tokens +++ b/tests/suite/django.tokens @@ -200,4 +200,11 @@ Line([Open('', mode=DjHTML, ignore=True)], ignore=True) +Line([Open('{% weird_tag %}', mode=DjTXT)]) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('div', mode=InsideHTMLTag, absolute=5), Open('>', mode=DjHTML, level=1), Text('first', mode=DjHTML), Close('', mode=DjHTML)], level=1) +Line([CloseAndOpen('{% weird_middle %}', mode=DjTXT)]) +Line([Text(' ', mode=DjHTML), Text('<', mode=DjHTML), Text('div', mode=InsideHTMLTag, absolute=5), Open('>', mode=DjHTML, level=1), Text('second', mode=DjHTML), Close('', mode=DjHTML)], level=1) +Line([Close('{% endweird %}', mode=DjTXT)]) Line([]) \ No newline at end of file diff --git a/tests/test_suite.py b/tests/test_suite.py index cab0eba..3d34532 100644 --- a/tests/test_suite.py +++ b/tests/test_suite.py @@ -28,18 +28,18 @@ def _test_file(self, basename: str) -> None: # Indent the expected output to 0 (no indentation) unindented = DjHTML( - expected_output, extra_blocks={"weird_tag": "endweird"} + expected_output, extra_blocks={"weird_tag": "endweird"}, extra_middle_tags=["weird_middle"] ).indent(0) self.assertNotEqual(unindented, expected_output) # Re-indent the unindented output to 4 actual_output = DjHTML( - unindented, extra_blocks={"weird_tag": "endweird"} + unindented, extra_blocks={"weird_tag": "endweird"}, extra_middle_tags=["weird_middle"] ).indent(4) self.assertEqual(expected_output, actual_output) # Compare the tokenization actual_tokens = DjHTML( - actual_output, extra_blocks={"weird_tag": "endweird"} + actual_output, extra_blocks={"weird_tag": "endweird"}, extra_middle_tags=["weird_middle"] ).debug() self.assertEqual(expected_tokens, actual_tokens) From 81cdb1a91ec8a31993741dbee07babe713843002 Mon Sep 17 00:00:00 2001 From: Sol Rosca Date: Thu, 26 Mar 2026 15:12:03 +0100 Subject: [PATCH 2/3] Place middle tags before end tag in -b flag --- djhtml/__main__.py | 4 ++-- djhtml/options.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/djhtml/__main__.py b/djhtml/__main__.py index da481d1..d02ed51 100644 --- a/djhtml/__main__.py +++ b/djhtml/__main__.py @@ -52,8 +52,8 @@ def main() -> None: extra_middle_tags = [] for block_tuple in options.extra_block or (): if len(block_tuple) >= 2: - extra_blocks[block_tuple[0]] = block_tuple[1] - extra_middle_tags.extend(block_tuple[2:]) + extra_blocks[block_tuple[0]] = block_tuple[-1] + extra_middle_tags.extend(block_tuple[1:-1]) for filename in _generate_filenames(options.input_filenames, suffixes): # Read input file diff --git a/djhtml/options.py b/djhtml/options.py index 51ce31e..f217070 100644 --- a/djhtml/options.py +++ b/djhtml/options.py @@ -62,7 +62,7 @@ "-b", "--extra-block", action="append", - help="startblock,endblock[,middletag,...] tuple", + help="startblock[,middletag,...],endblock tuple", type=lambda x: tuple(x.split(",")), ) From f90ab17da2d333500d14354c4823a48c081c3231 Mon Sep 17 00:00:00 2001 From: Sol Rosca Date: Thu, 26 Mar 2026 17:07:50 +0100 Subject: [PATCH 3/3] Fix formatting --- djhtml/__main__.py | 4 +--- djhtml/modes.py | 4 +++- tests/test_suite.py | 12 +++++++++--- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/djhtml/__main__.py b/djhtml/__main__.py index d02ed51..51ea58d 100644 --- a/djhtml/__main__.py +++ b/djhtml/__main__.py @@ -86,9 +86,7 @@ def main() -> None: source, extra_blocks=extra_blocks, extra_middle_tags=extra_middle_tags, - ).indent( - options.tabwidth or guess or 4 - ) + ).indent(options.tabwidth or guess or 4) except modes.MaxLineLengthExceeded: problematic_files += 1 _error(f"Maximum line length exceeded in {filename}") diff --git a/djhtml/modes.py b/djhtml/modes.py index 6b97e9e..56b57e9 100644 --- a/djhtml/modes.py +++ b/djhtml/modes.py @@ -254,7 +254,9 @@ def create_token( token = Token.Close(raw_token, mode=DjTXT, **self.offsets) elif self._has_closing_token(name, raw_token, src): token = Token.Open(raw_token, mode=DjTXT, **self.offsets) - elif name in self.CLOSING_AND_OPENING_TAGS or name in self.extra_middle_tags: + elif ( + name in self.CLOSING_AND_OPENING_TAGS or name in self.extra_middle_tags + ): token = Token.CloseAndOpen(raw_token, mode=DjTXT, **self.offsets) else: token = Token.Text(raw_token, mode=DjTXT, **self.offsets) diff --git a/tests/test_suite.py b/tests/test_suite.py index 3d34532..411b479 100644 --- a/tests/test_suite.py +++ b/tests/test_suite.py @@ -28,18 +28,24 @@ def _test_file(self, basename: str) -> None: # Indent the expected output to 0 (no indentation) unindented = DjHTML( - expected_output, extra_blocks={"weird_tag": "endweird"}, extra_middle_tags=["weird_middle"] + expected_output, + extra_blocks={"weird_tag": "endweird"}, + extra_middle_tags=["weird_middle"], ).indent(0) self.assertNotEqual(unindented, expected_output) # Re-indent the unindented output to 4 actual_output = DjHTML( - unindented, extra_blocks={"weird_tag": "endweird"}, extra_middle_tags=["weird_middle"] + unindented, + extra_blocks={"weird_tag": "endweird"}, + extra_middle_tags=["weird_middle"], ).indent(4) self.assertEqual(expected_output, actual_output) # Compare the tokenization actual_tokens = DjHTML( - actual_output, extra_blocks={"weird_tag": "endweird"}, extra_middle_tags=["weird_middle"] + actual_output, + extra_blocks={"weird_tag": "endweird"}, + extra_middle_tags=["weird_middle"], ).debug() self.assertEqual(expected_tokens, actual_tokens)