shithub: hugo

Download patch

ref: acfa153863d6ff2acf17ffb4395e05d102229905
parent: f033d9f01d13d8cd08205ccfaa09919ed15dca77
author: Cameron Moore <moorereason@gmail.com>
date: Thu Oct 15 09:54:47 EDT 2020

output: Improve layout path construction


--- a/output/layout.go
+++ b/output/layout.go
@@ -14,7 +14,6 @@
 package output
 
 import (
-	"fmt"
 	"strings"
 	"sync"
 
@@ -65,7 +64,6 @@
 // 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 @@
 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 @@
 	}
 
 	return layouts
-
 }
 
 func (l *layoutBuilder) resolveVariations() []string {
-
 	var layouts []string
 
 	var variations []string
@@ -220,7 +215,7 @@
 
 	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 @@
 				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 {
+// 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 ""
+	}
 
-	var l string
+	// Commence valid path construction...
 
-	if typeVar != "" {
-		l = "TYPE/"
+	var (
+		p       strings.Builder
+		needDot bool
+	)
+
+	if typ != "" {
+		p.WriteString(typ)
+		p.WriteString("/")
 	}
 
-	if layoutVar != "" {
-		l += "LAYOUT.VARIATIONS.EXTENSION"
-	} else {
-		l += "VARIATIONS.EXTENSION"
+	if layout != "" {
+		p.WriteString(layout)
+		needDot = true
 	}
 
-	return l
-}
+	if variations != "" {
+		if needDot {
+			p.WriteString(".")
+		}
+		p.WriteString(variations)
+		needDot = true
+	}
 
-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)
+	if extension != "" {
+		if needDot {
+			p.WriteString(".")
 		}
+		p.WriteString(extension)
 	}
 
-	return filteredLayouts
-}
-
-func replaceKeyValues(s string, oldNew ...string) string {
-	replacer := strings.NewReplacer(oldNew...)
-	return replacer.Replace(s)
+	return p.String()
 }
--- a/output/layout_test.go
+++ b/output/layout_test.go
@@ -663,13 +663,25 @@
 }
 
 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)
+		}
 	}
 }