Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Manual whitespace control on comments are ignored #200

Closed
brycelelbach opened this issue Jun 4, 2021 · 1 comment
Closed

Manual whitespace control on comments are ignored #200

brycelelbach opened this issue Jun 4, 2021 · 1 comment
Labels

Comments

@brycelelbach
Copy link
Contributor

brycelelbach commented Jun 4, 2021

Inja allows you to specify that whitespace should be stripped either before or after an expression or statement by adding a minus:

{{ dont_strip }}
{{- strip_before }}
{{ strip_after -}}
{{- strip_before_and_after -}}
{% set dont_strip = true %}
{%- set strip_before = true %}
{% set stip_after = true -%}
{%- set strip_before_and_after = true -%} 

However, while this syntax is accepted for Inja comments (e.g. {# ... #}), it has no effect. Adding manual whitespace control to comments is ignored.

{#- Doesn't strip before -#}
{# Doesn't strip after. -#}
{#- Doesn't strip before and after -#}

Inserting an Inja comment after a statement or expression with manual whitespace control can lead to surprising results, because the manual whitespace control from the statement or expression preceding the Inja comment will end at the start of the comment.

{{ strip_until_newline -}}
{{ strip_until_newline -}}{# The expression will only strip to the comment and won't eat the newline! -#}

Here's a full example of this problem (check it out on Godbolt):

inja::Environment env;

nlohmann::json data;
data["A"] = true;

char const* template_with_comments =
R"(START
{%- if exists("A") -%}
  {#- Some documentation.. -#}
  A
{%- endif -%}
END

)";

char const* template_without_comments =
R"(START
{%- if exists("A") -%}
  A
{%- endif -%}
END

)";

env.add_void_callback("noop", 0, [](inja::Arguments& args) {});

char const* template_with_nooped_comments =
R"(START
{%- if exists("A") -%}
  {#- Some documentation. -#}{{ noop() -}}
  A
{%- endif -%}
END

)";

std::cout
  << "template_with_comments        : " << env.render(template_with_comments,        data)
  << "template_without_comments     : " << env.render(template_without_comments,     data)
  << "template_with_nooped_comments : " << env.render(template_with_nooped_comments, data)
  ;

In the above example, template_with_comments produces this output:

START

  A
END

As you can see, the manual whitespace control is ignored - newline after the comment and the two spaces at the start of the next line are not removed.

template_with_nooped_comments shows a trick that I use to work around this. I add a noop callback to the environment which does nothing, and then use that to insert expressions that do nothing other than trigger whitespace trimming. The noop callback is necessary because empty expressions ({{}}, {{- -}}, etc) aren't allowed.

I use this noop trick in other places where I need to manually insert whitespace control too. But if comments supported manual whitespace control, or if empty expressions were allowed, I could use those instead of this noop mechanism to insert whitespace control where needed.

Here's an example (check it out on Godbolt):

inja::Environment env;

nlohmann::json data;
data["children"] = {
  {{"url", "https://a.com"}, {"name", "A"}},
  {{"url", "https://b.com"}, {"name", "B"}},
  {{"url", "https://c.com"}, {"name", "C"}}
};


char const* template_with_comments =
R"(START
{%- if exists("children") -%}
  {%- for child in children -%}
    <div>{# -#}
      <b><a href="{{ child.url }}">{{ child.name }}</a></b>{# -#}
    </div>
  {%- endfor -%}
{%- endif -%}
END

)";

env.add_void_callback("noop", 0, [](inja::Arguments& args) {});

char const* template_with_noops =
R"(START
{%- if exists("children") -%}
  {%- for child in children -%}
    <div>{{ noop() -}}
      <b><a href="{{ child.url }}">{{ child.name }}</a></b>{{ noop() -}}
    </div>
  {%- endfor -%}
{%- endif -%}
END

)";

std::cout
  << "template_with_comments: " << env.render(template_with_comments, data)
  << "template_with_noops:    " << env.render(template_with_noops,    data) 
  ;

In the above example today, template_with_comments produces the following output:

START
<div>
      <b><a href="https://a.com">A</a></b>
    </div>
<div>
      <b><a href="https://b.com">B</a></b>
    </div>
<div>
      <b><a href="https://c.com">C</a></b>
    </div>
END

In the above example, template_with_noops produces the following output:

START
<div><b><a href="https://a.com">A</a></b></div>
<div><b><a href="https://b.com">B</a></b></div>
<div><b><a href="https://c.com">C</a></b></div>
END

Which is exactly what I want - it condenses the three lines in the template for the components of the <div> into a single line, and removes leading whitespace.

@pantor
Copy link
Owner

pantor commented Jun 9, 2021

Thanks for your detailed bug report! Indeed, comments were ignored regarding whitespace control so far, but I've changed that with the latest commit on the master branch. I hope that fixes this issue.

@pantor pantor closed this as completed Jun 12, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants