hugo/tpl/tplimpl/shortcodes.go
Bjørn Erik Pedersen 597e418cb0
Make Page an interface
The main motivation of this commit is to add a `page.Page` interface to replace the very file-oriented `hugolib.Page` struct.
This is all a preparation step for issue  #5074, "pages from other data sources".

But this also fixes a set of annoying limitations, especially related to custom output formats, and shortcodes.

Most notable changes:

* The inner content of shortcodes using the `{{%` as the outer-most delimiter will now be sent to the content renderer, e.g. Blackfriday.
  This means that any markdown will partake in the global ToC and footnote context etc.
* The Custom Output formats are now "fully virtualized". This removes many of the current limitations.
* The taxonomy list type now has a reference to the `Page` object.
  This improves the taxonomy template `.Title` situation and make common template constructs much simpler.

See #5074
Fixes #5763
Fixes #5758
Fixes #5090
Fixes #5204
Fixes #4695
Fixes #5607
Fixes #5707
Fixes #5719
Fixes #3113
Fixes #5706
Fixes #5767
Fixes #5723
Fixes #5769
Fixes #5770
Fixes #5771
Fixes #5759
Fixes #5776
Fixes #5777
Fixes #5778
2019-03-23 18:51:22 +01:00

148 lines
3.3 KiB
Go

// 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 tplimpl
import (
"strings"
"github.com/gohugoio/hugo/tpl"
)
// Currently lang, outFormat, suffix
const numTemplateVariants = 3
type shortcodeVariant struct {
// The possible variants: lang, outFormat, suffix
// gtag
// gtag.html
// gtag.no.html
// gtag.no.amp.html
// A slice of length numTemplateVariants.
variants []string
info tpl.Info
templ tpl.Template
}
type shortcodeTemplates struct {
variants []shortcodeVariant
}
func (s *shortcodeTemplates) indexOf(variants []string) int {
L:
for i, v1 := range s.variants {
for i, v2 := range v1.variants {
if v2 != variants[i] {
continue L
}
}
return i
}
return -1
}
func (s *shortcodeTemplates) fromVariants(variants tpl.TemplateVariants) (shortcodeVariant, bool) {
return s.fromVariantsSlice([]string{
variants.Language,
strings.ToLower(variants.OutputFormat.Name),
variants.OutputFormat.MediaType.Suffix(),
})
}
// Get the most specific template given a full name, e.g gtag.no.amp.html.
func (s *shortcodeTemplates) fromName(name string) (shortcodeVariant, bool) {
return s.fromVariantsSlice(templateVariants(name))
}
func (s *shortcodeTemplates) fromVariantsSlice(variants []string) (shortcodeVariant, bool) {
var (
bestMatch shortcodeVariant
bestMatchWeight int
)
for _, variant := range s.variants {
w := s.compareVariants(variants, variant.variants)
if bestMatchWeight == 0 || w > bestMatchWeight {
bestMatch = variant
bestMatchWeight = w
}
}
return bestMatch, true
}
// calculate a weight for two string slices of same lenght.
// higher value means "better match".
func (s *shortcodeTemplates) compareVariants(a, b []string) int {
weight := 0
for i, av := range a {
bv := b[i]
if av == bv {
weight++
} else {
weight--
}
}
return weight
}
func templateVariants(name string) []string {
_, variants := templateNameAndVariants(name)
return variants
}
func templateNameAndVariants(name string) (string, []string) {
variants := make([]string, numTemplateVariants)
parts := strings.Split(name, ".")
if len(parts) <= 1 {
// No variants.
return name, variants
}
name = parts[0]
parts = parts[1:]
lp := len(parts)
start := len(variants) - lp
for i, j := start, 0; i < len(variants); i, j = i+1, j+1 {
variants[i] = parts[j]
}
if lp > 1 && lp < len(variants) {
for i := lp - 1; i > 0; i-- {
variants[i-1] = variants[i]
}
}
if lp == 1 {
// Suffix only. Duplicate it into the output format field to
// make HTML win over AMP.
variants[len(variants)-2] = variants[len(variants)-1]
}
return name, variants
}
func isShortcode(name string) bool {
return strings.Contains(name, "shortcodes/")
}
func isInternal(name string) bool {
return strings.HasPrefix(name, "_internal/")
}