diff --git a/helpers/path.go b/helpers/path.go index a40e0f8ff..b302b1569 100644 --- a/helpers/path.go +++ b/helpers/path.go @@ -87,7 +87,8 @@ func ishex(c rune) bool { // a predefined set of special Unicode characters. // If RemovePathAccents configuration flag is enabled, Unicode accents // are also removed. -// Spaces will be replaced with a single hyphen, and sequential hyphens will be reduced to one. +// Hyphens in the original input are maintained. +// Spaces will be replaced with a single hyphen, and sequential replacement hyphens will be reduced to one. func (p *PathSpec) UnicodeSanitize(s string) string { if p.RemovePathAccents { s = text.RemoveAccentsString(s) @@ -95,20 +96,30 @@ func (p *PathSpec) UnicodeSanitize(s string) string { source := []rune(s) target := make([]rune, 0, len(source)) - var prependHyphen bool + var ( + prependHyphen bool + wasHyphen bool + ) for i, r := range source { - isAllowed := r == '.' || r == '/' || r == '\\' || r == '_' || r == '#' || r == '+' || r == '~' + isAllowed := r == '.' || r == '/' || r == '\\' || r == '_' || r == '#' || r == '+' || r == '~' || r == '-' isAllowed = isAllowed || unicode.IsLetter(r) || unicode.IsDigit(r) || unicode.IsMark(r) isAllowed = isAllowed || (r == '%' && i+2 < len(source) && ishex(source[i+1]) && ishex(source[i+2])) if isAllowed { + // track explicit hyphen in input; no need to add a new hyphen if + // we just saw one. + wasHyphen = r == '-' + if prependHyphen { - target = append(target, '-') + // if currently have a hyphen, don't prepend an extra one + if !wasHyphen { + target = append(target, '-') + } prependHyphen = false } target = append(target, r) - } else if len(target) > 0 && (r == '-' || unicode.IsSpace(r)) { + } else if len(target) > 0 && !wasHyphen && unicode.IsSpace(r) { prependHyphen = true } } diff --git a/helpers/path_test.go b/helpers/path_test.go index 1d2dc1184..6a119a741 100644 --- a/helpers/path_test.go +++ b/helpers/path_test.go @@ -40,6 +40,10 @@ func TestMakePath(t *testing.T) { expected string removeAccents bool }{ + {"dot.slash/backslash\\underscore_pound#plus+hyphen-", "dot.slash/backslash\\underscore_pound#plus+hyphen-", true}, + {"abcXYZ0123456789", "abcXYZ0123456789", true}, + {"%20 %2", "%20-2", true}, + {"foo- bar", "foo-bar", true}, {" Foo bar ", "Foo-bar", true}, {"Foo.Bar/foo_Bar-Foo", "Foo.Bar/foo_Bar-Foo", true}, {"fOO,bar:foobAR", "fOObarfoobAR", true}, @@ -52,7 +56,7 @@ func TestMakePath(t *testing.T) { {"a%C3%B1ame", "a%C3%B1ame", false}, // Issue #1292 {"this+is+a+test", "this+is+a+test", false}, // Issue #1290 {"~foo", "~foo", false}, // Issue #2177 - + {"foo--bar", "foo--bar", true}, // Issue #7288 } for _, test := range tests {