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

Add "inline" shortcode #4011

Closed
bep opened this issue Oct 26, 2017 · 26 comments · Fixed by #5471
Closed

Add "inline" shortcode #4011

bep opened this issue Oct 26, 2017 · 26 comments · Fixed by #5471
Assignees
Milestone

Comments

@bep
Copy link
Member

bep commented Oct 26, 2017

Also see #4010

This change is motivated by the Bootstrap docs, but this should be generally very useful. Given the example Markdown below:

https://raw.githubusercontent.com/twbs/bootstrap/v4-dev-xmr-hugo/docs/content/docs/4.0/about/team.md

All of this is currently possible with Hugo, but to get that loop you would probably end up with creating a shortcode, which is probably not what you want since this is used one time only.

So, I suggest we create some magic internal "inline shortcode wrapper" that allows shortcodes to be defined inside content files (i.e. scoped for that content file only).

So, combined with #4010 and the inline shortcode wrapper, the above example could look something like this:

---
layout: docs
title: Team
description: An overview of the founding team and core contributors to Bootstrap.
group: about
---

Bootstrap is maintained by the founding team and a small group of invaluable core contributors, with the massive support and involvement of our community.

{{< team.inline >}}
<div class="list-group bd-team">
  {{ range .Site.Data.core-team }}
    <div class="list-group-item">
      <iframe class="github-btn" src="https://ghbtns.com/github-btn.html?user={{ .user }}&amp;type=follow"></iframe>
      <a class="team-member" href="https://github.com/{{ .user }}">
        <img src="https://secure.gravatar.com/avatar/{{ .gravatar }}" alt="@{{ .user }}" width="32" height="32">
        <strong>{{ .name }}</strong> <small>@{{ .user }}</small>
      </a>
    </div>
  {{ end }}
</div>
{{< / team.inline >}}

Get involved with Bootstrap development by [opening an issue]({{< param "repo" >}}/issues/new) or submitting a pull request. Read our [contributing guidelines]({{< param "repo" >}}/blob/v{{< param "current_version" >}}/.github/CONTRIBUTING.md) for information on how we develop.

Some notes to the above:

  • I just quickly edited the above, so there may be errors -- but you get the drift.
  • I named with suffix ".inline" to mark it as something internal
  • I assume it is natural to pass the current Page as a context to this template; given that, the above would just work.
  • In the example above there are no parameter passing from markdown to template, but this would behave the same as for the regular shortcodes
  • Note that it should be possible to nest this shortcode inside, say, the highlight shortcode, which would solve some other issues I have seen in the Bootstrap docs.

Comments etc. are welcomed.

/cc @XhmikosR @moorereason @spf13 @digitalcraftsman and gang

More examples based on the comments below

So an "inline shortcode" has a name. As such, it should be possible to define it once and then reuse it later, possibly with different params.

So:

The **number one thing** to know about Hugo is:

{{< hugo_rules.inline "100" >}}
{{ range (seq (.Get 0)) }}Hugo Rules! {{ end }}
{{< / hugo_rules.inline >}}

If the above wasn't clear enough:

{{< hugo_rules.inline "300" />}}

So the valid names for inline shortcodes are any name that ends with "inline". You are responsible to keep them separated. Any repeated inline name will have its shortcode reused, which of course can be very useful (see above).

Note the use of the self-closing shortcode syntax in the last example (probably, let's see). We reuse the shortcode definition earlier in the same markdown file.

The order is important here, and this does not work across different files.

A general remark to the above: The main performance cost of shortcode is the template parsing, so the main use of shortcode will still be to put them in layouts/shortcodes. But this would be useful for those special use cases.

@bep bep added the Proposal label Oct 26, 2017
@galopin
Copy link

galopin commented Oct 26, 2017

I named it __inline to mark it as something internal, but I would appreciate a better name for this special shortcode

What about __lambda? Also could it be defined first and (re)used at whim within the document? I'm dreaming of a one-time/special-purpose shortcode that could be invoked as needed throughout the same content file…

@kaushalmodi
Copy link
Contributor

+1 for __lambda .. resonates well with lisps and even Python.

@kaushalmodi
Copy link
Contributor

What if one needs multiple one-time shortcodes in a content page?

@galopin
Copy link

galopin commented Oct 26, 2017

What if one needs multiple one-time shortcodes in a content page?

@kaushalmodi I think this use case is already covered as nothing prevents you from redefining (and executing) the anonymous shortcode elsewhere in the content file — an immediate analogy that comes to mind is the IIFE ("iffy") JavaScript idiom. But in order to invoke distinct inline shortcodes over and over, we should be given the ability to name things (e.g. labels ⁉️)…

@kaushalmodi
Copy link
Contributor

kaushalmodi commented Oct 26, 2017

we should be given the ability to name things

That's what I meant. May be this new construct acts like a macro that generates shortcodes with the specified name?

Example: {{< __inline foo >}} ..

Or may be something like: {{< define_scode foo >}} .. ?

@kaushalmodi
Copy link
Contributor

kaushalmodi commented Oct 26, 2017

The benefit of having a unique name is also searchability. If you have different versions of __inline in the same project, it can get kind of difficult to get to the __inline definition you really wanted.

@Jos512
Copy link

Jos512 commented Oct 26, 2017

I think it's a great feature, and Bep is really at it with significant improvements (fast render mode, related content, Chroma incorporation and improvements, plus upcoming bags and 'inlines'). Very impressive! 😄


What I don't like is the name __lambda. I think that, as Hugo grows, it pays to have terminology that's more accessible, and also more inclusive for people with all educational and work backgrounds. While I suspect that practically every backend developer understands how this new feature works when we say that 'Hugo also supports lambda functions in the content file'.

But for people that come from WordPress, Ghost, or another CMS might have no clue what lambdas are. And that technical term might make it harder for them to understand what's going on. Even front developers that know JavaScript might be unclear what it means.

(Interestingly, I see here a whole page from a JavaScript book writer to try explain Lambda functions in the context of JavaScript. I don't think that we should assume that other people also have a clear grasp of lambda functions.)

So I'm in favour of the name __inline because I think it's more descriptive for Hugo beginners.

@bep
Copy link
Member Author

bep commented Oct 26, 2017

I have adjusted my proposal a little + added one more example.

  • To the people suggesting lambda; what is the definition of that in the world of Lisp? (I still think I like inline better -- it was the first thing that popped into my head, which is usually a good sign).
  • I have added a shortcode name to the syntax to enable reuse. My first thought was to just add an internal counter to all the "internal shortcodes" in a document, but the above is cleaner I think.

@kaushalmodi
Copy link
Contributor

To the people suggesting lambda; what is the definition of that in the world of Lisp?

The lambda suggestion was before the development to named shortcodes. Here's a pretty good explanation of lambda from (E)lisp manual: http://www.gnu.org/software/emacs/manual/html_node/elisp/Lambda-Expressions.html

Instead of first defining the function and then calling it, you just throw in the anonymous function definition where a function call is expected.


Now with named inline shortcodes, the anonymous part doesn't hold true. So the update in the proposal looks good! 👍

@kaushalmodi
Copy link
Contributor

kaushalmodi commented Oct 26, 2017

@bep So to be clear, in the updated example:

{{< team.inline >}}
..
{{< / team.inline >}}

Get involved with Bootstrap ...

The place where you have the team.inline "definition" is where it gets applied, right? Then it doesn't need to be "called" separately? In that sense, it does feel like a lambda, and lambda definitely has a cooler ring to it then inline IMO.

As to point that @Jos512 raised about having terms accessible to general public.. just as the general public have to learn about the differences between template and short code, layouts and scopes of params, etc, lambda can be just one more thing. And folks who are already familiar with lambda can easily relate with this. WDYT?

@bep
Copy link
Member Author

bep commented Oct 26, 2017

The place where you have the team.inline "definition" is where it gets applied, right? Then it doesn't need to be "called" separately?

That is correct, and its main use case (my guess) is to use it once and forget about it. I just added the "name" as a requirement because there are several good reasons to have one (searching, reuse) -- and it makes the implementation slightly simpler.

So, a lambda is an anonymous function, which may on some level apply here. But it is a foreign word to many, even to me -- and I have been in the game to remember the talk about the year 2000 issue) ... So maybe the cool thing isn't really enough.

But, I also do agree that people learn fast -- and the important thing is that it is distinct.

@kaushalmodi
Copy link
Contributor

kaushalmodi commented Oct 26, 2017

So maybe the cool thing isn't really enough.

I didn't say that was the only reason.

Along with lisps, Python (which I believe has one of the largest mind-share in the developer/coder world) uses lambda too, apparently inspired from lisps): Python lambda expressions or this (one of the many Google results on searching Python lambda).


More on the Python lambda expressions linked above

The example has:

def make_incrementor(n):
    return lambda x: x + n

That basically generates an anonymous function that adds n to the input arg x and returns that sum. So instead of having a separate function that does that and calling it, lambda x: x + n does the same in a much concise fashion.

Your team.inline example does very much the same thing, except that we are just giving it a name (just in case it needs to be referred again, and for searching).

@galopin
Copy link

galopin commented Oct 26, 2017

I have added a shortcode name to the syntax to enable reuse. My first thought was to just add an internal counter to all the "internal shortcodes" in a document, but the above is cleaner I think.

@bep It is indeed. Thank you! 👍

What about the .macro suffix? Would it make more sense for the less tech-savvy users?

The **number one thing** to know about Hugo is:

{{< hugo_rules.macro "100" >}}
{{ range (seq (.Get 0)) }}Hugo Rules! {{ end }}
{{< / hugo_rules.macro >}}

If the above wasn't clear enough:

{{< hugo_rules.macro "300" />}}

@kaushalmodi
Copy link
Contributor

Sorry, one last thing on lambda and Go. So I googled (as I don't know Go :)) and apparently Go has something called function literals that are like lambdas:

@galopin
Copy link

galopin commented Oct 26, 2017

BTW, do you have to repeat the .inline, .lambda, .macro or whatever suffix for any subsequent invocation?

The **number one thing** to know about Hugo is:

{{< hugo_rules.macro "100" >}}
{{ range (seq (.Get 0)) }}Hugo Rules! {{ end }}
{{< / hugo_rules.macro >}}

If the above wasn't clear enough:

{{< hugo_rules "300" />}}

@bep
Copy link
Member Author

bep commented Oct 26, 2017

BTW, do you have to repeat the

I don't have to do anything. But it makes it unambiguous (i.e. what would I do in the above example if there was a shortcode in layouts/shortcodes/hugo_rules.html?).

I could add some complex logic, or I could keep it simple.

An added note to the above, that my current spec does not solve, is this:

For shortcodes in layouts/shortcodes you can have multiple versions of the same shortcode for different outputformats, i.e.:

note.html
note.amp.html

I have no idea how to support that in the inline variant, so I'm inclined to drop it for now. If no one has a brilliant idea ...

@galopin
Copy link

galopin commented Oct 26, 2017

IMHO inline shortcodes could be given precedence over standard shortcodes. That's the whole point of being able to define shortcodes in the scope of a content file. Is it possible in your current spec to nest standard shortcodes within inline shortcodes?

@bep
Copy link
Member Author

bep commented Oct 26, 2017

@galopin Nesting is possible, as mentioned above (for Bootstrap it would be great to wrap the highlight shortcode around an inline shortcode). Re. precedence: I'm not sure I follow the "that's the whole point" argument. The main point (i.e. the bulk of the "whole point") of the main example above is to create a "one time inline shortcode" (i.e. not overriding anything), which then is reused later in the same document? There is no talk about presedence in that usage ... So what is your take on this: To override the built-in figure shortcode with an inline definition?

Note that I'm not saying you don't have a point, just that I obviously have not thought about everything.

@spf13
Copy link
Contributor

spf13 commented Oct 26, 2017

I'm not sold on the concept of inline shortcodes. I'm not against them, but it's not very hard to define a shortcode. We are adding a reasonable amount of complexity for a small gain. I do see the advantage of not having to create another file, but only a small one.

If we do inline shortcodes there should be some clear rules around them.

  1. Used 1x only. If you want reuse, create a real shortcode.
  2. Anonymous. If you need a name, create a real shortcode.

There shouldn't be any precedence or conflict with existing shortcodes as these are anonymous. They shouldn't be used to "override" another shortcode.. they are simply different things.

@bep
Copy link
Member Author

bep commented Oct 26, 2017

@spf13 is correct. His "scope limiting" will not make every person happy, but it does make this a fairly simple task -- and it jumps over possibly impossible problems such as how to handle multiple output formats etc.

@spf13
Copy link
Contributor

spf13 commented Oct 26, 2017

also I feel quite strongly that "inline" is the right term to use. Adding a prefix such as "__inline" may solve the problem of "inline" not conflicting with a shortcode named "inline.html", though I'm also ok with that just being a prohibited name. Lambda is not a well understood term across most languages (both computer and speaking) and definitely not among most web creators. It also has different meanings in different programming languages, so using it here would add another definition to an already overloaded term.

@jgreely
Copy link

jgreely commented Oct 26, 2017

For the name, think about answering questions in the forums: "How do I put template code directly in my content files?".

  1. "You write an inline shortcode."
  2. "You define a lambda function".
  3. "You make a macro."
  4. "You embed the code."
  5. "You create a local shortcode."

The difference between templates and shortcodes is already a little fuzzy for new users, so I think it's important to pick the least-confusing term for a feature that lets you use template code in a content file.

One suggestion I'd make is to have a config parameter to enable the feature; not everyone wants their content creators writing code.

@bep
Copy link
Member Author

bep commented Oct 27, 2017

I have slept on this and have updated the spec in line with how it's going to be.

@galopin
Copy link

galopin commented Oct 27, 2017

So what is your take on this: To override the built-in figure shortcode with an inline definition?

@bep Exactly. When I define something in the local scope, I expect it to override anything defined in the global scope. I'm glad you did not remove the naming ability for inline shortcodes — using the .inline suffix systematically solves any shortcode name-collision issue 👍

Lambda is not a well understood term across most languages (both computer and speaking) […]

@spf13 Touché! I guess we could say the same about "partial", a term which has distinct meanings outside the context of a templating language 😉

[…] not everyone wants their content creators writing code.

@jgreely Lambda was merely a suggestion and not a recommendation. Strictly speaking, inserting an inline shortcode into a content file has nothing to do with programming but I understand your concern. Hugo seems to be stricter than Jekyll in the way it handles Markdown content files. Hence #4011 and #4013 are welcome features that will help people transition from other SSGs such as Jekyll. What I miss, for example, is the ability to capture the output of a whole block into a variable.

@stale
Copy link

stale bot commented Jan 25, 2018

This issue has been automatically marked as stale because it has not had recent activity. The resources of the Hugo team are limited, and so we are asking for your help.
If this is a bug and you can still reproduce this error on the master branch, please reply with all of the information you have about it in order to keep the issue open.
If this is a feature request, and you feel that it is still relevant and valuable, please tell us why.
This issue will automatically be closed in the near future if no further activity occurs. Thank you for all your contributions.

@stale stale bot added the Stale label Jan 25, 2018
@bep bep added Enhancement and removed Proposal labels Jan 25, 2018
@bep bep modified the milestones: v0.47, v0.48 Aug 3, 2018
@bep bep modified the milestones: v0.48, v0.49 Aug 22, 2018
@bep bep modified the milestones: v0.49, v0.50 Sep 13, 2018
@bep bep modified the milestones: v0.50, v0.51 Oct 6, 2018
@bep bep modified the milestones: v0.51, v0.53 Nov 8, 2018
@bep bep modified the milestones: v0.53, v0.52 Nov 26, 2018
@bep bep self-assigned this Nov 26, 2018
bep added a commit to bep/hugo that referenced this issue Nov 26, 2018
bep added a commit to bep/hugo that referenced this issue Nov 26, 2018
bep added a commit to bep/hugo that referenced this issue Nov 26, 2018
bep added a commit to bep/hugo that referenced this issue Nov 27, 2018
bep added a commit to bep/hugo that referenced this issue Nov 27, 2018
bep added a commit to bep/hugo that referenced this issue Nov 27, 2018
An inline shortcode's name must end with `.inline`, all lowercase.

E.g.:

```bash
{{< time.inline >}}{{ now }}{{< /time.inline >}}
```

The above will print the current date and time.

Note that an inline shortcode's inner content is parsed and executed as a Go text template with the same context as a regular shortcode template.

This means that the current page can be accessed via `.Page.Title` etc. This also means that there are no concept of "nested inline shortcodes".

The same inline shortcode can be reused later in the same content file, with different params if needed, using the self-closing syntax:

```
{{< time.inline />}}
```

Fixes gohugoio#4011
bep added a commit to bep/hugo that referenced this issue Nov 27, 2018
An inline shortcode's name must end with `.inline`, all lowercase.

E.g.:

```bash
{{< time.inline >}}{{ now }}{{< /time.inline >}}
```

The above will print the current date and time.

Note that an inline shortcode's inner content is parsed and executed as a Go text template with the same context as a regular shortcode template.

This means that the current page can be accessed via `.Page.Title` etc. This also means that there are no concept of "nested inline shortcodes".

The same inline shortcode can be reused later in the same content file, with different params if needed, using the self-closing syntax:

```
{{< time.inline />}}
```

Fixes gohugoio#4011
bep added a commit to bep/hugo that referenced this issue Nov 27, 2018
@bep bep closed this as completed in #5471 Nov 27, 2018
bep added a commit that referenced this issue Nov 27, 2018
An inline shortcode's name must end with `.inline`, all lowercase.

E.g.:

```bash
{{< time.inline >}}{{ now }}{{< /time.inline >}}
```

The above will print the current date and time.

Note that an inline shortcode's inner content is parsed and executed as a Go text template with the same context as a regular shortcode template.

This means that the current page can be accessed via `.Page.Title` etc. This also means that there are no concept of "nested inline shortcodes".

The same inline shortcode can be reused later in the same content file, with different params if needed, using the self-closing syntax:

```
{{< time.inline />}}
```

Fixes #4011
bep added a commit that referenced this issue Nov 27, 2018
@github-actions
Copy link

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 23, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants