diff --git a/output/layout.go b/output/layout.go index 4dd37b7b1..55e7fa305 100644 --- 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 { - - 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 -} + if layout != "" { + p.WriteString(layout) + 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 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() } diff --git a/output/layout_test.go b/output/layout_test.go index 5651a09b9..38b9d5faf 100644 --- 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) + } } }