Skip to content

Commit

Permalink
feat(formatter): added ability to format objects/arrays in function tags
Browse files Browse the repository at this point in the history
thanks @tomdavies and @oliver-schulz

closes #370
  • Loading branch information
christopherpickering committed May 18, 2023
1 parent 92c083b commit d446efc
Show file tree
Hide file tree
Showing 6 changed files with 192 additions and 42 deletions.
100 changes: 61 additions & 39 deletions src/djlint/formatter/indent.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,56 +319,78 @@ def fix_handlebars_template_tags(html: str, match: re.Match) -> str:
beautified_code = beautified_code + tmp

# 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()
def format_data(config: Config, contents: str, tag_size: int, leading_space) -> str:
try:
# try to format the contents as json
data = json.loads(contents)
contents = json.dumps(data, trailing_commas=False)

if tag_size + len(contents) >= config.max_line_length:
# if the line is too long we can indent the json
contents = json.dumps(
data, indent=config.indent_size, trailing_commas=False
)

except:
# was not json.. try to eval as set
try:
contents = str(eval(contents))
except:
contents = contents.strip()
return (f"\n{leading_space}").join(contents.splitlines())

def format_set(config: Config, match: re.Match) -> str:
leading_space = match.group(1)
open_bracket = match.group(2)
tag = match.group(3)
close_bracket = match.group(5)
contents = match.group(4).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)
contents = (
contents_split[0].strip()
+ " = "
+ format_data(
config,
contents_split[-1],
len(f"{open_bracket} {tag} {close_bracket}"),
leading_space,
)
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()
)
return f"{leading_space}{open_bracket} {tag} {contents} {close_bracket}"

def format_function(config: Config, match: re.Match) -> str:
leading_space = match.group(1)
open_bracket = match.group(2)
tag = match.group(3).strip()
close_bracket = match.group(5)
contents = format_data(
config,
match.group(4).strip()[1:-1],
len(f"{open_bracket} {tag}() {close_bracket}"),
leading_space,
)

return f"{open_bracket} {tag} {contents} {close_braket}"
return f"{leading_space}{open_bracket} {tag}({contents}) {close_bracket}"

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

func = partial(format_function, config)
# format function contents
beautified_code = re.sub(
re.compile(
r"([ ]*)({{)[ ]*?((?:(?!}}).)*?\w)(\([^\)]*?\)[ ]*)((?:(?!}}).)*?}})",
flags=re.IGNORECASE | re.MULTILINE | re.VERBOSE | re.DOTALL,
),
func,
Expand Down
2 changes: 1 addition & 1 deletion tests/test_html/test_attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
),
Expand Down
2 changes: 1 addition & 1 deletion tests/test_html/test_interpolation.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
" preserve\n"
" invalid\n"
" interpolation\n"
" }} reprehenderit voluptates minus {{ console.log( short_interpolation ) }} nemo.\n"
" }} reprehenderit voluptates minus {{ console.log(short_interpolation) }} nemo.\n"
"</div>\n"
),
id="interpolation_in_text",
Expand Down
2 changes: 1 addition & 1 deletion tests/test_jinja/test_parenthesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
test_data = [
pytest.param(
("{{ url('foo')}}"),
("{{ url('foo') }}\n"),
('{{ url("foo") }}\n'),
id="parenthesis_tag",
),
]
Expand Down
94 changes: 94 additions & 0 deletions tests/test_nunjucks/test_functions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
"""Test nunjucks functions.
poetry run pytest tests/test_nunjucks/test_functions.py
"""
import pytest

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

test_data = [
pytest.param(
(
"{{ myfunc({\n"
" bar: {\n"
" baz: {\n"
" cux: 1\n"
" }\n"
" }\n"
"})}}"
),
("{{ myfunc({bar: {baz: {cux: 1}}}) }}\n"),
({}),
id="long line",
),
pytest.param(
(
"{{ myfunc({\n"
" bar: {\n"
" baz: {\n"
" cux: 1\n"
" }\n"
" }\n"
"})}}"
),
(
"{{ myfunc({\n"
" bar: {\n"
" baz: {\n"
" cux: 1\n"
" }\n"
" }\n"
"}) }}\n"
),
({"max_line_length": 1}),
id="short line",
),
pytest.param(
(
"<div>{{ myfunc({\n"
" bar: {\n"
" baz: {\n"
" cux: 1\n"
" }\n"
" }\n"
"})}}</div>"
),
(
"<div>\n"
" {{ myfunc({\n"
" bar: {\n"
" baz: {\n"
" cux: 1\n"
" }\n"
" }\n"
" }) }}\n"
"</div>\n"
),
({"max_line_length": 1}),
id="nested",
),
pytest.param(
(
"{{ myfunc({\n"
" bar: {\n"
" baz: {\n"
" cux: 1\n"
" }\n"
" }\n"
"})}"
),
("{{ myfunc({\n" "bar: {\n" "baz: {\n" "cux: 1\n" "}\n" "}\n" "})}\n"),
({}),
id="broken",
),
]


@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
34 changes: 34 additions & 0 deletions tests/test_nunjucks/test_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,40 @@
({"max_line_length": 10}),
id="indent multiilne",
),
pytest.param(
(
"<div>{% 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"
"] %}</div>"
),
(
"<div>\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"
"</div>\n"
),
({"max_line_length": 10}),
id="nestedindent multiilne",
),
pytest.param(
(
'{% set schema=[{"name": "id",\n'
Expand Down

0 comments on commit d446efc

Please sign in to comment.