mirror of
https://github.com/gohugoio/hugo.git
synced 2025-08-28 22:19:59 +02:00
Make string sorting (e.g. ByTitle, ByLinkTitle and ByParam) language aware
Fixes #2180
This commit is contained in:
@@ -70,3 +70,69 @@ date: "2020-02-01"
|
||||
b.AssertFileContent("public/en/index.html", "0|February, 2020|Pages(1)1|January, 2020|Pages(1)")
|
||||
b.AssertFileContent("public/fr/index.html", "0|février, 2020|Pages(1)1|janvier, 2020|Pages(1)")
|
||||
}
|
||||
|
||||
func TestPagesSortCollation(t *testing.T) {
|
||||
|
||||
files := `
|
||||
-- config.toml --
|
||||
defaultContentLanguage = 'en'
|
||||
defaultContentLanguageInSubdir = true
|
||||
[languages]
|
||||
[languages.en]
|
||||
title = 'My blog'
|
||||
weight = 1
|
||||
[languages.fr]
|
||||
title = 'Mon blogue'
|
||||
weight = 2
|
||||
[languages.nn]
|
||||
title = 'Bloggen min'
|
||||
weight = 3
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: "zulu"
|
||||
date: "2020-01-01"
|
||||
param1: "xylophone"
|
||||
tags: ["xylophone", "éclair", "zulu", "emma"]
|
||||
---
|
||||
-- content/p2.md --
|
||||
---
|
||||
title: "émotion"
|
||||
date: "2020-01-01"
|
||||
param1: "violin"
|
||||
---
|
||||
-- content/p3.md --
|
||||
---
|
||||
title: "alpha"
|
||||
date: "2020-01-01"
|
||||
param1: "éclair"
|
||||
---
|
||||
-- layouts/index.html --
|
||||
ByTitle: {{ range site.RegularPages.ByTitle }}{{ .Title }}|{{ end }}
|
||||
ByLinkTitle: {{ range site.RegularPages.ByLinkTitle }}{{ .Title }}|{{ end }}
|
||||
ByParam: {{ range site.RegularPages.ByParam "param1" }}{{ .Params.param1 }}|{{ end }}
|
||||
Tags Alphabetical: {{ range site.Taxonomies.tags.Alphabetical }}{{ .Term }}|{{ end }}
|
||||
GroupBy: {{ range site.RegularPages.GroupBy "Title" }}{{ .Key }}|{{ end }}
|
||||
{{ with (site.GetPage "p1").Params.tags }}
|
||||
Sort: {{ sort . }}
|
||||
ByWeight: {{ range site.RegularPages.ByWeight }}{{ .Title }}|{{ end }}
|
||||
{{ end }}
|
||||
|
||||
`
|
||||
|
||||
b := hugolib.NewIntegrationTestBuilder(
|
||||
hugolib.IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
NeedsOsFS: true,
|
||||
}).Build()
|
||||
|
||||
b.AssertFileContent("public/en/index.html", `
|
||||
ByTitle: alpha|émotion|zulu|
|
||||
ByLinkTitle: alpha|émotion|zulu|
|
||||
ByParam: éclair|violin|xylophone
|
||||
Tags Alphabetical: éclair|emma|xylophone|zulu|
|
||||
GroupBy: alpha|émotion|zulu|
|
||||
Sort: [éclair emma xylophone zulu]
|
||||
ByWeight: alpha|émotion|zulu|
|
||||
`)
|
||||
}
|
||||
|
@@ -53,13 +53,16 @@ type mapKeyByInt struct{ mapKeyValues }
|
||||
|
||||
func (s mapKeyByInt) Less(i, j int) bool { return s.mapKeyValues[i].Int() < s.mapKeyValues[j].Int() }
|
||||
|
||||
type mapKeyByStr struct{ mapKeyValues }
|
||||
|
||||
func (s mapKeyByStr) Less(i, j int) bool {
|
||||
return compare.LessStrings(s.mapKeyValues[i].String(), s.mapKeyValues[j].String())
|
||||
type mapKeyByStr struct {
|
||||
less func(a, b string) bool
|
||||
mapKeyValues
|
||||
}
|
||||
|
||||
func sortKeys(v []reflect.Value, order string) []reflect.Value {
|
||||
func (s mapKeyByStr) Less(i, j int) bool {
|
||||
return s.less(s.mapKeyValues[i].String(), s.mapKeyValues[j].String())
|
||||
}
|
||||
|
||||
func sortKeys(examplePage Page, v []reflect.Value, order string) []reflect.Value {
|
||||
if len(v) <= 1 {
|
||||
return v
|
||||
}
|
||||
@@ -72,10 +75,12 @@ func sortKeys(v []reflect.Value, order string) []reflect.Value {
|
||||
sort.Sort(mapKeyByInt{v})
|
||||
}
|
||||
case reflect.String:
|
||||
stringLess, close := collatorStringLess(examplePage)
|
||||
defer close()
|
||||
if order == "desc" {
|
||||
sort.Sort(sort.Reverse(mapKeyByStr{v}))
|
||||
sort.Sort(sort.Reverse(mapKeyByStr{stringLess, v}))
|
||||
} else {
|
||||
sort.Sort(mapKeyByStr{v})
|
||||
sort.Sort(mapKeyByStr{stringLess, v})
|
||||
}
|
||||
}
|
||||
return v
|
||||
@@ -161,7 +166,7 @@ func (p Pages) GroupBy(key string, order ...string) (PagesGroup, error) {
|
||||
tmp.SetMapIndex(fv, reflect.Append(tmp.MapIndex(fv), ppv))
|
||||
}
|
||||
|
||||
sortedKeys := sortKeys(tmp.MapKeys(), direction)
|
||||
sortedKeys := sortKeys(p[0], tmp.MapKeys(), direction)
|
||||
r := make([]PageGroup, len(sortedKeys))
|
||||
for i, k := range sortedKeys {
|
||||
r[i] = PageGroup{Key: k.Interface(), Pages: tmp.MapIndex(k).Interface().(Pages)}
|
||||
@@ -213,7 +218,7 @@ func (p Pages) GroupByParam(key string, order ...string) (PagesGroup, error) {
|
||||
}
|
||||
|
||||
var r []PageGroup
|
||||
for _, k := range sortKeys(tmp.MapKeys(), direction) {
|
||||
for _, k := range sortKeys(p[0], tmp.MapKeys(), direction) {
|
||||
r = append(r, PageGroup{Key: k.Interface(), Pages: tmp.MapIndex(k).Interface().(Pages)})
|
||||
}
|
||||
|
||||
|
@@ -17,6 +17,7 @@ import (
|
||||
"sort"
|
||||
|
||||
"github.com/gohugoio/hugo/common/collections"
|
||||
"github.com/gohugoio/hugo/langs"
|
||||
|
||||
"github.com/gohugoio/hugo/resources/resource"
|
||||
|
||||
@@ -72,7 +73,7 @@ var (
|
||||
}
|
||||
if p1.Weight() == p2.Weight() {
|
||||
if p1.Date().Unix() == p2.Date().Unix() {
|
||||
c := compare.Strings(p1.LinkTitle(), p2.LinkTitle())
|
||||
c := collatorStringCompare(func(p Page) string { return p.LinkTitle() }, p1, p2)
|
||||
if c == 0 {
|
||||
if p1.File().IsZero() || p2.File().IsZero() {
|
||||
return p1.File().IsZero()
|
||||
@@ -121,11 +122,11 @@ var (
|
||||
}
|
||||
|
||||
lessPageTitle = func(p1, p2 Page) bool {
|
||||
return compare.LessStrings(p1.Title(), p2.Title())
|
||||
return collatorStringCompare(func(p Page) string { return p.Title() }, p1, p2) < 0
|
||||
}
|
||||
|
||||
lessPageLinkTitle = func(p1, p2 Page) bool {
|
||||
return compare.LessStrings(p1.LinkTitle(), p2.LinkTitle())
|
||||
return collatorStringCompare(func(p Page) string { return p.LinkTitle() }, p1, p2) < 0
|
||||
}
|
||||
|
||||
lessPageDate = func(p1, p2 Page) bool {
|
||||
@@ -151,6 +152,46 @@ func (p Pages) Limit(n int) Pages {
|
||||
return p
|
||||
}
|
||||
|
||||
var collatorStringSort = func(getString func(Page) string) func(p Pages) {
|
||||
return func(p Pages) {
|
||||
if len(p) == 0 {
|
||||
return
|
||||
}
|
||||
// Pages may be a mix of multiple languages, so we need to use the language
|
||||
// for the currently rendered Site.
|
||||
currentSite := p[0].Site().Current()
|
||||
coll := langs.GetCollator(currentSite.Language())
|
||||
coll.Lock()
|
||||
defer coll.Unlock()
|
||||
|
||||
sort.SliceStable(p, func(i, j int) bool {
|
||||
return coll.CompareStrings(getString(p[i]), getString(p[j])) < 0
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var collatorStringCompare = func(getString func(Page) string, p1, p2 Page) int {
|
||||
currentSite := p1.Site().Current()
|
||||
coll := langs.GetCollator(currentSite.Language())
|
||||
coll.Lock()
|
||||
c := coll.CompareStrings(getString(p1), getString(p2))
|
||||
coll.Unlock()
|
||||
return c
|
||||
}
|
||||
|
||||
var collatorStringLess = func(p Page) (less func(s1, s2 string) bool, close func()) {
|
||||
currentSite := p.Site().Current()
|
||||
coll := langs.GetCollator(currentSite.Language())
|
||||
coll.Lock()
|
||||
return func(s1, s2 string) bool {
|
||||
return coll.CompareStrings(s1, s2) < 1
|
||||
},
|
||||
func() {
|
||||
coll.Unlock()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ByWeight sorts the Pages by weight and returns a copy.
|
||||
//
|
||||
// Adjacent invocations on the same receiver will return a cached result.
|
||||
@@ -175,7 +216,8 @@ func SortByDefault(pages Pages) {
|
||||
func (p Pages) ByTitle() Pages {
|
||||
const key = "pageSort.ByTitle"
|
||||
|
||||
pages, _ := spc.get(key, pageBy(lessPageTitle).Sort, p)
|
||||
pages, _ := spc.get(key, collatorStringSort(func(p Page) string { return p.Title() }), p)
|
||||
|
||||
return pages
|
||||
}
|
||||
|
||||
@@ -187,7 +229,7 @@ func (p Pages) ByTitle() Pages {
|
||||
func (p Pages) ByLinkTitle() Pages {
|
||||
const key = "pageSort.ByLinkTitle"
|
||||
|
||||
pages, _ := spc.get(key, pageBy(lessPageLinkTitle).Sort, p)
|
||||
pages, _ := spc.get(key, collatorStringSort(func(p Page) string { return p.LinkTitle() }), p)
|
||||
|
||||
return pages
|
||||
}
|
||||
@@ -323,9 +365,15 @@ func (p Pages) Reverse() Pages {
|
||||
//
|
||||
// This may safely be executed in parallel.
|
||||
func (p Pages) ByParam(paramsKey any) Pages {
|
||||
if len(p) < 2 {
|
||||
return p
|
||||
}
|
||||
paramsKeyStr := cast.ToString(paramsKey)
|
||||
key := "pageSort.ByParam." + paramsKeyStr
|
||||
|
||||
stringLess, close := collatorStringLess(p[0])
|
||||
defer close()
|
||||
|
||||
paramsKeyComparator := func(p1, p2 Page) bool {
|
||||
v1, _ := p1.Param(paramsKeyStr)
|
||||
v2, _ := p2.Param(paramsKeyStr)
|
||||
@@ -354,7 +402,8 @@ func (p Pages) ByParam(paramsKey any) Pages {
|
||||
s1 := cast.ToString(v1)
|
||||
s2 := cast.ToString(v2)
|
||||
|
||||
return compare.LessStrings(s1, s2)
|
||||
return stringLess(s1, s2)
|
||||
|
||||
}
|
||||
|
||||
pages, _ := spc.get(key, pageBy(paramsKeyComparator).Sort, p)
|
||||
|
Reference in New Issue
Block a user