gohugoio / hugo

The world’s fastest framework for building websites.
https://gohugo.io
Apache License 2.0
75k stars 7.47k forks source link

Add Page.Markup with scope support #12759

Closed bep closed 1 month ago

bep commented 1 month ago

Edit in 2024-08-16. I have revised my take on this after talking to @jmooring and thinking a little. The (original) primary motivation behind this PR (for me, anyway) was to make it possible to render different versions of a given page's content on, say, the home page. But we also want to make room for further improvements in this area without blowing the scope of this PR out of proportions. I have always wanted to create a map of the rendered content, but that would only be practical for Goldmark (and hard even there, I guess). I think that and similar extensions should be fairly simple to fit in the below.


With this you can do:

{{ with .Markup }}
  {{ with .Render }}
    {{ .Content }} 
    {{ .WordCount }} // ... etc.
  {{ end }}
{{ end }}

You can also give it its own scope:

{{ range site.RegularPages | first 20 }}
  {{ with .Markup "home" }}
    {{ .Render.Content }}
  {{ end }}
{{ end }}
type Markup interface {
    Render(context.Context) (Content, error)
    RenderString(ctx context.Context, args ...any) (template.HTML, error)
    RenderShortcodes(context.Context) (template.HTML, error)
    Fragments(context.Context) *tableofcontents.Fragments
}

type Content interface {
        Content(context.Context) template.HTML
    ContentWithoutSummary(context.Context) template.HTML
    Summary(context.Context) Summary
    Plain(context.Context) string
    PlainWords(context.Context) []string
    WordCount(context.Context) int
    FuzzyWordCount(context.Context) int
    ReadingTime(context.Context) int
    Len(context.Context) int
}

type Summary struct {
    Text      template.HTML
    Type      string // "auto" or "manual".
    Truncated bool
}

Fixes #8680

bep commented 1 month ago

Looking at the above, I like most things about it; we greatly reduce the amount of methods on the giga Page interface (making it easier to understand, I think) and the introduction of "scoped" content rendering can solve many hard-to-workaround issues reported by users.

But I don't like the main name ...

{{ .Contents.Render }}

The above does not read well, and I think .Contents is confusingly too similar with what we had. So, instead of trying to find a term that describes what this is I thought we could find a term that describes what this is about, and also use the same term we use about this in the configuration:

{{ .Markup.Render }}

Or ...

{{ with .Markup }}
   {{ .Render }}
{{ end }}

Which is what we do, we render the markup (Markdown, HTML, Pandoc ...) to one or more output formats.

Also, if we could somehow get the scope key above into the markup configuration, it would be useful for people to have some custom config for e.g. rendering of pages on the home page.

/cc @jmooring

jmooring commented 1 month ago

I've tested this a bit. It works as expected except for ContentWithoutSummary which is expected at this point.

I did not play with the scope as described above because:

  1. I don't understand what it does
  2. I don't understand where I would want to use it

Both of these are documentation issues.

And while I appreciate grouping related items, I believe most users would prefer to do this:

{{ .Content }}
{{ .WordCount }}

Instead of this:

{{ .Markup.Render.Content }}
{{ .Markup.Render.WordCount }}

I also think the former is easier to understand.

So your intention to leave existing methods as-is makes sense to me.

bep commented 1 month ago

@jmooring I have now pushed a version that passes all tests, including ContentWithoutSummary.

I don't understand what it does

So, scope is a little specialised, but questions related to this pops up on the forum (and here) from time to time. From the top of my head I can mention 2:

There are others and better examples, I'm sure, but those are recent ones.

So, when doing something like this in the home page template:

{{ range site.RegularPages | first 20 }}
  {{ with .Markup "home" }}
    {{ .Render.Content }}
  {{ end }}
{{ end }}

The home page will get a fresh render of all of those 20 pages (home acts as a cache key); any shortcode/render hook template used doing that will have page.IsHome == true and hugo.MarkupScope == home.

There's a performance cost to the above, of course, so it should be ... used with care.

bep commented 1 month ago

And while I appreciate grouping related items, I believe most users would prefer to do this:

I will keep these top level Page content methods for now, but at some point it may be a question about the cost of maintaining these in relation to what "users prefer". The motivation behind this grouping isn't primarily cosmetic.