diff --git a/slug.go b/slug.go index 62e3010..b4ed7cd 100644 --- a/slug.go +++ b/slug.go @@ -38,6 +38,14 @@ var ( // Default is true. Lowercase = true + // DisableMultipleDashTrim defines if multiple dashes should be preserved. + // Default is false (multiple dashes will be replaced with single dash). + DisableMultipleDashTrim = false + + // DisableEndsTrim defines if the slug should keep leading and trailing + // dashes and underscores. Default is false (trim enabled). + DisableEndsTrim = false + // Append timestamp to the end in order to make slug unique // Default is false AppendTimestamp = false @@ -126,8 +134,12 @@ func MakeLang(s string, lang string) (slug string) { // Process all remaining symbols slug = regexpNonAuthorizedChars.ReplaceAllString(slug, "-") - slug = regexpMultipleDashes.ReplaceAllString(slug, "-") - slug = strings.Trim(slug, "-_") + if !DisableMultipleDashTrim { + slug = regexpMultipleDashes.ReplaceAllString(slug, "-") + } + if !DisableEndsTrim { + slug = strings.Trim(slug, "-_") + } if MaxLength > 0 && EnableSmartTruncate { slug = smartTruncate(slug) @@ -145,7 +157,7 @@ func MakeLang(s string, lang string) (slug string) { // order. Many passes, on one substitution another one could apply. func Substitute(s string, sub map[string]string) (buf string) { buf = s - var keys = make([]string, 0, len(sub)) + keys := make([]string, 0, len(sub)) for k := range sub { keys = append(keys, k) } diff --git a/slug_test.go b/slug_test.go index 6e39901..9788a27 100644 --- a/slug_test.go +++ b/slug_test.go @@ -472,6 +472,52 @@ func TestIsSlug(t *testing.T) { }) } +func TestSlugMakeDisableTrimOptions(t *testing.T) { + testCases := []struct { + name string + in string + want string + disableMultipleDash bool + disableEndsTrim bool + }{ + // Test multiple dash trim + {"multiple dashes preserved", "test--slug", "test--slug", true, false}, + {"multiple dashes and spaces", "spaces converted to--dashes", "spaces--converted--to--dashes", true, false}, + {"symbols to multiple dashes", "symbols!!!replaced", "symbols---replaced", true, false}, + {"only dashes multiple", "----", "", true, false}, + {"only underscores multiple", "____", "", true, false}, + + // Test end trim + {"leading/trailing dashes", "-test-slug-", "-test-slug-", false, true}, + {"leading/trailing underscores", "_test_slug_", "_test_slug_", false, true}, + {"mixed endings", "-_mixed_-", "-_mixed_-", false, true}, + {"only dashes end", "----", "-", false, true}, + {"only underscores end", "____", "____", false, true}, + + // Test both options together + {"both options enabled", "--test---slug--", "--test---slug--", true, true}, + {"multiple types of edges", "__test---slug--", "__test---slug--", true, true}, + {"only dashes both", "----", "----", true, true}, + {"only underscores both", "____", "____", true, true}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + DisableMultipleDashTrim = tc.disableMultipleDash + DisableEndsTrim = tc.disableEndsTrim + defer func() { + DisableMultipleDashTrim = false + DisableEndsTrim = false + }() + + got := Make(tc.in) + if got != tc.want { + t.Errorf("Make(%#v) = %#v; want %#v", tc.in, got, tc.want) + } + }) + } +} + func BenchmarkMakeShortAscii(b *testing.B) { b.ReportAllocs() for n := 0; n < b.N; n++ {