output: Improve layout path construction - hugo - [fork] hugo port for 9front
 (HTM) git clone git@git.drkhsh.at/hugo.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Submodules
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit acfa153863d6ff2acf17ffb4395e05d102229905
 (DIR) parent f033d9f01d13d8cd08205ccfaa09919ed15dca77
 (HTM) Author: Cameron Moore <moorereason@gmail.com>
       Date:   Thu, 15 Oct 2020 13:54:47 -0500
       
       output: Improve layout path construction
       
       
       Diffstat:
         M output/layout.go                    |      85 ++++++++++++++++---------------
         M output/layout_test.go               |      20 ++++++++++++++++----
       
       2 files changed, 60 insertions(+), 45 deletions(-)
       ---
 (DIR) diff --git a/output/layout.go b/output/layout.go
       @@ -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 {
       +// 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 ""
       +        }
       +
       +        // Commence valid path construction...
        
       -        var l string
       +        var (
       +                p       strings.Builder
       +                needDot bool
       +        )
        
       -        if typeVar != "" {
       -                l = "TYPE/"
       +        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
       -}
       -
       -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 variations != "" {
       +                if needDot {
       +                        p.WriteString(".")
                        }
       +                p.WriteString(variations)
       +                needDot = true
                }
        
       -        return filteredLayouts
       -}
       +        if extension != "" {
       +                if needDot {
       +                        p.WriteString(".")
       +                }
       +                p.WriteString(extension)
       +        }
        
       -func replaceKeyValues(s string, oldNew ...string) string {
       -        replacer := strings.NewReplacer(oldNew...)
       -        return replacer.Replace(s)
       +        return p.String()
        }
 (DIR) diff --git a/output/layout_test.go b/output/layout_test.go
       @@ -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)
       +                }
                }
        }