diff --git a/src/djlint/formatter/compress.py b/src/djlint/formatter/compress.py index 1092ef5a..d0969c32 100644 --- a/src/djlint/formatter/compress.py +++ b/src/djlint/formatter/compress.py @@ -25,21 +25,69 @@ def _flatten_attributes(match: re.Match) -> str: if child_of_ignored_block(config, html, match): return match.group() + close = match.group(3) if "/" not in match.group(3) else f" {match.group(3)}" + # pylint: disable=C0209 return "{} {}{}".format( match.group(1), " ".join(x.strip() for x in match.group(2).strip().splitlines()), - match.group(3), + close, ) # put attributes on one line html = re.sub( re.compile( - rf"(<(?:{config.indent_html_tags}))\s((?:\"[^\"]*\"|'[^']*'|{{{{(?:(?!}}}}).)*}}}}|{{%(?:(?!%}}).)*%}}|[^'\">{{}}])+)(/?>)", + rf"(<(?:{config.indent_html_tags}))\s((?:\s*?(?:\"[^\"]*\"|'[^']*'|{{{{(?:(?!}}}}).)*}}}}|{{%(?:(?!%}}).)*%}}|[^'\">{{}}\/\s]))+)\s*?(/?>)", flags=re.IGNORECASE | re.MULTILINE | re.VERBOSE, ), _flatten_attributes, html, ) + # put closing tags back on one line + # + html = re.sub( + re.compile( + rf"()", + flags=re.IGNORECASE | re.MULTILINE | re.VERBOSE, + ), + r"\1\2", + html, + ) + + # remove extra space from empty tags + # + # ^ + html = re.sub( + re.compile( + rf"(<(?:{config.indent_html_tags}))\s*?(>)", + flags=re.IGNORECASE | re.MULTILINE | re.VERBOSE, + ), + r"\1\2", + html, + ) + + # ensure space before closing tag + # + # ^ + html = re.sub( + re.compile( + rf"(<(?:{config.indent_html_tags}))\s*?(/>)", + flags=re.IGNORECASE | re.MULTILINE | re.VERBOSE, + ), + r"\1 \2", + html, + ) + + # cleanup whitespace in doctype + html = re.sub( + re.compile( + r"({}\/\s]))+)\s*?(>)", + flags=re.IGNORECASE | re.MULTILINE | re.VERBOSE, + ), + r"\1 \2\3", + html, + ) + return html diff --git a/src/djlint/formatter/indent.py b/src/djlint/formatter/indent.py index 01494694..de8f905a 100644 --- a/src/djlint/formatter/indent.py +++ b/src/djlint/formatter/indent.py @@ -200,7 +200,7 @@ def indent_html(rawcode: str, config: Config) -> str: tmp = re.sub( re.compile( - rf"(\s*?)(<(?:{config.indent_html_tags})\b)((?:\"[^\"]*\"|'[^']*'|{{[^}}]*}}|[^'\">{{}}])+?)(/?>)", + rf"(\s*?)(<(?:{config.indent_html_tags})\b)((?:\"[^\"]*\"|'[^']*'|{{[^}}]*}}|[^'\">{{}}\/])+?)(\s?/?>)", re.VERBOSE | re.IGNORECASE, ), func, diff --git a/tests/test_config/test_format_attribute_template_tags/html-one.html b/tests/test_config/test_format_attribute_template_tags/html-one.html index 18bbcfc7..e82e71b9 100644 --- a/tests/test_config/test_format_attribute_template_tags/html-one.html +++ b/tests/test_config/test_format_attribute_template_tags/html-one.html @@ -3,10 +3,10 @@ then something neat {% else %} that is long stuff asdf and more even - {% endif %}"/> + {% endif %}" /> report image + alt="report image" /> None: output = reformat( tmp_file, runner, - b'\n', + b'\n', ) assert output.exit_code == 0 @@ -58,7 +58,7 @@ def test_without_config(runner: CliRunner, tmp_file: TextIO) -> None: output.text == r"""report image + alt="report image" /> """ ) diff --git a/tests/test_config/test_preserve_blank_lines/html_one.html b/tests/test_config/test_preserve_blank_lines/html_one.html index c5ac12b2..4a0a27ea 100644 --- a/tests/test_config/test_preserve_blank_lines/html_one.html +++ b/tests/test_config/test_preserve_blank_lines/html_one.html @@ -1,3 +1,3 @@ {% block someblock %}{% endblock %} -
-
+
+
diff --git a/tests/test_html/test_attributes.py b/tests/test_html/test_attributes.py index c25b3edd..d3b2f7dc 100644 --- a/tests/test_html/test_attributes.py +++ b/tests/test_html/test_attributes.py @@ -44,7 +44,7 @@ ' calc(100vw - 30px)"\n' ' class="richtext-image imageblock overflow {{ value.image_position }} lazy"\n' ' title="{{ value.image.title }}"\n' - ' alt="Block image"/>\n' + ' alt="Block image" />\n' ), id="srcset", ), @@ -62,7 +62,7 @@ ' style="width:100px;\n' " cursor: text;\n" ' border:1px solid pink"\n' - ' required="true"/>\n' + ' required="true" />\n' ), id="long_attributes", ), @@ -195,7 +195,7 @@ ' class="form-control"\n' ' type="text"\n' ' name="driver_id"\n' - " value=\"{{ id|default(' sample_text ') }}\"/>\n" + " value=\"{{ id|default(' sample_text ') }}\" />\n" ), id="another_boolean_after_tag", ), @@ -477,11 +477,10 @@ ' target="_blank"\n' ' role="tab"\n' ' href="https://yandex.ru/news?msid=1581089780.29024.161826.172442&mlid=1581088893.glob_225"\n' - ' rel="noopener">...\n" - " \n" - " \n" + ' rel="noopener">...
\n' + " \n" " \n" + "\n" ), id="class_bem2", ), @@ -594,7 +593,7 @@ ' src="https://s.yimg.com/rz/p/yahoo_frontpage_en-US_s_f_p_205x58_frontpage_2x.png"\n' ' height="58px"\n' ' width="205px"\n' - ' alt="Yahoo"/>\n' + ' alt="Yahoo" />\n' " \n" " \n" " \n" @@ -755,7 +754,7 @@ ' /assets/visual.png 805w"\n' ' sizes="(max-width: 66rem) 100vw,\n' ' 66rem"\n' - ' alt=""/>\n' + ' alt="" />\n' '\n' + ' alt="" />\n' '\n' + ' alt="" />\n' '\n' "\n" '\n' + ' alt="" />\n' '\n' + ' alt="" />\n' '\n' + ' alt="" />\n' '\n' "\n" '\n' + ' alt="" />\n' ), id="src_set", ), diff --git a/tests/test_html/test_bracket_same_line.py b/tests/test_html/test_bracket_same_line.py index 501c02ea..3462f9d2 100644 --- a/tests/test_html/test_bracket_same_line.py +++ b/tests/test_html/test_bracket_same_line.py @@ -141,12 +141,12 @@ ), ( '\n' - '\n' - '\n' - '\n' - '\n' - '\n' + ' src="./1.jpg" />\n' + '\n' + '\n' + '\n' + '\n' + '\n' ), id="void", ), @@ -157,12 +157,12 @@ ), ( '\n' - '\n' - '\n' - '\n' - '\n' - '\n' + ' src="./1.jpg" />\n' + '\n' + '\n' + '\n' + '\n' + '\n' ), id="void_braket_same_line", ), diff --git a/tests/test_html/test_comments.py b/tests/test_html/test_comments.py index ff026b77..8af19319 100644 --- a/tests/test_html/test_comments.py +++ b/tests/test_html/test_comments.py @@ -123,11 +123,11 @@ " \n" "\n" '\n' - "
\n" + "
\n" ' \n' "
\n" " \n" - "
\n" + "
\n" "\n" "\n" '\n' diff --git a/tests/test_html/test_doctype_declarations.py b/tests/test_html/test_doctype_declarations.py index 62ec7922..e0b801bc 100644 --- a/tests/test_html/test_doctype_declarations.py +++ b/tests/test_html/test_doctype_declarations.py @@ -15,7 +15,7 @@ ), pytest.param( (""), - ("\n"), + ("\n"), id="case_2", ), pytest.param( @@ -38,7 +38,7 @@ "\n" " \n" " An HTML standard template\n" - ' \n' + ' \n' " \n" " \n" "

… Your HTML content here …

\n" @@ -173,7 +173,7 @@ ' width="400"\n' ' height="250"\n' ' alt="Beep"\n' - ' vspace="20"/>\n' + ' vspace="20" />\n' " \n" '

\n' " Bar Foo,\n" diff --git a/tests/test_html/test_ignored.py b/tests/test_html/test_ignored.py index dc7f513a..cc720292 100644 --- a/tests/test_html/test_ignored.py +++ b/tests/test_html/test_ignored.py @@ -86,7 +86,7 @@ ( "\n" " \n" - ' \n' + ' \n' " \n" " \n" " \n" diff --git a/tests/test_html/test_next_line_empty.py b/tests/test_html/test_next_line_empty.py index a97305a6..c6815f38 100644 --- a/tests/test_html/test_next_line_empty.py +++ b/tests/test_html/test_next_line_empty.py @@ -29,23 +29,16 @@ "\n" ), ( - "

\n" - "\n" + "
\n" "\n" - "
\n" - "\n" + "
\n" "\n" - "
\n" - "\n" + "
\n" "\n" "
\n" - ' 123123123123\n" - " 123123\n" - "
\n" + ' 123123123123\n' + " 123123\n" + "
\n" ), id="standalone_end_marker", ) diff --git a/tests/test_html/test_single_attribute_per_line.py b/tests/test_html/test_single_attribute_per_line.py index bfdc0dd6..473efefd 100644 --- a/tests/test_html/test_single_attribute_per_line.py +++ b/tests/test_html/test_single_attribute_per_line.py @@ -38,7 +38,7 @@ '\n' 'bar\n' 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n' + ' alt="Lorem ipsum dolor sit amet, consectetur adipiscing elit." />\n' ), id="single_attrib_per_line_enabled", ), @@ -58,7 +58,7 @@ "
\n" '\n' 'bar\n' - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n' + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n' ), ( '
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
\n' @@ -72,7 +72,7 @@ '\n' 'bar\n' 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n' + ' alt="Lorem ipsum dolor sit amet, consectetur adipiscing elit." />\n' ), id="single_attrib_per_line_disabled", ), diff --git a/tests/test_html/test_srcset.py b/tests/test_html/test_srcset.py index 566101da..fead129c 100644 --- a/tests/test_html/test_srcset.py +++ b/tests/test_html/test_srcset.py @@ -15,7 +15,7 @@ " should-not-format 400w 100h,\n" " should-not-format 500w 200h\n" '"\n' - ' alt=""/>\n' + ' alt="" />\n' '\n' + ' alt="" />\n' '\n' + ' alt="" />\n' ), id="invalid", ), diff --git a/tests/test_html/test_svg.py b/tests/test_html/test_svg.py index b929478f..6e45a61b 100644 --- a/tests/test_html/test_svg.py +++ b/tests/test_html/test_svg.py @@ -64,7 +64,7 @@ " \n" "\n" '\n' - "
\n" " aaaaaaaaaa\n" - ' bbbbbbbbbb\n" - " cccccccccc\n" - "
\n" - "
\n" - " aaaaaaaaaa\n" - ' bbbbbbbbbbcccccccccc\n" - "
\n" + ' bbbbbbbbbb\n' + " cccccccccc\n" + "
\n" + "
\n" + " aaaaaaaaaa\n" + ' bbbbbbbbbbcccccccccc\n' + "
\n" ), id="close_at_start", ), @@ -84,28 +82,19 @@ ">\n" ), ( - "

\n" - " Want to write us a letter? Use ourmailing address.\n" - "\n" - "

\n" - ' Want to write us a letter? Use ourmailing address.\n" - "\n" - "

\n" + "

\n" + " Want to write us a letter? Use ourmailing address.\n" + "

\n" + "

\n" + ' Want to write us a letter? Use ourmailing address.\n' + "

\n" + "

\n" ' Want to write us a letter? Use ourmailing address.\n" - "\n" + ' href4="contacts.html#Mailing_address">mailing address.\n' + "

\n" ), id="opening_at_end", ), @@ -328,14 +317,14 @@ "\n" "

\n" "

\n" - "

\n" - "

\n" + "

\n" + "

\n" "

\n" "

\n" "
\n" "
\n" - "

long long long text long long long text long long long text long long long text
\n" - "

long long long text long long long text long long long text long long long text
\n" + "

long long long text long long long text long long long text long long long text
\n" + "

long long long text long long long text long long long text long long long text
\n" ), id="pre", ), @@ -468,22 +457,22 @@ '\n' ), ( - "
\n" + "
\n" "
\n" "
\n" "
\n" "
\n" - "
\n" + "
\n" '
\n' '
\n' - '
\n' - '
\n' + '
\n' + '
\n' '
\n' + ' attribute-f="value" />\n' "
string
\n" "
very very very very very very very very very very very very very very very very long string
\n" "
\n" @@ -566,26 +555,19 @@ "
string
\n" "
string
\n" "
\n" - "
    \n" + "
      \n" " 123\n" - '
    • \n' - " First\n" - " \n" + '
    • First
    • \n' " 456\n" - '
    • \n' - " Second\n" - " \n" + '
    • Second
    • \n' " 789\n" - "\n" + "
    \n" "*200\n" - '\n' + '\n' "123\n" "
    \n" " 123\n" - " \n" + " \n" " 456\n" "
    \n" "

    \n" @@ -600,32 +582,29 @@ " \n" "

    \n" "\n" - "\n" + "\n" "|\n" "\n" "
    \n" "\n" "
    \n" "
    \n" '\n" "
    \n" "
    \n" "\n" "
    \n" "
    \n" '\n" "
    \n" "
    \n" "

    \n" @@ -669,13 +648,13 @@ "\n" "\n" "\n" - ' \n' + ' \n' "\n" "\n" - ' \n' + ' \n' "\n" "\n" - ' \n' + ' \n' "\n" ), id="tags", @@ -754,8 +733,8 @@ "\n" "

    \n" ' before\n' - ' \n' - ' \n' + ' \n' + ' \n' "after\n" "
    \n" "
    \n" @@ -767,8 +746,7 @@ ")\n" "html_out = (\n" "
    \n" - " before
    \n" "
    \n" " before\n" @@ -787,12 +765,10 @@ ' before\n' ' \n' ' \n' - "after\n" + "after\n" "
    \n" "
    \n" - ' beforeafter\n" + ' beforeafter\n' "
    \n" "
    \n" ' beforeafter\n' diff --git a/tests/test_html/test_whitespace.py b/tests/test_html/test_whitespace.py index 651e0c92..1db13f6d 100644 --- a/tests/test_html/test_whitespace.py +++ b/tests/test_html/test_whitespace.py @@ -94,7 +94,7 @@ "

    \n" ' about fedco bottom image\n' + ' style="float: left" />\n' " We are a cooperative, one of the few seed companies so organized\n" " in the United States. Because we do not have an individual owner or beneficiary,\n" " profit is not our primary goal. Consumers own 60% of the cooperative and worker\n" @@ -161,15 +161,8 @@ '>sum\n' ), ( - '/ˌɪləˈnɔɪ/\n' - 'ipsum\n' + '/ˌɪləˈnɔɪ/\n' + 'ipsum\n' ), id="nested_inline_without_whitespace", ), @@ -206,14 +199,12 @@ ")\n" "html_out = (\n" "\n" - "Nihil aut odit omnis. Quam maxime est molestiae. Maxime dolorem dolores\n" - " voluptas quaerat ut qui sunt vitae error.\n" - " \n" - " Nihil aut odit omnis. Quam maxime est molestiae. Maxime dolorem dolores voluptas quaerat ut qui sunt vitae error.\n" - " \n" - " Prix : 32 €\n" + "Nihil aut odit omnis. Quam maxime est molestiae. Maxime dolorem dolores\n" + "voluptas quaerat ut qui sunt vitae error.\n" + "\n" + "Nihil aut odit omnis. Quam maxime est molestiae. Maxime dolorem dolores voluptas quaerat ut qui sunt vitae error.\n" + "\n" + "Prix : 32 €\n" ), id="non_breaking_whitespace", ), @@ -259,22 +250,22 @@ ), pytest.param( ("\n"), - (" \n" "\n"), + (" \n" "\n"), id="snippet_26", ), pytest.param( ("\n"), - ("  \n" "\n"), + ("  \n" "\n"), id="snippet_27", ), pytest.param( ("    \n"), - ("   \n" "\n"), + ("   \n" "\n"), id="snippet_28", ), pytest.param( ("     \n"), - ("    \n" "\n"), + ("    \n" "\n"), id="snippet_29", ), pytest.param(