Does PHPStorm not support code folding for Twig?

I rely on code folding heavily to write code; I use it to remove things from my sight that are not relevant to the issue I'm working on - it helps remove distraction. I am finding that working with Twig in PHPStorm is far from optimal because I can't fold Twig properly.

For example, in the following code block I want to fold: The case block. The macro call. The form open tag.

In comparison with this same code, VSCode gets all this folding correct:

https://twitter.com/MattWilcox/status/1550418862360989696

5 comments
Comment actions Permalink

Could you please share the code samples as text? I'll extract each case as a feature request, but there's no fun in re-typing code from a screenshot. Thank you!

0
Comment actions Permalink

Of course, no problem. Here's the complete twig file:

{% import '_macros' as macros %}

{% if sprig.isRequest %}
    {% set form = wheelform.form({
        id: formId
    }) %}

    {% if success %}
        {% if formRedirect is defined and formRedirect | length %}
            <script>
                location.assign("{{ formRedirect }}");
            </script>
        {% endif %}

        <p class="message success">
            {{ wheelform.settings.success_message }}
        </p>
    {% else %}
        <form
            sprig
            s-method="post"
            s-encoding="multipart/form-data"
            s-action="wheelform/message/send"
        >
            <input type="hidden" name="form_id" value="{{ formId }}">
            {% if formRedirect | length %}
                <input type="hidden" name="redirect" value="{{ formRedirect }}">
            {% endif %}

            {{ wheelformErrors['form'] is defined      ? macros.errorList(wheelformErrors['form']) }}
            {{ wheelformErrors['recaptcha'] is defined ? macros.errorList(wheelformErrors['recaptcha']) }}
            {{ wheelformErrors['honeypot'] is defined  ? macros.errorList(wheelformErrors['honeypot']) }}

            {% for field in form.fields %}
                {% switch field.type %}
                    {% case 'hidden' %}
                        <input
                            type="hidden"
                            name="{{field.name}}"
                            value="{{ item }}"
                        />

                    {% case "radio" %}
                        {# Build the radio buttons array... #}
                            {% set options = [] %}
                            {% for item in field.items %}
                                {% set options = options | merge( [{
                                    type    : field.type,
                                    label   : item,
                                    value   : item,
                                    name    : field.name,
                                    id      : field.name ~ loop.index ~ formId,
                                    checked : values[field.name] is defined and item == values[field.name]
                                }] ) %}
                            {% endfor %}

                            {% set groupError = null %}
                            {% if wheelformErrors is defined and wheelformErrors[field.name] %}
                                {% set groupError = wheelformErrors[field.name][0] %}
                            {% endif %}

                            {{ macros.htmlRadioset({
                                legend      : field.options.display_label ? field.label ??? null,
                                legendStyle: 'dc_uppercase-adjusted',
                                required    : field.required,
                                error       : groupError,
                                options     : options
                            }) }}

                    {% case "checkbox" %}
                        {# Build the radio buttons array... #}
                            {% set options = [] %}
                            {% for item in field.items %}
                                {% set options = options | merge( [{
                                    type    : field.type,
                                    label   : item,
                                    value   : item,
                                    name    : field.name ~ '[]',
                                    id      : field.name ~ loop.index ~ formId,
                                    checked : values[field.name] is defined and item == values[field.name]
                                }] ) %}
                            {% endfor %}

                            {% set groupError = null %}
                            {% if wheelformErrors is defined and wheelformErrors[field.name] %}
                                {% set groupError = wheelformErrors[field.name][0] %}
                            {% endif %}

                            {{ macros.htmlRadioset({
                                legend      : field.options.display_label ? field.label ??? null,
                                legendStyle: 'dc_uppercase-adjusted',
                                required    : field.required,
                                error       : groupError,
                                options     : options
                            }) }}

                    {% case "select" %}
                        {% set options = [] %}
                        {% for item in field.items %}
                            {% set options = options | merge([
                                {
                                    label:    item,
                                    value:    item,
                                    selected: values[field.name] is defined and item == values[field.name]
                                }
                            ]) %}
                        {% endfor %}

                        {{ macros.htmlField({
                            type   : 'select',
                            id     : field.name ~ loop.index ~ formId,
                            name   : field.name,
                            label  : field.label ??? 'Field Label Not Set',
                            options: options,
                            error  : wheelformErrors[field.name] is defined ? wheelformErrors[field.name][0] : null
                        }) }}

                    {% case "textarea" %}
                        {{ macros.htmlField({
                            type        : 'textarea',
                            id          : field.name ~ loop.index ~ formId,
                            name        : field.name,
                            value       : (values is defined ? values[field.name] : ''),
                            required    : field.required,
                            label       : field.label ??? 'Field Label Not Set',
                            instructions: null,
                            error       : wheelformErrors[field.name] is defined ? wheelformErrors[field.name][0] : null
                        }) }}

                    {% case "file" %}
                        {{ macros.htmlField({
                            type        : 'file',
                            id          : field.name ~ loop.index ~ formId,
                            name        : field.name,
                            value       : (values is defined ? values[field.name] : ''),
                            required    : field.required,
                            label       : field.label ??? 'Field Label Not Set',
                            instructions: null,
                            error       : wheelformErrors[field.name] is defined ? wheelformErrors[field.name][0] : null
                        }) }}
                        <script>
                            var uploadField = document.getElementById("{{ field.name ~ loop.index ~ formId }}");

                            uploadField.onchange = function() {
                                if(this.files[0].size > 8388608){
                                alert("Sorry, the file size you are trying to upload is too big, please reduce the file size. ");
                                this.value = "";
                                };
                            };
                        </script>

                    {% case "html" %}
                        <div class="wheelform-html">
                            {{ field.content | raw }}
                        </div>

                    {% default %}
                        {{ macros.htmlField({
                            type        : 'text',
                            id          : field.name ~ loop.index ~ formId,
                            name        : field.name,
                            value       : (values is defined ? values[field.name] : ''),
                            required    : field.required,
                            label       : field.label ??? 'Field Label Not Set',
                            instructions: null,
                            error       : wheelformErrors[field.name] is defined ? wheelformErrors[field.name][0] : null
                        }) }}
                {% endswitch %}
            {% endfor %}

            {% if form.recaptcha %}
                <div>
                    <script src="https://www.google.com/recaptcha/api.js"></script>
                    <!-- Production captcha -->
                    <div class="g-recaptcha" data-sitekey="{{ wheelform.getSettings('recaptcha_public') }}"></div>
                </div>
            {% endif %}

            <div class="formActions">
                <div class="message">* Required fields</div>
                <button class="dc_button">Send Message</button>
            </div>
        </form>
    {% endif %}
{% else %}
    <p>Fetching form...</p>
{% endif %}
0
Comment actions Permalink

Thank you! I've extracted the switch case request:
https://youtrack.jetbrains.com/issue/WI-67995/Twig-Ability-to-fold-switch-case-blocks

Folding tag attributes isn't even supported in HTML; here's a feature request for that:
https://youtrack.jetbrains.com/issue/WEB-31463/Add-code-folding-for-wrapped-attributes-in-HTMLXML-tags-collapse-attributes

Do I get it right that when you say "macro call", you actually mean that you want to fold the hash you pass as an argument?
https://twig.symfony.com/doc/3.x/templates.html#literals

0
Comment actions Permalink

Great stuff, thank you.

I mean this block here:

{{ macros.htmlField({
type        : 'text',
id          : field.name ~ loop.index ~ formId,
name        : field.name,
value       : (values is defined ? values[field.name] : ''),
required    : field.required,
label       : field.label ??? 'Field Label Not Set',
instructions: null,
error       : wheelformErrors[field.name] is defined ? wheelformErrors[field.name][0] : null
}) }}

so ideally it'd fold to

{{ macros.htmlField({
}) }}

Which is what VSCode does.

0
Comment actions Permalink

Yep, it's a hash that has to be collapsed then. It makes sense to support it for arrays too.
Extracted: https://youtrack.jetbrains.com/issue/WI-68018/Twig-Folding-of-multiline-hashes-and-arrays

0

Please sign in to leave a comment.