mirror of https://github.com/gohugoio/hugo
189 lines
5.3 KiB
Go
189 lines
5.3 KiB
Go
// Copyright 2025 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 tpl contains template functions and related types.
|
|
package tpl
|
|
|
|
import (
|
|
"context"
|
|
"slices"
|
|
"strings"
|
|
"sync"
|
|
"unicode"
|
|
|
|
bp "github.com/gohugoio/hugo/bufferpool"
|
|
"github.com/gohugoio/hugo/common/hcontext"
|
|
"github.com/gohugoio/hugo/identity"
|
|
"github.com/gohugoio/hugo/langs"
|
|
|
|
htmltemplate "github.com/gohugoio/hugo/tpl/internal/go_templates/htmltemplate"
|
|
texttemplate "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate"
|
|
)
|
|
|
|
// Template is the common interface between text/template and html/template.
|
|
type Template interface {
|
|
Name() string
|
|
Prepare() (*texttemplate.Template, error)
|
|
}
|
|
|
|
// RenderingContext represents the currently rendered site/language.
|
|
type RenderingContext struct {
|
|
Site site
|
|
SiteOutIdx int
|
|
}
|
|
|
|
type (
|
|
contextKey uint8
|
|
)
|
|
|
|
const (
|
|
contextKeyDependencyManagerScopedProvider contextKey = iota
|
|
contextKeyDependencyScope
|
|
contextKeyPage
|
|
contextKeyIsInGoldmark
|
|
cntextKeyCurrentTemplateInfo
|
|
)
|
|
|
|
// Context manages values passed in the context to templates.
|
|
var Context = struct {
|
|
DependencyManagerScopedProvider hcontext.ContextDispatcher[identity.DependencyManagerScopedProvider]
|
|
GetDependencyManagerInCurrentScope func(context.Context) identity.Manager
|
|
DependencyScope hcontext.ContextDispatcher[int]
|
|
Page hcontext.ContextDispatcher[page]
|
|
IsInGoldmark hcontext.ContextDispatcher[bool]
|
|
CurrentTemplate hcontext.ContextDispatcher[*CurrentTemplateInfo]
|
|
}{
|
|
DependencyManagerScopedProvider: hcontext.NewContextDispatcher[identity.DependencyManagerScopedProvider](contextKeyDependencyManagerScopedProvider),
|
|
DependencyScope: hcontext.NewContextDispatcher[int](contextKeyDependencyScope),
|
|
Page: hcontext.NewContextDispatcher[page](contextKeyPage),
|
|
IsInGoldmark: hcontext.NewContextDispatcher[bool](contextKeyIsInGoldmark),
|
|
CurrentTemplate: hcontext.NewContextDispatcher[*CurrentTemplateInfo](cntextKeyCurrentTemplateInfo),
|
|
}
|
|
|
|
func init() {
|
|
Context.GetDependencyManagerInCurrentScope = func(ctx context.Context) identity.Manager {
|
|
idmsp := Context.DependencyManagerScopedProvider.Get(ctx)
|
|
if idmsp != nil {
|
|
return idmsp.GetDependencyManagerForScope(Context.DependencyScope.Get(ctx))
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
type page interface {
|
|
IsNode() bool
|
|
}
|
|
|
|
type site interface {
|
|
Language() *langs.Language
|
|
}
|
|
|
|
const (
|
|
// HugoDeferredTemplatePrefix is the prefix for placeholders for deferred templates.
|
|
HugoDeferredTemplatePrefix = "__hdeferred/"
|
|
// HugoDeferredTemplateSuffix is the suffix for placeholders for deferred templates.
|
|
HugoDeferredTemplateSuffix = "__d="
|
|
)
|
|
|
|
const hugoNewLinePlaceholder = "___hugonl_"
|
|
|
|
var stripHTMLReplacerPre = strings.NewReplacer("\n", " ", "</p>", hugoNewLinePlaceholder, "<br>", hugoNewLinePlaceholder, "<br />", hugoNewLinePlaceholder)
|
|
|
|
// StripHTML strips out all HTML tags in s.
|
|
func StripHTML(s string) string {
|
|
// Shortcut strings with no tags in them
|
|
if !strings.ContainsAny(s, "<>") {
|
|
return s
|
|
}
|
|
|
|
pre := stripHTMLReplacerPre.Replace(s)
|
|
preReplaced := pre != s
|
|
|
|
s = htmltemplate.StripTags(pre)
|
|
|
|
if preReplaced {
|
|
s = strings.ReplaceAll(s, hugoNewLinePlaceholder, "\n")
|
|
}
|
|
|
|
var wasSpace bool
|
|
b := bp.GetBuffer()
|
|
defer bp.PutBuffer(b)
|
|
for _, r := range s {
|
|
isSpace := unicode.IsSpace(r)
|
|
if !(isSpace && wasSpace) {
|
|
b.WriteRune(r)
|
|
}
|
|
wasSpace = isSpace
|
|
}
|
|
|
|
if b.Len() > 0 {
|
|
s = b.String()
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
// DeferredExecution holds the template and data for a deferred execution.
|
|
type DeferredExecution struct {
|
|
Mu sync.Mutex
|
|
Ctx context.Context
|
|
TemplatePath string
|
|
Data any
|
|
|
|
Executed bool
|
|
Result string
|
|
}
|
|
|
|
type CurrentTemplateInfoOps interface {
|
|
CurrentTemplateInfoCommonOps
|
|
Base() CurrentTemplateInfoCommonOps
|
|
}
|
|
|
|
type CurrentTemplateInfoCommonOps interface {
|
|
// Template name.
|
|
Name() string
|
|
// Template source filename.
|
|
// Will be empty for internal templates.
|
|
Filename() string
|
|
}
|
|
|
|
// CurrentTemplateInfo as returned in templates.Current.
|
|
type CurrentTemplateInfo struct {
|
|
Parent *CurrentTemplateInfo
|
|
CurrentTemplateInfoOps
|
|
}
|
|
|
|
// CurrentTemplateInfos is a slice of CurrentTemplateInfo.
|
|
type CurrentTemplateInfos []*CurrentTemplateInfo
|
|
|
|
// Reverse creates a copy of the slice and reverses it.
|
|
func (c CurrentTemplateInfos) Reverse() CurrentTemplateInfos {
|
|
if len(c) == 0 {
|
|
return c
|
|
}
|
|
r := make(CurrentTemplateInfos, len(c))
|
|
copy(r, c)
|
|
slices.Reverse(r)
|
|
return r
|
|
}
|
|
|
|
// Ancestors returns the ancestors of the current template.
|
|
func (ti *CurrentTemplateInfo) Ancestors() CurrentTemplateInfos {
|
|
var ancestors []*CurrentTemplateInfo
|
|
for ti.Parent != nil {
|
|
ti = ti.Parent
|
|
ancestors = append(ancestors, ti)
|
|
}
|
|
return ancestors
|
|
}
|