Slug joiner normalization
This commit is contained in:
@@ -715,20 +715,7 @@ func normalizeWikiTitle(value string) string {
|
||||
}
|
||||
|
||||
func slugifyWikiTitle(value string) string {
|
||||
var b strings.Builder
|
||||
lastDash := false
|
||||
for _, r := range strings.ToLower(strings.TrimSpace(value)) {
|
||||
if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') {
|
||||
b.WriteRune(r)
|
||||
lastDash = false
|
||||
continue
|
||||
}
|
||||
if !lastDash && b.Len() > 0 {
|
||||
b.WriteByte('-')
|
||||
lastDash = true
|
||||
}
|
||||
}
|
||||
return strings.Trim(b.String(), "-")
|
||||
return slugifyWikiText(value, "")
|
||||
}
|
||||
|
||||
func loadDeployManifest(path string) wikiDeployManifest {
|
||||
|
||||
@@ -168,6 +168,21 @@ func TestMatchExistingNodeBBPagePrefersGeneratedPublicSlug(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeploySlugifyWikiTitleMatchesGeneratedSlugPolicy(t *testing.T) {
|
||||
tests := map[string]string{
|
||||
`Grandmaster's Battle Momentum`: "grandmasters-battle-momentum",
|
||||
`Bigby’s Clenched Fist`: "bigbys-clenched-fist",
|
||||
`Hardiness vs. Enchantments`: "hardiness-vs-enchantments",
|
||||
`Clairaudience/Clairvoyance`: "clairaudience-clairvoyance",
|
||||
`Élite & Noble Houses`: "elite-noble-houses",
|
||||
}
|
||||
for input, want := range tests {
|
||||
if got := slugifyWikiTitle(input); got != want {
|
||||
t.Fatalf("slugifyWikiTitle(%q) = %q, want %q", input, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeployWikiCreatesNodeBBTopicAndWritesManifest(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
sourceDir := filepath.Join(root, "pages")
|
||||
|
||||
@@ -1682,25 +1682,7 @@ func wikiPublicTarget(namespace, slug string) string {
|
||||
}
|
||||
|
||||
func wikiSlugifyTitle(value string) string {
|
||||
value = html.UnescapeString(strings.TrimSpace(value))
|
||||
var b strings.Builder
|
||||
lastDash := false
|
||||
for _, r := range strings.ToLower(value) {
|
||||
if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') {
|
||||
b.WriteRune(r)
|
||||
lastDash = false
|
||||
continue
|
||||
}
|
||||
if b.Len() > 0 && !lastDash {
|
||||
b.WriteByte('-')
|
||||
lastDash = true
|
||||
}
|
||||
}
|
||||
slug := strings.Trim(b.String(), "-")
|
||||
if slug == "" {
|
||||
return "topic"
|
||||
}
|
||||
return slug
|
||||
return slugifyWikiText(value, "topic")
|
||||
}
|
||||
|
||||
func wikiPageIDToRelPath(pageID string) string {
|
||||
|
||||
@@ -1239,6 +1239,25 @@ func TestWikiPageSlugOverridesChangeTargets(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWikiSlugifyTitleNormalizesPunctuationAndSeparators(t *testing.T) {
|
||||
tests := map[string]string{
|
||||
`Grandmaster's Battle Momentum`: "grandmasters-battle-momentum",
|
||||
`Bigby’s Clenched Fist`: "bigbys-clenched-fist",
|
||||
`"Quoted" ‘Curly’ “Title”`: "quoted-curly-title",
|
||||
`Hardiness vs. Enchantments`: "hardiness-vs-enchantments",
|
||||
`Clairaudience/Clairvoyance`: "clairaudience-clairvoyance",
|
||||
`Moon-on-a-Stick`: "moon-on-a-stick",
|
||||
`Élite & Noble Houses`: "elite-noble-houses",
|
||||
`Rock 'n' Roll`: "rock-n-roll",
|
||||
`Melf's Acid Arrow`: "melfs-acid-arrow",
|
||||
}
|
||||
for input, want := range tests {
|
||||
if got := wikiSlugifyTitle(input); got != want {
|
||||
t.Fatalf("wikiSlugifyTitle(%q) = %q, want %q", input, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPublicWikiTargetUsesTargetTitleBeforeDisplayLabel(t *testing.T) {
|
||||
ctx := &wikiContext{
|
||||
rowsByKey: map[string]map[string]any{
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package topdata
|
||||
|
||||
import (
|
||||
"html"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"golang.org/x/text/unicode/norm"
|
||||
)
|
||||
|
||||
func slugifyWikiText(value string, fallback string) string {
|
||||
value = html.UnescapeString(strings.TrimSpace(value))
|
||||
var b strings.Builder
|
||||
lastDash := false
|
||||
for _, r := range norm.NFKD.String(strings.ToLower(value)) {
|
||||
switch {
|
||||
case r >= 'a' && r <= 'z':
|
||||
b.WriteRune(r)
|
||||
lastDash = false
|
||||
case r >= '0' && r <= '9':
|
||||
b.WriteRune(r)
|
||||
lastDash = false
|
||||
case unicode.Is(unicode.Mn, r):
|
||||
continue
|
||||
case isWikiSlugJoinerPunctuation(r):
|
||||
continue
|
||||
default:
|
||||
if b.Len() > 0 && !lastDash {
|
||||
b.WriteByte('-')
|
||||
lastDash = true
|
||||
}
|
||||
}
|
||||
}
|
||||
slug := strings.Trim(b.String(), "-")
|
||||
if slug == "" {
|
||||
return fallback
|
||||
}
|
||||
return slug
|
||||
}
|
||||
|
||||
func isWikiSlugJoinerPunctuation(r rune) bool {
|
||||
switch r {
|
||||
case '\'', '"', '`', '´', '‘', '’', '‚', '‛', '“', '”', '„', '‟', '‹', '›', '«', '»', '′', '″', ''', '"':
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user