zola icon indicating copy to clipboard operation
zola copied to clipboard

Allow shortcodes in shortcode bodies

Open glfmn opened this issue 5 years ago • 12 comments

If a short-code with a body is used in the body of a short-code it's {% end %} is matched with the outer short-code. This prevents the use of nested short-codes with bodies.

Use Case

I'm trying to write reveal.js presentations in Gutenberg currently and I have defined simple shortcodes for vertical slides, fragments, and notes:

<aside class="notes">
{{ body | safe | trim | markdown }}
</aside>
<section{%- if id %} id="{{id}}" {%- endif -%}
        {%- if attributes -%}
          {%- for attr in attributes %}{#
            #} {{attr.attribute}}="{{attr.value}}"
          {%- endfor -%}
        {%- endif -%}>
{{ body | safe | markdown(inline=true) }}
</section>
<span class="fragment">
    {{ body | safe | trim | markdown }}
</span>

This makes it easier to write slides like so:

+++
title = "Installation instructions"
+++

{% vertical_slide() %}
Main content

{% notes() %}
- notes on main content
{% end %}
{% end %}


{% vertical_slide() %}
Basement slide with extra content

{% notes() %}
- notes on basement content
{% end %}
{% end %}

Workaround

Of course I can just throw in html to work around this, but I find short-codes are more pleasant to work with, especially when I have more complicated things going on like figures with complex markup that I've created with short-codes showing up in vertical slides.

For example:

{% vertical_slide() %}
**Linux** (Ubuntu/Debian)

`sudo apt-get install git`

<figure class="toggle-figure">
    <span class="toggle-figure__button"></span>
    <img class="toggle-figure__figure" alt="`sudo apt-get install git`" src="img/gif/linux-install-git.gif"/>
</figure>

<aside class="notes">
- Linux typically comes with `git` installed
- `git` was created specifically to manage the Linux project
</aside>
{% end %}

However, this isn't ideal since the list inside the aside is not rendered as markdown.

glfmn avatar Nov 05 '18 21:11 glfmn

Having shortcodes inside shortcodes is very unlikely to happen imo, it doesn't feel great and would probably be a bit messy since there are already issues with the shortcodes <> markdown interactions in some cases (https://github.com/Keats/gutenberg/issues/479)

If you have time you could write the code and I will have a look ('m not promising to merge it just to be clear).

Keats avatar Nov 07 '18 20:11 Keats

Is there anyone wanting to implement that? It looks like most of the work is actually parser-related, in components/rendering/shortcode.rs and the content.pest file in the same folder.

Keats avatar Mar 26 '19 18:03 Keats

@Keats I will be more than willing to implement this once I am done with my current project.

creikey avatar May 03 '19 01:05 creikey

Hi y'all,

Thanks for all of your efforts with Zola -- I really enjoy building sites with it! I just thought I'd chime in to say that I, too, would absolutely love to see nested shortcodes.

Obviously, I don't have the same perspective as you, @Keats, but for me personally, I think this feature would make the interaction with markdown much more natural -- right now, I'm writing a lot of repetitive HTML on my site, and I think that (a) nested shortcodes would actually simplify the page code greatly and (b) actually improve the interaction with markdown because inserting HTML breaks parsing, too.

I would also be thrilled to help with this (@creikey, would love to join forces or support you in any way!), but not being familiar with rust nor pest (humble web dev here 😉), I'm not sure how to get started. My very naive impression is that shortcode_with_body and text_in_body_sc in components/rendering/src/content.pest would need to be adapted to allow for nested shortcodes (so text_in_body_sc would be able to include shortcode_with_body as well as inline_shortcode), and render_shortcode in components/rendering/src/shortcode.rs would need to run recursively. Would that be a starting point?

Thanks a bunch, and again, please don't hesitate to let me know if I can help with this! (maybe I could try to make a test case #479?)

-Felix

FelixHenninger avatar Jun 04 '19 17:06 FelixHenninger

Thanks for the kind words!

Regarding the work to be done, I am not 100% sure as I haven't a look at that in ages. I think you nailed the parser changes but it's likely to be more complex when rendering them than just running recursively. I think https://github.com/getzola/zola/blob/fa0cf05fe0ab911a7e59c27cc7ab80fbcc9eff2c/components/rendering/src/shortcode.rs#L173 will need to be extracted to another function and https://github.com/getzola/zola/blob/fa0cf05fe0ab911a7e59c27cc7ab80fbcc9eff2c/components/rendering/src/shortcode.rs#L185 will need to call it instead of taking everything as a string. It doesn't look too hard but you never know...

Keats avatar Jun 04 '19 17:06 Keats

Hej, thanks a lot for your super-quick and kind response ... and no worries, this is the absolute least I can do!

I'm really enjoying working with Zola, and it's been an immense help with a new website I've been setting up -- I'd love to explore abstracting away some common elements that take up a lot of space in the code, and see whether that would make sense for Zola in general.

As noted above, I'm new to Pest and Rust, so I can't make any promises, but I'll play around with the pest online editor over the coming days to see whether I can get the parser running, and then maybe I'll have a go at the rendering code.

@creikey Please let me know if you'd like to take over, I wouldn't want to steal this from you if you've already made plans.

FelixHenninger avatar Jun 04 '19 23:06 FelixHenninger

@FelixHenninger I am very busy with Naval Battle and Godot Engine as of now, unlikely I'll start working on this in the near future.

creikey avatar Jun 05 '19 01:06 creikey

This would make pages with columns much simpler

Th3Whit3Wolf avatar Mar 24 '20 20:03 Th3Whit3Wolf

This is something that I too have been looking for in static site generators without success (I'm currently using Hugo). My use case is documentation, e.g.:

{{% note %}}
Read more about {{ doc "nginx#anchor" }} and see these {{ file "snippet.conf" "custom settings" }}.
{{% end %}}
  • The note code could expand to a <div>, itself containing header and body <div>s.
  • The doc code might search for nginx.md in the document tree, fetch its frontmatter title, embed the anchor in the href, but obscure it in the link text.
  • The file code might look in a custom data directory, produce a <span> with a file icon and become red (or throw an error at build time) if the file isn't found.

There are a lot of possibilities that allow one to write concise documentation. Having to use inline HTML or do manual validation is terrible for productivity, and any changes to recurring elements would have be reapplied in every document, instead of a single template file.

adrian5 avatar Apr 03 '20 21:04 adrian5

@Th3Whit3Wolf I have exactly the same use case! We currently have a workaround (shown below) by creating a .md file for each column and using the paginator, but it would be nice to be able to create rows and columns from within the same markdown file.

{% block content %}
  <!-- Main Content -->
  <div class="container">
    <div class="row">
      {% for page in paginator.pages %}
      {% if page.extra.col %}
      <div class="col-md-{{page.extra.col}} col-sm-12 mx-auto">
      {% else %}
      <div class="col-md-4 col-sm-12 mx-auto">
      {% endif %}
        <h2>{{ page.title }}</h2>
        {{ page.content | safe }}
      </div>
      {% endfor%}
    </div>
    {% endblock content %}

Riezebos avatar Jul 25 '20 09:07 Riezebos

Hi there, has there been any movement on this? I noticed that #1564 was merged, but it looks like that may not have resolved this issue. This is not a blocker for me as my outer-shortcode is just a single HTML tag, but I'd love to have this working to keep HTML out of my Markdown if possible 🙏

philpax avatar Feb 06 '22 06:02 philpax

No progress on that, it's been punted on as it's not a super important feature. Maybe after the 0.16 and more tests are added it should be possible for someone else to implement it

Keats avatar Feb 06 '22 20:02 Keats

Am I correct that this would allow resolving this issue https://github.com/getzola/zola/issues/375 as I could pass a colocated file name as an argument to a shortcode, and then let the shortcode do something like include "{{page.colocated_path}}{{name}}.html"

to include a pre-built html file in any part of the markdown page that I want (which is neat for organization, currently I'd have to move all co-located htmls to the templates/shortcode location to be able to import them within markdown files)?

eugenesvk avatar Apr 11 '23 07:04 eugenesvk

You can use the load_data function to load the file but include with a variable is not going to happen

Keats avatar Apr 11 '23 11:04 Keats

Commenting that I would also be interested in nested shortcodes. My attempted use case below:

use case.

As a theme writer I want to be able to have two stages of shortcodes where the first stage renders a markdown body that can then be further embedded within custom HTML. In the example below the snippet.md Markdown Shortcode fetches plaintext data to be rendered within a code block (i.e., ```) which is then passed as a body to the _source_url.html HTML Shortcode.

example.

snipped.md

{% if filename %}
	<!-- if a specific file within a multi-file snippet is specified, use it -->
	{% set snippet_url = url ~ "/raw/main/" ~ filename %}
{% else %}
	{% set snippet_url = url ~ "/raw" %}
{% endif %}

{% set source = load_data(url=snippet_url) %}

<a class="text-xs" href=url>source</a>    

{% _source_link(source_url=url) %}
```{{ file_type }}
{{ source }}
```
{% end %}

_source_url.html

<div class="source-link">
    {{ body }}
    <small><a href={{ source_url }}>source.</a></small>
</div>

workaround.

I opted to put my link above the code vs. under it to avoid padding issues and call the two short codes separately.

{{ source_link(url="https://gitlab.com/-/snippets/2549420") }}
{{ gitlab_snippet(url="https://gitlab.com/-/snippets/2549420", file_type="makefile", filename="Makefile")  }}

This has the downside of less control but reasonably checks the box (for now) of what I was looking for.

An alternative workaround could be to pass the snippet as a body and then abuse the {{ body }} variable but that has some code smell to it and I don't trust myself to remember the hackery next time I expect to touch this code.

mbaltrusitis avatar Jun 08 '23 20:06 mbaltrusitis