A possible bug in the parser?

Hello, I’m experimenting with shortcodes and I believe I found a bug which I don’t know how to better explain.

See below the code of my (perhaps silly) shortcode and it’s output.

Content in the test.md file:

{{<concat "params" "test 123" "test 2" "test 3">}}<br>
{{<concat "mix">}}param inner test{{</concat>}}<br>   
{{<concat>}}param inner{{</concat>}}<br>

Code of my concat shortcode

{{- $string := "" -}}

{{- range .Params -}}
    {{- $string = printf "%s" . | printf "%s %s" $string -}}
{{- end -}}

{{- with .Inner -}}
    {{- $string = printf "%s" . | printf "%s %s" $string -}}
{{- end -}}

{{- $string = trim $string " " -}}
{{- printf "%s" $string | safeHTML -}}

With the above, the HTML printed is the follow:

params test 123 test 2 test 3 <br>
mix param inner test<br>   
param inner<br>

Some problems:

  • the first line ends with a space that is nowhere.
  • if I do remove the | safeHTML from the shortcode, the <br> in the content.md outside the parameters collection is escaped when it should be not.
  • I couldn’t figure out how to test for the presence of the .Params collection and eventually skip some code execution. If I do add a if eq (len .Params) 0, then on the third implementation the code fail because the .Params collection is a nil. What’s the best way to do such tests?

Are you 100% sure your concat shortcode does not print any space?

For your last question according to this message you need to do:

{{ if .Params }}
your code here...
{{ end }}

The <br> is escaped because of your use of .Inner. For the params only (e.g. first line, you need the {{< innershortcode "param1" "param2" />}} form of a shortcode). This is also why you get the space.

Note that unless you set you markup configuration for goldmark to allow raw HTML you will get <!-- raw HTML omitted --> instead of the <br> (standard Hugo behaviour for HTML in Markdown).

See also:

@bep Yes the concact indeed adds a space, but the trim later on should remove it, shouldn’t it?

@BenSouchet I’ve tried the if .Paramus, but as soon as the third variant of the shortcode kicks in, the error is raised. A nil exception.

@cshoredaniel is this happening because I’m using the .inner in general, concatenating the two strings (Params and inner)? I’ve to test it again without, but I’m fairly sure this was happening also with the former

@andreamoro Well there are actually a couple of different things going on. I tested by using {{<concat "params" "test 123" "test 2" "test 3" />}}<br> on the first line and got the results I mentioned (and no space).

I think the other issue is that trim returns a slice not a string

so you really ought to have something like:

{{- first 1 $string -}} instead of {{- printf "%s" $string -}}

The safeHTML is not necessary when you and stop having Hugo include the <br> as part of .Inner.

I would say there is a parse of sorts; Hugo probably should warn that there is an unclosed shortcode in your original example.

@cshoredaniel thanks for the hints. You say the first shortcode is not closed. Am I missing something?

Cannot shortcode exist in both ways, paired and not paired?
The idea was to be able to support both and intercept where the params where provided. So the first one is the version without pairing for which no closing is expected.

I’ve reread the doc again and a {{% concat "params" "test 123" "test 2" "test 3" %}} <br> should not read the <br> as it’s not part of the shortcode. It’s an html tag outside the brackets. So to me this is a bug, isn’t it??

On your second point, the value seems to be a string and not a slice. So if I start playing around with the first and after, I’m going to have the first letter printed and not up to the first slice. When you suggested so, I thought somehow I was working with an array like [" ", “something”, " "] which would have cut the last empty space. But this doesn’t seem to be the case.

You are right, I misread the ‘slice’ bit. You can take a ‘slice’ of a string. So that was a red herring. Sorry. That part I didn’t test (my mistake). The closing tag part I did.

For the first part

I think it’s a documentation bug. It seems that as soon as you have .Inner that the parser needs a closing tag. So you can have either a shortcode with no .Inner, which doesn’t need to be closed, or a shortcode with .Inner in the template which must be closed. Perhaps @jmooring or @bep can comment on the expected vs. observed vs. documented behaviour?

This is true, but your concat shortcode is producing the whitespace.

Go templates (which is not a Hugo thing) and whitespace can be a challenge some times, but Hugo’s shortcode feature does not add or remove any whitespace. This has come up as a theme many times before, and every time the source of the whitespace has had some natural explanation (e.g. the editor adding a trailing newline at the end of the template file).

@bep the template is intentionally white, no HTML at all, because I was keen to learn the inner bits. And the markdown file is as per the code

I’ll try to put a git together if I can during the next days.

That said, what can you say about that
being read?
And most importantly, even if my concat is adding the space, why the subsequent trim, or other methods tried later on can’t get rid of it?

Is there any other better way to concatenate strings?

@cshoredaniel’s initial response is correct.

First, if you want to include HTML in your markdown, even a br element, your site configuration must specify:

[markup.goldmark.renderer]
unsafe = true

Second, if a shortcode references .Inner, even if it is not rendered, you must close the shortcode call in your markdown. You can use either of the following constructs to close your shortcode:

{{< my-shortcode >}}{{< /my-shortcode >}}

OR

{{< my-shortcode />}}  

Third, because you did not close the initial shortcode call, the br element following the call is part of its .Inner content. So the space before the rendered br element is caused by the space between the two verbs in your printf statement.

printf "%s %s"
          ^

You can verify this by replacing the space between the verbs with a character:

printf "%sX%s"
1 Like

@jmooring thanks for confirming about the closing bit. Perhaps an opportunity to improve the documentation too?

Btw, I was not trying to have HTML rendered from within the shortcode, rather flagging that br outside being incorporated. I did find out about the setting to get HTML rendered already.

As for the space, yes, I appreciate it’s me adding it. That’s what I need with my concatenation shortcode, but I was also expecting to get rid of the first space in very front that neither the trim not the string.Trim* seems capable to remove.
The same for the first of after, which on my resulting end string find a string in front of them.

Any suggestion here too?

If you do what @cshoredaniel described, the page will be rendered to:

<p>params test 123 test 2 test 3<br>
mix param inner test<br>
param inner<br></p>

Try it:

git clone --single-branch -b hugo-forum-topic-39570 https://github.com/jmooring/hugo-testing hugo-forum-topic-39570
cd hugo-forum-topic-39570
hugo server
2 Likes

Thank you for clarifying my muddle of an explanation!

I’ve created an issue Clarify that a shortcode with `.Inner` must be closed · Issue #1784 · gohugoio/hugoDocs · GitHub and hope to create a PR after some more urgent things are taken care of.

1 Like