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
|
package output
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
@ -65,7 +64,6 @@ func NewLayoutHandler() *LayoutHandler {
|
||||||
// For returns a layout for the given LayoutDescriptor and options.
|
// For returns a layout for the given LayoutDescriptor and options.
|
||||||
// Layouts are rendered and cached internally.
|
// Layouts are rendered and cached internally.
|
||||||
func (l *LayoutHandler) For(d LayoutDescriptor, f Format) ([]string, error) {
|
func (l *LayoutHandler) For(d LayoutDescriptor, f Format) ([]string, error) {
|
||||||
|
|
||||||
// We will get lots of requests for the same layouts, so avoid recalculations.
|
// We will get lots of requests for the same layouts, so avoid recalculations.
|
||||||
key := layoutCacheKey{d, f.Name}
|
key := layoutCacheKey{d, f.Name}
|
||||||
l.mu.RLock()
|
l.mu.RLock()
|
||||||
|
@ -131,7 +129,6 @@ func (l *layoutBuilder) addKind() {
|
||||||
const renderingHookRoot = "/_markup"
|
const renderingHookRoot = "/_markup"
|
||||||
|
|
||||||
func resolvePageTemplate(d LayoutDescriptor, f Format) []string {
|
func resolvePageTemplate(d LayoutDescriptor, f Format) []string {
|
||||||
|
|
||||||
b := &layoutBuilder{d: d, f: f}
|
b := &layoutBuilder{d: d, f: f}
|
||||||
|
|
||||||
if !d.RenderingHook && d.Layout != "" {
|
if !d.RenderingHook && d.Layout != "" {
|
||||||
|
@ -208,11 +205,9 @@ func resolvePageTemplate(d LayoutDescriptor, f Format) []string {
|
||||||
}
|
}
|
||||||
|
|
||||||
return layouts
|
return layouts
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *layoutBuilder) resolveVariations() []string {
|
func (l *layoutBuilder) resolveVariations() []string {
|
||||||
|
|
||||||
var layouts []string
|
var layouts []string
|
||||||
|
|
||||||
var variations []string
|
var variations []string
|
||||||
|
@ -220,7 +215,7 @@ func (l *layoutBuilder) resolveVariations() []string {
|
||||||
|
|
||||||
if l.d.Lang != "" {
|
if l.d.Lang != "" {
|
||||||
// We prefer the most specific type before language.
|
// 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 {
|
} else {
|
||||||
variations = append(variations, name)
|
variations = append(variations, name)
|
||||||
}
|
}
|
||||||
|
@ -233,55 +228,63 @@ func (l *layoutBuilder) resolveVariations() []string {
|
||||||
if variation == "" && layoutVar == "" {
|
if variation == "" && layoutVar == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
template := layoutTemplate(typeVar, layoutVar)
|
|
||||||
layouts = append(layouts, replaceKeyValues(template,
|
s := constructLayoutPath(typeVar, layoutVar, variation, l.f.MediaType.Suffix())
|
||||||
"TYPE", typeVar,
|
if s != "" {
|
||||||
"LAYOUT", layoutVar,
|
layouts = append(layouts, s)
|
||||||
"VARIATIONS", variation,
|
}
|
||||||
"EXTENSION", l.f.MediaType.Suffix(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return filterDotLess(layouts)
|
return layouts
|
||||||
}
|
}
|
||||||
|
|
||||||
func layoutTemplate(typeVar, layoutVar string) string {
|
// constructLayoutPath constructs a layout path given a type, layout,
|
||||||
|
// variations, and extension. The path constructed follows the pattern of
|
||||||
var l string
|
// type/layout.variations.extension. If any value is empty, it will be left out
|
||||||
|
// of the path construction.
|
||||||
if typeVar != "" {
|
//
|
||||||
l = "TYPE/"
|
// 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 != "" {
|
// Commence valid path construction...
|
||||||
l += "LAYOUT.VARIATIONS.EXTENSION"
|
|
||||||
} else {
|
var (
|
||||||
l += "VARIATIONS.EXTENSION"
|
p strings.Builder
|
||||||
|
needDot bool
|
||||||
|
)
|
||||||
|
|
||||||
|
if typ != "" {
|
||||||
|
p.WriteString(typ)
|
||||||
|
p.WriteString("/")
|
||||||
}
|
}
|
||||||
|
|
||||||
return l
|
if layout != "" {
|
||||||
}
|
p.WriteString(layout)
|
||||||
|
needDot = true
|
||||||
|
}
|
||||||
|
|
||||||
func filterDotLess(layouts []string) []string {
|
if variations != "" {
|
||||||
var filteredLayouts []string
|
if needDot {
|
||||||
|
p.WriteString(".")
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
p.WriteString(variations)
|
||||||
|
needDot = true
|
||||||
}
|
}
|
||||||
|
|
||||||
return filteredLayouts
|
if extension != "" {
|
||||||
}
|
if needDot {
|
||||||
|
p.WriteString(".")
|
||||||
|
}
|
||||||
|
p.WriteString(extension)
|
||||||
|
}
|
||||||
|
|
||||||
func replaceKeyValues(s string, oldNew ...string) string {
|
return p.String()
|
||||||
replacer := strings.NewReplacer(oldNew...)
|
|
||||||
return replacer.Replace(s)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -663,13 +663,25 @@ func TestLayout(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkLayout(b *testing.B) {
|
func BenchmarkLayout(b *testing.B) {
|
||||||
c := qt.New(b)
|
|
||||||
descriptor := LayoutDescriptor{Kind: "taxonomy", Section: "categories"}
|
descriptor := LayoutDescriptor{Kind: "taxonomy", Section: "categories"}
|
||||||
l := NewLayoutHandler()
|
l := NewLayoutHandler()
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
layouts, err := l.For(descriptor, HTMLFormat)
|
_, err := l.For(descriptor, HTMLFormat)
|
||||||
c.Assert(err, qt.IsNil)
|
if err != nil {
|
||||||
c.Assert(layouts, qt.Not(qt.HasLen), 0)
|
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