mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-21 20:46:30 -05:00
output: Improve layout path construction
This commit is contained in:
parent
f033d9f01d
commit
acfa153863
2 changed files with 61 additions and 46 deletions
|
@ -14,7 +14,6 @@
|
|||
package output
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
|
@ -65,7 +64,6 @@ func NewLayoutHandler() *LayoutHandler {
|
|||
// For returns a layout for the given LayoutDescriptor and options.
|
||||
// Layouts are rendered and cached internally.
|
||||
func (l *LayoutHandler) For(d LayoutDescriptor, f Format) ([]string, error) {
|
||||
|
||||
// We will get lots of requests for the same layouts, so avoid recalculations.
|
||||
key := layoutCacheKey{d, f.Name}
|
||||
l.mu.RLock()
|
||||
|
@ -131,7 +129,6 @@ func (l *layoutBuilder) addKind() {
|
|||
const renderingHookRoot = "/_markup"
|
||||
|
||||
func resolvePageTemplate(d LayoutDescriptor, f Format) []string {
|
||||
|
||||
b := &layoutBuilder{d: d, f: f}
|
||||
|
||||
if !d.RenderingHook && d.Layout != "" {
|
||||
|
@ -208,11 +205,9 @@ func resolvePageTemplate(d LayoutDescriptor, f Format) []string {
|
|||
}
|
||||
|
||||
return layouts
|
||||
|
||||
}
|
||||
|
||||
func (l *layoutBuilder) resolveVariations() []string {
|
||||
|
||||
var layouts []string
|
||||
|
||||
var variations []string
|
||||
|
@ -220,7 +215,7 @@ func (l *layoutBuilder) resolveVariations() []string {
|
|||
|
||||
if l.d.Lang != "" {
|
||||
// We prefer the most specific type before language.
|
||||
variations = append(variations, []string{fmt.Sprintf("%s.%s", l.d.Lang, name), name, l.d.Lang}...)
|
||||
variations = append(variations, []string{l.d.Lang + "." + name, name, l.d.Lang}...)
|
||||
} else {
|
||||
variations = append(variations, name)
|
||||
}
|
||||
|
@ -233,55 +228,63 @@ func (l *layoutBuilder) resolveVariations() []string {
|
|||
if variation == "" && layoutVar == "" {
|
||||
continue
|
||||
}
|
||||
template := layoutTemplate(typeVar, layoutVar)
|
||||
layouts = append(layouts, replaceKeyValues(template,
|
||||
"TYPE", typeVar,
|
||||
"LAYOUT", layoutVar,
|
||||
"VARIATIONS", variation,
|
||||
"EXTENSION", l.f.MediaType.Suffix(),
|
||||
))
|
||||
|
||||
s := constructLayoutPath(typeVar, layoutVar, variation, l.f.MediaType.Suffix())
|
||||
if s != "" {
|
||||
layouts = append(layouts, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return filterDotLess(layouts)
|
||||
return layouts
|
||||
}
|
||||
|
||||
func layoutTemplate(typeVar, layoutVar string) string {
|
||||
|
||||
var l string
|
||||
|
||||
if typeVar != "" {
|
||||
l = "TYPE/"
|
||||
// constructLayoutPath constructs a layout path given a type, layout,
|
||||
// variations, and extension. The path constructed follows the pattern of
|
||||
// type/layout.variations.extension. If any value is empty, it will be left out
|
||||
// of the path construction.
|
||||
//
|
||||
// Path construction requires at least 2 of 3 out of layout, variations, and extension.
|
||||
// If more than one of those is empty, an empty string is returned.
|
||||
func constructLayoutPath(typ, layout, variations, extension string) string {
|
||||
// we already know that layout and variations are not both empty because of
|
||||
// checks in resolveVariants().
|
||||
if extension == "" && (layout == "" || variations == "") {
|
||||
return ""
|
||||
}
|
||||
|
||||
if layoutVar != "" {
|
||||
l += "LAYOUT.VARIATIONS.EXTENSION"
|
||||
} else {
|
||||
l += "VARIATIONS.EXTENSION"
|
||||
// Commence valid path construction...
|
||||
|
||||
var (
|
||||
p strings.Builder
|
||||
needDot bool
|
||||
)
|
||||
|
||||
if typ != "" {
|
||||
p.WriteString(typ)
|
||||
p.WriteString("/")
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func filterDotLess(layouts []string) []string {
|
||||
var filteredLayouts []string
|
||||
|
||||
for _, l := range layouts {
|
||||
l = strings.Replace(l, "..", ".", -1)
|
||||
l = strings.Trim(l, ".")
|
||||
// If media type has no suffix, we have "index" type of layouts in this list, which
|
||||
// doesn't make much sense.
|
||||
if strings.Contains(l, ".") {
|
||||
filteredLayouts = append(filteredLayouts, l)
|
||||
}
|
||||
}
|
||||
|
||||
return filteredLayouts
|
||||
}
|
||||
|
||||
func replaceKeyValues(s string, oldNew ...string) string {
|
||||
replacer := strings.NewReplacer(oldNew...)
|
||||
return replacer.Replace(s)
|
||||
if layout != "" {
|
||||
p.WriteString(layout)
|
||||
needDot = true
|
||||
}
|
||||
|
||||
if variations != "" {
|
||||
if needDot {
|
||||
p.WriteString(".")
|
||||
}
|
||||
p.WriteString(variations)
|
||||
needDot = true
|
||||
}
|
||||
|
||||
if extension != "" {
|
||||
if needDot {
|
||||
p.WriteString(".")
|
||||
}
|
||||
p.WriteString(extension)
|
||||
}
|
||||
|
||||
return p.String()
|
||||
}
|
||||
|
|
|
@ -663,13 +663,25 @@ func TestLayout(t *testing.T) {
|
|||
}
|
||||
|
||||
func BenchmarkLayout(b *testing.B) {
|
||||
c := qt.New(b)
|
||||
descriptor := LayoutDescriptor{Kind: "taxonomy", Section: "categories"}
|
||||
l := NewLayoutHandler()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
layouts, err := l.For(descriptor, HTMLFormat)
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(layouts, qt.Not(qt.HasLen), 0)
|
||||
_, err := l.For(descriptor, HTMLFormat)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLayoutUncached(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
descriptor := LayoutDescriptor{Kind: "taxonomy", Section: "categories"}
|
||||
l := NewLayoutHandler()
|
||||
|
||||
_, err := l.For(descriptor, HTMLFormat)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue