diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2022-04-10 20:30:52 +0200 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2022-04-12 13:24:16 +0200 |
commit | 627eed1d620910f494056330733db6c6187b8fe9 (patch) | |
tree | a136a398e8f85b10d3de7b1781825453886435ed /resources | |
parent | 82ba634ed90186c756189a79b637559b28dd363e (diff) |
Make string sorting (e.g. ByTitle, ByLinkTitle and ByParam) language aware
Fixes #2180
Diffstat (limited to 'resources')
-rw-r--r-- | resources/page/integration_test.go | 66 | ||||
-rw-r--r-- | resources/page/pagegroup.go | 19 | ||||
-rw-r--r-- | resources/page/pages_sort.go | 61 |
3 files changed, 133 insertions, 13 deletions
diff --git a/resources/page/integration_test.go b/resources/page/integration_test.go index 285b14342..9dc322b4a 100644 --- a/resources/page/integration_test.go +++ b/resources/page/integration_test.go @@ -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| +`) +} diff --git a/resources/page/pagegroup.go b/resources/page/pagegroup.go index 601af606a..1d9827907 100644 --- a/resources/page/pagegroup.go +++ b/resources/page/pagegroup.go @@ -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 } +type mapKeyByStr struct { + less func(a, b string) bool + mapKeyValues +} func (s mapKeyByStr) Less(i, j int) bool { - return compare.LessStrings(s.mapKeyValues[i].String(), s.mapKeyValues[j].String()) + return s.less(s.mapKeyValues[i].String(), s.mapKeyValues[j].String()) } -func sortKeys(v []reflect.Value, order string) []reflect.Value { +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)}) } diff --git a/resources/page/pages_sort.go b/resources/page/pages_sort.go index 02096dadd..08cb34a32 100644 --- a/resources/page/pages_sort.go +++ b/resources/page/pages_sort.go @@ -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) |