Skip to content

Commit

Permalink
fix(set): added formatting of json/list style set contents
Browse files Browse the repository at this point in the history
if the contents are parsable as a python list or json5 they can be reformmated. Otherwise we will
fall back to the standard indent this pr added.

closes #287, closes #518, re #370
  • Loading branch information
christopherpickering committed May 5, 2023
1 parent 9154a3f commit 85aca4c
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 17 deletions.
19 changes: 17 additions & 2 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ html-void-elements = "^0.1.0"
html-tag-names = "^0.1.2"
jsbeautifier = "^1.14.4"
cssbeautifier = "^1.14.4"
json5 = "^0.9.11"

[tool.poetry.scripts]
djlint = "djlint:main"
Expand Down Expand Up @@ -93,7 +94,7 @@ ignore = ["E501", "PLR0913", "PLR0915", "PLR0912", "D203", "D213"]
"src/djlint/formatter/css.py" = ["PLW2901"]
"src/djlint/formatter/js.py" = ["PLW2901"]
"src/djlint/lint.py" = ["PLW2901"]
"src/djlint/formatter/indent.py" = ["SIM114"]
"src/djlint/formatter/indent.py" = ["SIM114", "E722"]

[tool.ruff.mccabe]
max-complexity = 10
10 changes: 5 additions & 5 deletions src/djlint/formatter/condense.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,11 @@ def condense_html(html, config):
# space for other purposes, we should not try to remove it.
return html

def condense_line(config: Config, match: re.Match) -> str:
def condense_line(config: Config, html: str, match: re.Match) -> str:
"""Put contents on a single line if below max line length."""
if (
(
not inside_ignored_block(config, html, match)
and (
len(match.group(1).splitlines()[-1] + match.group(3) + match.group(4))
< config.max_line_length
)
Expand Down Expand Up @@ -177,8 +178,7 @@ def if_blank_line_before_match(config: Config, html: str) -> bool:
return True

# add blank lines before tags

func = partial(condense_line, config)
func = partial(condense_line, config, html)

# put short single line tags on one line
html = re.sub(
Expand All @@ -194,7 +194,7 @@ def if_blank_line_before_match(config: Config, html: str) -> bool:
# jinja +%} and {%+ intentionally omitted.
html = re.sub(
re.compile(
rf"((?:\s|^){{%-?[ ]*?({config.optional_single_line_template_tags})[^\n(?:%}})]*?%}})\s*([^%\n]*?)\s*?({{%-?[ ]+?end(\2)[ ]*?%}})",
rf"((?:\s|^){{%-?[ ]*?({config.optional_single_line_template_tags})(?:(?!\n|%}}).)*?%}})\s*([^%\n]*?)\s*?({{%-?[ ]+?end(\2)[ ]*?%}})",
flags=re.IGNORECASE | re.MULTILINE | re.VERBOSE,
),
func,
Expand Down
64 changes: 61 additions & 3 deletions src/djlint/formatter/indent.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from functools import partial

import json5 as json
import regex as re

from ..helpers import (
Expand Down Expand Up @@ -101,8 +102,6 @@ def indent_html(rawcode: str, config: Config) -> str:
):
tmp = (indent * indent_level) + item + "\n"

# if unindent, move left

# closing set tag
elif (
re.search(
Expand Down Expand Up @@ -134,6 +133,7 @@ def indent_html(rawcode: str, config: Config) -> str:
indent_level = max(indent_level - 1, 0)
tmp = (indent * indent_level) + item + "\n"

# if unindent, move left
elif (
re.search(
config.tag_unindent,
Expand Down Expand Up @@ -195,7 +195,7 @@ def indent_html(rawcode: str, config: Config) -> str:
elif (
re.search(
re.compile(
r"^([ ]*{%[ ]*set)(?!.*%}).*$",
r"^([ ]*{%[ ]*?set)(?!.*%}).*$",
re.IGNORECASE | re.MULTILINE | re.VERBOSE,
),
item,
Expand Down Expand Up @@ -329,6 +329,64 @@ def fix_handlebars_template_tags(
# handlebars templates
beautified_code = re.sub(r"({{#(?:each|if).+?[^ ])(}})", func, beautified_code)

# try to fix internal formatting of set tag
def format_set(config, match):
open_bracket = match.group(1)
tag = match.group(2)
close_braket = match.group(4)
contents = match.group(3).strip()
contents_split = contents.split("=", 1)

if len(contents_split) > 1:
try:
# try to format the contents as json
data = json.loads(contents_split[-1])
contents = (
contents_split[0].strip()
+ " = "
+ json.dumps(data, trailing_commas=False)
)
completed_tag = f"{open_bracket} {tag} {contents} {close_braket}"

if len(completed_tag) >= config.max_line_length:
# if the line is too long we can indent the json
contents = (
contents_split[0].strip()
+ " = "
+ json.dumps(
data, indent=config.indent_size, trailing_commas=False
)
)
completed_tag = f"{open_bracket} {tag} {contents} {close_braket}"
return completed_tag

except:
# was not json.. try to eval as set
try:
contents = (
contents_split[0].strip()
+ " = "
+ str(eval(contents_split[-1]))
)
except:
contents = (
contents_split[0].strip() + " = " + contents_split[-1].strip()
)
pass

return f"{open_bracket} {tag} {contents} {close_braket}"

func = partial(format_set, config)
# format set contents
beautified_code = re.sub(
re.compile(
r"([ ]*{%-?)[ ]*(set)((?:(?!%}).)*?)(-?%})",
flags=re.IGNORECASE | re.MULTILINE | re.VERBOSE | re.DOTALL,
),
func,
beautified_code,
)

if not config.preserve_blank_lines:
beautified_code = beautified_code.lstrip()

Expand Down
1 change: 1 addition & 0 deletions src/djlint/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,7 @@ def __init__(
+ f"Error: Invalid pyproject.toml indent value {djlint_settings['indent']}"
)
indent = default_indent
self.indent_size = indent
self.indent: str = int(indent) * " "

default_exclude: str = r"""
Expand Down
2 changes: 1 addition & 1 deletion tests/test_html/test_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
" window.ga = function () { ga.q.push(arguments) }; ga.q = []; ga.l = +new Date;\n"
" ga('create', 'UA-XXXXX-Y', 'auto'); ga('send', 'pageview')\n"
" </script>\n"
' <script src="https://www.google-analytics.com/analytics.js" ASYNC DEFER></script>\n'
' <script src="https://www.google-analytics.com/analytics.js" ASYNC DEFER> </script>\n'
" </body>\n"
"</html>\n"
),
Expand Down
2 changes: 1 addition & 1 deletion tests/test_html/test_tag_script.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@
),
pytest.param(
('<script type="text/template">\n' " <div>\n" " </div>\n" "</script>\n"),
('<script type="text/template">\n' " <div></div>\n" "</script>\n"),
('<script type="text/template">\n' " <div>\n" " </div>\n" "</script>\n"),
id="something_else",
),
pytest.param(
Expand Down
73 changes: 69 additions & 4 deletions tests/test_nunjucks/test_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,90 @@
import pytest

from src.djlint.reformat import formatter
from tests.conftest import printer
from tests.conftest import config_builder, printer

test_data = [
pytest.param(
("{%- set posts = collections.docs -%}"),
("{%- set posts = collections.docs -%}\n"),
({}),
id="set",
),
pytest.param(
("{%-set posts = collections.docs-%}\n{%asdf%}"),
("{%- set posts = collections.docs -%}\n" "{% asdf %}\n"),
({}),
id="set_with_sibling",
),
pytest.param(
(
"{% set schema=[\n"
"{\n"
'"name":"id",\n'
'"type": "integer",\n'
'"primary": true,\n'
"id:1\n"
"},\n"
"{\n"
'"name": "name",\n'
'"type": "string"\n'
"}\n"
"] %}"
),
(
"{% set schema = [\n"
" {\n"
' name: "id",\n'
' type: "integer",\n'
" primary: true,\n"
" id: 1\n"
" },\n"
" {\n"
' name: "name",\n'
' type: "string"\n'
" }\n"
"] %}\n"
),
({"max_line_length": 10}),
id="indent multiilne",
),
pytest.param(
(
'{% set schema=[{"name": "id",\n'
'"type": "integer",\n'
'"primary": true\n'
"},] %}"
),
('{% set schema = [{name: "id", type: "integer", primary: true}] %}\n'),
({}),
id="indent invalid json",
),
pytest.param(
(
'{% set schema=[{name: "id",\n'
"'type': \"1\",\n"
'"primary+1": true\n'
"}] %}"
),
('{% set schema = [{name: "id", type: "1", "primary+1": true}] %}\n'),
({}),
id="indent valid json",
),
pytest.param(
(
'{% set table_keys = [ ( "date_started", "Start date"), ( "name", "Name" )] %}'
),
("{% set table_keys = [('date_started', 'Start date'), ('name', 'Name')] %}\n"),
({}),
id="indent py style list",
),
]


@pytest.mark.parametrize(("source", "expected"), test_data)
def test_base(source, expected, nunjucks_config):
output = formatter(nunjucks_config, source)
@pytest.mark.parametrize(("source", "expected", "args"), test_data)
def test_base(source, expected, args, nunjucks_config):
args["profile"] = "nunjucks"
output = formatter(config_builder(args), source)

printer(expected, source, output)
assert expected == output

0 comments on commit 85aca4c

Please sign in to comment.