hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

template.go (5593B)

    1 // Copyright 2019 The Hugo Authors. All rights reserved.
    2 //
    3 // Licensed under the Apache License, Version 2.0 (the "License");
    4 // you may not use this file except in compliance with the License.
    5 // You may obtain a copy of the License at
    6 // http://www.apache.org/licenses/LICENSE-2.0
    7 //
    8 // Unless required by applicable law or agreed to in writing, software
    9 // distributed under the License is distributed on an "AS IS" BASIS,
   10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   11 // See the License for the specific language governing permissions and
   12 // limitations under the License.
   13 
   14 package tpl
   15 
   16 import (
   17 	"context"
   18 	"io"
   19 	"reflect"
   20 	"regexp"
   21 	"strings"
   22 	"unicode"
   23 
   24 	bp "github.com/gohugoio/hugo/bufferpool"
   25 
   26 	"github.com/gohugoio/hugo/output"
   27 
   28 	htmltemplate "github.com/gohugoio/hugo/tpl/internal/go_templates/htmltemplate"
   29 	texttemplate "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate"
   30 )
   31 
   32 // TemplateManager manages the collection of templates.
   33 type TemplateManager interface {
   34 	TemplateHandler
   35 	TemplateFuncGetter
   36 	AddTemplate(name, tpl string) error
   37 	MarkReady() error
   38 }
   39 
   40 // TemplateVariants describes the possible variants of a template.
   41 // All of these may be empty.
   42 type TemplateVariants struct {
   43 	Language     string
   44 	OutputFormat output.Format
   45 }
   46 
   47 // TemplateFinder finds templates.
   48 type TemplateFinder interface {
   49 	TemplateLookup
   50 	TemplateLookupVariant
   51 }
   52 
   53 // UnusedTemplatesProvider lists unused templates if the build is configured to track those.
   54 type UnusedTemplatesProvider interface {
   55 	UnusedTemplates() []FileInfo
   56 }
   57 
   58 // TemplateHandler finds and executes templates.
   59 type TemplateHandler interface {
   60 	TemplateFinder
   61 	Execute(t Template, wr io.Writer, data any) error
   62 	ExecuteWithContext(ctx context.Context, t Template, wr io.Writer, data any) error
   63 	LookupLayout(d output.LayoutDescriptor, f output.Format) (Template, bool, error)
   64 	HasTemplate(name string) bool
   65 }
   66 
   67 type TemplateLookup interface {
   68 	Lookup(name string) (Template, bool)
   69 }
   70 
   71 type TemplateLookupVariant interface {
   72 	// TODO(bep) this currently only works for shortcodes.
   73 	// We may unify and expand this variant pattern to the
   74 	// other templates, but we need this now for the shortcodes to
   75 	// quickly determine if a shortcode has a template for a given
   76 	// output format.
   77 	// It returns the template, if it was found or not and if there are
   78 	// alternative representations (output format, language).
   79 	// We are currently only interested in output formats, so we should improve
   80 	// this for speed.
   81 	LookupVariant(name string, variants TemplateVariants) (Template, bool, bool)
   82 	LookupVariants(name string) []Template
   83 }
   84 
   85 // Template is the common interface between text/template and html/template.
   86 type Template interface {
   87 	Name() string
   88 	Prepare() (*texttemplate.Template, error)
   89 }
   90 
   91 // TemplateParser is used to parse ad-hoc templates, e.g. in the Resource chain.
   92 type TemplateParser interface {
   93 	Parse(name, tpl string) (Template, error)
   94 }
   95 
   96 // TemplateParseFinder provides both parsing and finding.
   97 type TemplateParseFinder interface {
   98 	TemplateParser
   99 	TemplateFinder
  100 }
  101 
  102 // TemplateDebugger prints some debug info to stdout.
  103 type TemplateDebugger interface {
  104 	Debug()
  105 }
  106 
  107 // templateInfo wraps a Template with some additional information.
  108 type templateInfo struct {
  109 	Template
  110 	Info
  111 }
  112 
  113 // templateInfo wraps a Template with some additional information.
  114 type templateInfoManager struct {
  115 	Template
  116 	InfoManager
  117 }
  118 
  119 // TemplatesProvider as implemented by deps.Deps.
  120 type TemplatesProvider interface {
  121 	Tmpl() TemplateHandler
  122 	TextTmpl() TemplateParseFinder
  123 }
  124 
  125 // WithInfo wraps the info in a template.
  126 func WithInfo(templ Template, info Info) Template {
  127 	if manager, ok := info.(InfoManager); ok {
  128 		return &templateInfoManager{
  129 			Template:    templ,
  130 			InfoManager: manager,
  131 		}
  132 	}
  133 
  134 	return &templateInfo{
  135 		Template: templ,
  136 		Info:     info,
  137 	}
  138 }
  139 
  140 var baseOfRe = regexp.MustCompile("template: (.*?):")
  141 
  142 func extractBaseOf(err string) string {
  143 	m := baseOfRe.FindStringSubmatch(err)
  144 	if len(m) == 2 {
  145 		return m[1]
  146 	}
  147 	return ""
  148 }
  149 
  150 // TemplateFuncGetter allows to find a template func by name.
  151 type TemplateFuncGetter interface {
  152 	GetFunc(name string) (reflect.Value, bool)
  153 }
  154 
  155 // GetDataFromContext returns the template data context (usually .Page) from ctx if set.
  156 // NOte: This is not fully implemented yet.
  157 func GetDataFromContext(ctx context.Context) any {
  158 	return ctx.Value(texttemplate.DataContextKey)
  159 }
  160 
  161 func GetHasLockFromContext(ctx context.Context) bool {
  162 	if v := ctx.Value(texttemplate.HasLockContextKey); v != nil {
  163 		return v.(bool)
  164 	}
  165 	return false
  166 }
  167 
  168 func SetHasLockInContext(ctx context.Context, hasLock bool) context.Context {
  169 	return context.WithValue(ctx, texttemplate.HasLockContextKey, hasLock)
  170 }
  171 
  172 const hugoNewLinePlaceholder = "___hugonl_"
  173 
  174 var (
  175 	stripHTMLReplacerPre = strings.NewReplacer("\n", " ", "</p>", hugoNewLinePlaceholder, "<br>", hugoNewLinePlaceholder, "<br />", hugoNewLinePlaceholder)
  176 	whitespaceRe         = regexp.MustCompile(`\s+`)
  177 )
  178 
  179 // StripHTML strips out all HTML tags in s.
  180 func StripHTML(s string) string {
  181 	// Shortcut strings with no tags in them
  182 	if !strings.ContainsAny(s, "<>") {
  183 		return s
  184 	}
  185 
  186 	pre := stripHTMLReplacerPre.Replace(s)
  187 	preReplaced := pre != s
  188 
  189 	s = htmltemplate.StripTags(pre)
  190 
  191 	if preReplaced {
  192 		s = strings.ReplaceAll(s, hugoNewLinePlaceholder, "\n")
  193 	}
  194 
  195 	var wasSpace bool
  196 	b := bp.GetBuffer()
  197 	defer bp.PutBuffer(b)
  198 	for _, r := range s {
  199 		isSpace := unicode.IsSpace(r)
  200 		if !(isSpace && wasSpace) {
  201 			b.WriteRune(r)
  202 		}
  203 		wasSpace = isSpace
  204 	}
  205 
  206 	if b.Len() > 0 {
  207 		s = b.String()
  208 	}
  209 
  210 	return s
  211 }