djlint / djLint

✨ HTML Template Linter and Formatter. Django - Jinja - Nunjucks - Handlebars - GoLang
https://djLint.com
GNU General Public License v3.0
686 stars 84 forks source link

[BUG] [Formatter] Attribute access after function calls in Jinja gets extra space #704

Open klette opened 1 year ago

klette commented 1 year ago

System Info

Issue

When function formatting is enabled the following template is formated wrongly breaking the template.

{{ foo('foo').bar }}

becomes

{{ foo("foo") .bar }}

The simple case of attribute access directly after the function call is easy enough to fix by adding \w to the \d in the "index" capture group (matching group 5).

Here with some comments on the regex added during testing the issue:

if config.no_function_formatting is False:
        func = partial(format_function, config, beautified_code)
        # format function contents
        beautified_code = re.sub(
            re.compile(
                r"""
                ([ ]*)    # Matching Group 1: Matches any number of spaces at the beginning
                ({{-?\+?) # Matching Group 2: Matches '{{' followed by an optional '-' or '+' sign
                [ ]*?     # Matches any number of spaces after '{{-?+' (non-greedy)
                ((?:(?!}}).)*?\w) # Matching Group 3: Matches any character except '}}' followed by a word character (\w)
                (\((?:\"[^\"]*\"|'[^']*'|[^\)])*?\)[ ]*)  # Matching Group 4: Matches parentheses and their contents within '{{...}}'
                ((?:\[[^\]]*?\]|\.[\d\w]+)[ ]*)? # Matching Group 5: Matches square brackets or dot notation within '{{...}}'
                ((?:(?!}}).)*?-?\+?}}) # Matching Group 6: Matches any character except '}}' followed by an optional '-' or '+' sign and '}}'
                """,
                flags=re.IGNORECASE | re.MULTILINE | re.VERBOSE | re.DOTALL,
            ),
            func,
            beautified_code,
        )

But the repeated case requires a bit more work to fix I think.

How To Reproduce

Add the following test cases to tests/test_jinja/test_parenthesis.py:

    pytest.param(
        ("{{ url('foo').foo }}"),
        ('{{ url("foo").foo }}\n'),
        id="function_call_attribute_access",
    ),
    pytest.param(
        ("{{ url('foo').foo().bar[1] }}"),
        ('{{ url("foo").foo().bar[1] }}\n'),
        id="function_call_attribute_access_multiple",
    ),
welcome[bot] commented 1 year ago

Thanks for opening your first issue here!

christopherpickering commented 1 year ago

Thanks for reporting! You are welcome to do a pr with this, otherwise I will get it in a release next week.

christopherpickering commented 1 year ago

chained was pretty rough :D

christopherpickering commented 1 year ago

:tada: This issue has been resolved in version 1.32.0 :tada:

The release is available on:

Your semantic-release bot :package::rocket:

christopherpickering commented 1 year ago

I don't have time to get to #720 in the next few days so I will roll back this change until then.

christopherpickering commented 1 year ago

I did leave in a fix for the extra spaces, even in chained functions, but only the first function will be formatted currently.