hugo/resources/page/page.go

421 lines
12 KiB
Go
Raw Normal View History

// Copyright 2019 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package page contains the core interfaces and types for the Page resource,
// a core component in Hugo.
package page
import (
"html/template"
"github.com/gohugoio/hugo/identity"
"github.com/bep/gitmap"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/tpl"
"github.com/gohugoio/hugo/common/maps"
"github.com/gohugoio/hugo/compare"
Introduce a tree map for all content This commit introduces a new data structure to store pages and their resources. This data structure is backed by radix trees. This simplies tree operations, makes all pages a bundle, and paves the way for #6310. It also solves a set of annoying issues (see list below). Not a motivation behind this, but this commit also makes Hugo in general a little bit faster and more memory effective (see benchmarks). Especially for partial rebuilds on content edits, but also when taxonomies is in use. ``` name old time/op new time/op delta SiteNew/Bundle_with_image/Edit-16 1.32ms ± 8% 1.00ms ± 9% -24.42% (p=0.029 n=4+4) SiteNew/Bundle_with_JSON_file/Edit-16 1.28ms ± 0% 0.94ms ± 0% -26.26% (p=0.029 n=4+4) SiteNew/Tags_and_categories/Edit-16 33.9ms ± 2% 21.8ms ± 1% -35.67% (p=0.029 n=4+4) SiteNew/Canonify_URLs/Edit-16 40.6ms ± 1% 37.7ms ± 3% -7.20% (p=0.029 n=4+4) SiteNew/Deep_content_tree/Edit-16 56.7ms ± 0% 51.7ms ± 1% -8.82% (p=0.029 n=4+4) SiteNew/Many_HTML_templates/Edit-16 19.9ms ± 2% 18.3ms ± 3% -7.64% (p=0.029 n=4+4) SiteNew/Page_collections/Edit-16 37.9ms ± 4% 34.0ms ± 2% -10.28% (p=0.029 n=4+4) SiteNew/Bundle_with_image-16 10.7ms ± 0% 10.6ms ± 0% -1.15% (p=0.029 n=4+4) SiteNew/Bundle_with_JSON_file-16 10.8ms ± 0% 10.7ms ± 0% -1.05% (p=0.029 n=4+4) SiteNew/Tags_and_categories-16 43.2ms ± 1% 39.6ms ± 1% -8.35% (p=0.029 n=4+4) SiteNew/Canonify_URLs-16 47.6ms ± 1% 47.3ms ± 0% ~ (p=0.057 n=4+4) SiteNew/Deep_content_tree-16 73.0ms ± 1% 74.2ms ± 1% ~ (p=0.114 n=4+4) SiteNew/Many_HTML_templates-16 37.9ms ± 0% 38.1ms ± 1% ~ (p=0.114 n=4+4) SiteNew/Page_collections-16 53.6ms ± 1% 54.7ms ± 1% +2.09% (p=0.029 n=4+4) name old alloc/op new alloc/op delta SiteNew/Bundle_with_image/Edit-16 486kB ± 0% 430kB ± 0% -11.47% (p=0.029 n=4+4) SiteNew/Bundle_with_JSON_file/Edit-16 265kB ± 0% 209kB ± 0% -21.06% (p=0.029 n=4+4) SiteNew/Tags_and_categories/Edit-16 13.6MB ± 0% 8.8MB ± 0% -34.93% (p=0.029 n=4+4) SiteNew/Canonify_URLs/Edit-16 66.5MB ± 0% 63.9MB ± 0% -3.95% (p=0.029 n=4+4) SiteNew/Deep_content_tree/Edit-16 28.8MB ± 0% 25.8MB ± 0% -10.55% (p=0.029 n=4+4) SiteNew/Many_HTML_templates/Edit-16 6.16MB ± 0% 5.56MB ± 0% -9.86% (p=0.029 n=4+4) SiteNew/Page_collections/Edit-16 16.9MB ± 0% 16.0MB ± 0% -5.19% (p=0.029 n=4+4) SiteNew/Bundle_with_image-16 2.28MB ± 0% 2.29MB ± 0% +0.35% (p=0.029 n=4+4) SiteNew/Bundle_with_JSON_file-16 2.07MB ± 0% 2.07MB ± 0% ~ (p=0.114 n=4+4) SiteNew/Tags_and_categories-16 14.3MB ± 0% 13.2MB ± 0% -7.30% (p=0.029 n=4+4) SiteNew/Canonify_URLs-16 69.1MB ± 0% 69.0MB ± 0% ~ (p=0.343 n=4+4) SiteNew/Deep_content_tree-16 31.3MB ± 0% 31.8MB ± 0% +1.49% (p=0.029 n=4+4) SiteNew/Many_HTML_templates-16 10.8MB ± 0% 10.9MB ± 0% +1.11% (p=0.029 n=4+4) SiteNew/Page_collections-16 21.4MB ± 0% 21.6MB ± 0% +1.15% (p=0.029 n=4+4) name old allocs/op new allocs/op delta SiteNew/Bundle_with_image/Edit-16 4.74k ± 0% 3.86k ± 0% -18.57% (p=0.029 n=4+4) SiteNew/Bundle_with_JSON_file/Edit-16 4.73k ± 0% 3.85k ± 0% -18.58% (p=0.029 n=4+4) SiteNew/Tags_and_categories/Edit-16 301k ± 0% 198k ± 0% -34.14% (p=0.029 n=4+4) SiteNew/Canonify_URLs/Edit-16 389k ± 0% 373k ± 0% -4.07% (p=0.029 n=4+4) SiteNew/Deep_content_tree/Edit-16 338k ± 0% 262k ± 0% -22.63% (p=0.029 n=4+4) SiteNew/Many_HTML_templates/Edit-16 102k ± 0% 88k ± 0% -13.81% (p=0.029 n=4+4) SiteNew/Page_collections/Edit-16 176k ± 0% 152k ± 0% -13.32% (p=0.029 n=4+4) SiteNew/Bundle_with_image-16 26.8k ± 0% 26.8k ± 0% +0.05% (p=0.029 n=4+4) SiteNew/Bundle_with_JSON_file-16 26.8k ± 0% 26.8k ± 0% +0.05% (p=0.029 n=4+4) SiteNew/Tags_and_categories-16 273k ± 0% 245k ± 0% -10.36% (p=0.029 n=4+4) SiteNew/Canonify_URLs-16 396k ± 0% 398k ± 0% +0.39% (p=0.029 n=4+4) SiteNew/Deep_content_tree-16 317k ± 0% 325k ± 0% +2.53% (p=0.029 n=4+4) SiteNew/Many_HTML_templates-16 146k ± 0% 147k ± 0% +0.98% (p=0.029 n=4+4) SiteNew/Page_collections-16 210k ± 0% 215k ± 0% +2.44% (p=0.029 n=4+4) ``` Fixes #6312 Fixes #6087 Fixes #6738 Fixes #6412 Fixes #6743 Fixes #6875 Fixes #6034 Fixes #6902 Fixes #6173 Fixes #6590
2019-09-10 09:26:34 +00:00
"github.com/gohugoio/hugo/hugofs/files"
"github.com/gohugoio/hugo/navigation"
"github.com/gohugoio/hugo/related"
"github.com/gohugoio/hugo/resources/resource"
"github.com/gohugoio/hugo/source"
)
// Clear clears any global package state.
func Clear() error {
spc.clear()
return nil
}
// AlternativeOutputFormatsProvider provides alternative output formats for a
// Page.
type AlternativeOutputFormatsProvider interface {
// AlternativeOutputFormats gives the alternative output formats for the
// current output.
// Note that we use the term "alternative" and not "alternate" here, as it
// does not necessarily replace the other format, it is an alternative representation.
AlternativeOutputFormats() OutputFormats
}
// AuthorProvider provides author information.
type AuthorProvider interface {
// Deprecated.
Author() Author
// Deprecated.
Authors() AuthorList
}
// ChildCareProvider provides accessors to child resources.
type ChildCareProvider interface {
Pages() Pages
// RegularPages returns a list of pages of kind 'Page'.
// In Hugo 0.57 we changed the Pages method so it returns all page
// kinds, even sections. If you want the old behaviour, you can
// use RegularPages.
RegularPages() Pages
2020-03-16 10:37:57 +00:00
// RegularPagesRecursive returns all regular pages below the current
// section.
RegularPagesRecursive() Pages
Resources() resource.Resources
}
// ContentProvider provides the content related values for a Page.
type ContentProvider interface {
Content() (any, error)
2022-04-21 08:59:13 +00:00
// Plain returns the Page Content stripped of HTML markup.
Plain() string
2022-04-21 08:59:13 +00:00
// PlainWords returns a string slice from splitting Plain using https://pkg.go.dev/strings#Fields.
PlainWords() []string
2022-04-21 08:59:13 +00:00
// Summary returns a generated summary of the content.
// The breakpoint can be set manually by inserting a summary separator in the source file.
Summary() template.HTML
2022-04-21 08:59:13 +00:00
// Truncated returns whether the Summary is truncated or not.
Truncated() bool
2022-04-21 08:59:13 +00:00
// FuzzyWordCount returns the approximate number of words in the content.
FuzzyWordCount() int
2022-04-21 08:59:13 +00:00
// WordCount returns the number of words in the content.
WordCount() int
2022-04-21 08:59:13 +00:00
// ReadingTime returns the reading time based on the length of plain text.
ReadingTime() int
2022-04-21 08:59:13 +00:00
// Len returns the length of the content.
Len() int
}
// FileProvider provides the source file.
type FileProvider interface {
File() source.File
}
// GetPageProvider provides the GetPage method.
type GetPageProvider interface {
// GetPage looks up a page for the given ref.
// {{ with .GetPage "blog" }}{{ .Title }}{{ end }}
//
// This will return nil when no page could be found, and will return
// an error if the ref is ambiguous.
GetPage(ref string) (Page, error)
// GetPageWithTemplateInfo is for internal use only.
GetPageWithTemplateInfo(info tpl.Info, ref string) (Page, error)
}
// GitInfoProvider provides Git info.
type GitInfoProvider interface {
GitInfo() *gitmap.GitInfo
2022-02-27 18:40:07 +00:00
CodeOwners() []string
}
// InSectionPositioner provides section navigation.
type InSectionPositioner interface {
NextInSection() Page
PrevInSection() Page
}
// InternalDependencies is considered an internal interface.
type InternalDependencies interface {
// GetRelatedDocsHandler is for internal use only.
GetRelatedDocsHandler() *RelatedDocsHandler
}
// OutputFormatsProvider provides the OutputFormats of a Page.
type OutputFormatsProvider interface {
OutputFormats() OutputFormats
}
// Page is the core interface in Hugo.
type Page interface {
ContentProvider
TableOfContentsProvider
PageWithoutContent
}
// PageMetaProvider provides page metadata, typically provided via front matter.
type PageMetaProvider interface {
// The 4 page dates
resource.Dated
// Aliases forms the base for redirects generation.
Aliases() []string
// BundleType returns the bundle type: `leaf`, `branch` or an empty string.
Introduce a tree map for all content This commit introduces a new data structure to store pages and their resources. This data structure is backed by radix trees. This simplies tree operations, makes all pages a bundle, and paves the way for #6310. It also solves a set of annoying issues (see list below). Not a motivation behind this, but this commit also makes Hugo in general a little bit faster and more memory effective (see benchmarks). Especially for partial rebuilds on content edits, but also when taxonomies is in use. ``` name old time/op new time/op delta SiteNew/Bundle_with_image/Edit-16 1.32ms ± 8% 1.00ms ± 9% -24.42% (p=0.029 n=4+4) SiteNew/Bundle_with_JSON_file/Edit-16 1.28ms ± 0% 0.94ms ± 0% -26.26% (p=0.029 n=4+4) SiteNew/Tags_and_categories/Edit-16 33.9ms ± 2% 21.8ms ± 1% -35.67% (p=0.029 n=4+4) SiteNew/Canonify_URLs/Edit-16 40.6ms ± 1% 37.7ms ± 3% -7.20% (p=0.029 n=4+4) SiteNew/Deep_content_tree/Edit-16 56.7ms ± 0% 51.7ms ± 1% -8.82% (p=0.029 n=4+4) SiteNew/Many_HTML_templates/Edit-16 19.9ms ± 2% 18.3ms ± 3% -7.64% (p=0.029 n=4+4) SiteNew/Page_collections/Edit-16 37.9ms ± 4% 34.0ms ± 2% -10.28% (p=0.029 n=4+4) SiteNew/Bundle_with_image-16 10.7ms ± 0% 10.6ms ± 0% -1.15% (p=0.029 n=4+4) SiteNew/Bundle_with_JSON_file-16 10.8ms ± 0% 10.7ms ± 0% -1.05% (p=0.029 n=4+4) SiteNew/Tags_and_categories-16 43.2ms ± 1% 39.6ms ± 1% -8.35% (p=0.029 n=4+4) SiteNew/Canonify_URLs-16 47.6ms ± 1% 47.3ms ± 0% ~ (p=0.057 n=4+4) SiteNew/Deep_content_tree-16 73.0ms ± 1% 74.2ms ± 1% ~ (p=0.114 n=4+4) SiteNew/Many_HTML_templates-16 37.9ms ± 0% 38.1ms ± 1% ~ (p=0.114 n=4+4) SiteNew/Page_collections-16 53.6ms ± 1% 54.7ms ± 1% +2.09% (p=0.029 n=4+4) name old alloc/op new alloc/op delta SiteNew/Bundle_with_image/Edit-16 486kB ± 0% 430kB ± 0% -11.47% (p=0.029 n=4+4) SiteNew/Bundle_with_JSON_file/Edit-16 265kB ± 0% 209kB ± 0% -21.06% (p=0.029 n=4+4) SiteNew/Tags_and_categories/Edit-16 13.6MB ± 0% 8.8MB ± 0% -34.93% (p=0.029 n=4+4) SiteNew/Canonify_URLs/Edit-16 66.5MB ± 0% 63.9MB ± 0% -3.95% (p=0.029 n=4+4) SiteNew/Deep_content_tree/Edit-16 28.8MB ± 0% 25.8MB ± 0% -10.55% (p=0.029 n=4+4) SiteNew/Many_HTML_templates/Edit-16 6.16MB ± 0% 5.56MB ± 0% -9.86% (p=0.029 n=4+4) SiteNew/Page_collections/Edit-16 16.9MB ± 0% 16.0MB ± 0% -5.19% (p=0.029 n=4+4) SiteNew/Bundle_with_image-16 2.28MB ± 0% 2.29MB ± 0% +0.35% (p=0.029 n=4+4) SiteNew/Bundle_with_JSON_file-16 2.07MB ± 0% 2.07MB ± 0% ~ (p=0.114 n=4+4) SiteNew/Tags_and_categories-16 14.3MB ± 0% 13.2MB ± 0% -7.30% (p=0.029 n=4+4) SiteNew/Canonify_URLs-16 69.1MB ± 0% 69.0MB ± 0% ~ (p=0.343 n=4+4) SiteNew/Deep_content_tree-16 31.3MB ± 0% 31.8MB ± 0% +1.49% (p=0.029 n=4+4) SiteNew/Many_HTML_templates-16 10.8MB ± 0% 10.9MB ± 0% +1.11% (p=0.029 n=4+4) SiteNew/Page_collections-16 21.4MB ± 0% 21.6MB ± 0% +1.15% (p=0.029 n=4+4) name old allocs/op new allocs/op delta SiteNew/Bundle_with_image/Edit-16 4.74k ± 0% 3.86k ± 0% -18.57% (p=0.029 n=4+4) SiteNew/Bundle_with_JSON_file/Edit-16 4.73k ± 0% 3.85k ± 0% -18.58% (p=0.029 n=4+4) SiteNew/Tags_and_categories/Edit-16 301k ± 0% 198k ± 0% -34.14% (p=0.029 n=4+4) SiteNew/Canonify_URLs/Edit-16 389k ± 0% 373k ± 0% -4.07% (p=0.029 n=4+4) SiteNew/Deep_content_tree/Edit-16 338k ± 0% 262k ± 0% -22.63% (p=0.029 n=4+4) SiteNew/Many_HTML_templates/Edit-16 102k ± 0% 88k ± 0% -13.81% (p=0.029 n=4+4) SiteNew/Page_collections/Edit-16 176k ± 0% 152k ± 0% -13.32% (p=0.029 n=4+4) SiteNew/Bundle_with_image-16 26.8k ± 0% 26.8k ± 0% +0.05% (p=0.029 n=4+4) SiteNew/Bundle_with_JSON_file-16 26.8k ± 0% 26.8k ± 0% +0.05% (p=0.029 n=4+4) SiteNew/Tags_and_categories-16 273k ± 0% 245k ± 0% -10.36% (p=0.029 n=4+4) SiteNew/Canonify_URLs-16 396k ± 0% 398k ± 0% +0.39% (p=0.029 n=4+4) SiteNew/Deep_content_tree-16 317k ± 0% 325k ± 0% +2.53% (p=0.029 n=4+4) SiteNew/Many_HTML_templates-16 146k ± 0% 147k ± 0% +0.98% (p=0.029 n=4+4) SiteNew/Page_collections-16 210k ± 0% 215k ± 0% +2.44% (p=0.029 n=4+4) ``` Fixes #6312 Fixes #6087 Fixes #6738 Fixes #6412 Fixes #6743 Fixes #6875 Fixes #6034 Fixes #6902 Fixes #6173 Fixes #6590
2019-09-10 09:26:34 +00:00
BundleType() files.ContentClass
// A configured description.
Description() string
// Whether this is a draft. Will only be true if run with the --buildDrafts (-D) flag.
Draft() bool
// IsHome returns whether this is the home page.
IsHome() bool
// Configured keywords.
Keywords() []string
// The Page Kind. One of page, home, section, taxonomy, term.
Kind() string
// The configured layout to use to render this page. Typically set in front matter.
Layout() string
// The title used for links.
LinkTitle() string
// IsNode returns whether this is an item of one of the list types in Hugo,
// i.e. not a regular content
IsNode() bool
// IsPage returns whether this is a regular content
IsPage() bool
// Param looks for a param in Page and then in Site config.
Param(key any) (any, error)
// Path gets the relative path, including file name and extension if relevant,
// to the source of this Page. It will be relative to any content root.
Path() string
// This is just a temporary bridge method. Use Path in templates.
// Pathc is for internal usage only.
Pathc() string
// The slug, typically defined in front matter.
Slug() string
// This page's language code. Will be the same as the site's.
Lang() string
// IsSection returns whether this is a section
IsSection() bool
// Section returns the first path element below the content root.
Section() string
// Returns a slice of sections (directories if it's a file) to this
// Page.
SectionsEntries() []string
// SectionsPath is SectionsEntries joined with a /.
SectionsPath() string
// Sitemap returns the sitemap configuration for this page.
Sitemap() config.Sitemap
// Type is a discriminator used to select layouts etc. It is typically set
// in front matter, but will fall back to the root section.
Type() string
// The configured weight, used as the first sort value in the default
// page sort if non-zero.
Weight() int
}
// PageRenderProvider provides a way for a Page to render content.
type PageRenderProvider interface {
Render(layout ...string) (template.HTML, error)
RenderString(args ...any) (template.HTML, error)
}
// PageWithoutContent is the Page without any of the content methods.
type PageWithoutContent interface {
RawContentProvider
resource.Resource
PageMetaProvider
resource.LanguageProvider
// For pages backed by a file.
FileProvider
GitInfoProvider
// Output formats
OutputFormatsProvider
AlternativeOutputFormatsProvider
// Tree navigation
ChildCareProvider
TreeProvider
2019-07-31 12:36:36 +00:00
// Horizontal navigation
InSectionPositioner
PageRenderProvider
PaginatorProvider
Positioner
navigation.PageMenusProvider
// TODO(bep)
AuthorProvider
// Page lookups/refs
GetPageProvider
RefProvider
resource.TranslationKeyProvider
TranslationsProvider
SitesProvider
// Helper methods
ShortcodeInfoProvider
compare.Eqer
2022-02-22 13:42:33 +00:00
// Scratch returns a Scratch that can be used to store temporary state.
// Note that this Scratch gets reset on server rebuilds. See Store() for a variant that survives.
maps.Scratcher
2022-02-22 13:42:33 +00:00
// Store returns a Scratch that can be used to store temporary state.
// In contrast to Scratch(), this Scratch is not reset on server rebuilds.
Store() *maps.Scratch
RelatedKeywordsProvider
2020-02-19 08:16:27 +00:00
// GetTerms gets the terms of a given taxonomy,
// e.g. GetTerms("categories")
GetTerms(taxonomy string) Pages
// Used in change/dependency tracking.
identity.Provider
DeprecatedWarningPageMethods
}
// Positioner provides next/prev navigation.
type Positioner interface {
Next() Page
Prev() Page
// Deprecated: Use Prev. Will be removed in Hugo 0.57
PrevPage() Page
// Deprecated: Use Next. Will be removed in Hugo 0.57
NextPage() Page
}
// RawContentProvider provides the raw, unprocessed content of the page.
type RawContentProvider interface {
RawContent() string
}
// RefProvider provides the methods needed to create reflinks to pages.
type RefProvider interface {
Ref(argsm map[string]any) (string, error)
// RefFrom is for internal use only.
RefFrom(argsm map[string]any, source any) (string, error)
RelRef(argsm map[string]any) (string, error)
// RefFrom is for internal use only.
RelRefFrom(argsm map[string]any, source any) (string, error)
}
// RelatedKeywordsProvider allows a Page to be indexed.
type RelatedKeywordsProvider interface {
// Make it indexable as a related.Document
// RelatedKeywords is meant for internal usage only.
RelatedKeywords(cfg related.IndexConfig) ([]related.Keyword, error)
}
// ShortcodeInfoProvider provides info about the shortcodes in a Page.
type ShortcodeInfoProvider interface {
// HasShortcode return whether the page has a shortcode with the given name.
// This method is mainly motivated with the Hugo Docs site's need for a list
// of pages with the `todo` shortcode in it.
HasShortcode(name string) bool
}
// SitesProvider provide accessors to get sites.
type SitesProvider interface {
Site() Site
Sites() Sites
}
// TableOfContentsProvider provides the table of contents for a Page.
type TableOfContentsProvider interface {
TableOfContents() template.HTML
}
// TranslationsProvider provides access to any translations.
type TranslationsProvider interface {
// IsTranslated returns whether this content file is translated to
// other language(s).
IsTranslated() bool
// AllTranslations returns all translations, including the current Page.
AllTranslations() Pages
// Translations returns the translations excluding the current Page.
Translations() Pages
}
// TreeProvider provides section tree navigation.
type TreeProvider interface {
// IsAncestor returns whether the current page is an ancestor of the given
// Note that this method is not relevant for taxonomy lists and taxonomy terms pages.
IsAncestor(other any) (bool, error)
// CurrentSection returns the page's current section or the page itself if home or a section.
// Note that this will return nil for pages that is not regular, home or section pages.
CurrentSection() Page
// IsDescendant returns whether the current page is a descendant of the given
// Note that this method is not relevant for taxonomy lists and taxonomy terms pages.
IsDescendant(other any) (bool, error)
// FirstSection returns the section on level 1 below home, e.g. "/docs".
// For the home page, this will return itself.
FirstSection() Page
// InSection returns whether the given page is in the current section.
// Note that this will always return false for pages that are
// not either regular, home or section pages.
InSection(other any) (bool, error)
// Parent returns a section's parent section or a page's section.
// To get a section's subsections, see Page's Sections method.
Parent() Page
// Sections returns this section's subsections, if any.
// Note that for non-sections, this method will always return an empty list.
Sections() Pages
// Page returns a reference to the Page itself, kept here mostly
// for legacy reasons.
Page() Page
}
// DeprecatedWarningPageMethods lists deprecated Page methods that will trigger
// a WARNING if invoked.
// This was added in Hugo 0.55.
type DeprecatedWarningPageMethods any // This was emptied in Hugo 0.93.0.
// Move here to trigger ERROR instead of WARNING.
// TODO(bep) create wrappers and put into the Page once it has some methods.
type DeprecatedErrorPageMethods any