Make string sorting (e.g. ByTitle, ByLinkTitle and ByParam) language aware

Fixes #2180
This commit is contained in:
Bjørn Erik Pedersen
2022-04-10 20:30:52 +02:00
parent 82ba634ed9
commit 627eed1d62
11 changed files with 297 additions and 31 deletions

View File

@@ -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)