ref: cdfd1c99baa22d69e865294dfcd783811f96c880
parent: 047af7cfe5e9aa740b85e0f9974a2d31a0ef4c08
author: Cameron Moore <moorereason@gmail.com>
date: Fri Aug 28 05:29:26 EDT 2020
tpl: Add limit support to replaceRE Go stdlib doesn't contain a limited replace in the regexp package, but we can accomplish the same thing with ReplaceAllStringFunc. Fixes #7586
--- a/tpl/strings/init.go
+++ b/tpl/strings/init.go
@@ -99,7 +99,16 @@
ns.AddMethodMapping(ctx.ReplaceRE,
[]string{"replaceRE"},
- [][2]string{},
+ [][2]string{
+ {
+ `{{ replaceRE "a+b" "X" "aabbaabbab" }}`,
+ `XbXbX`,
+ },
+ {
+ `{{ replaceRE "a+b" "X" "aabbaabbab" 1 }}`,
+ `Xbaabbab`,
+ },
+ },
)
ns.AddMethodMapping(ctx.SliceString,
--- a/tpl/strings/regexp.go
+++ b/tpl/strings/regexp.go
@@ -46,8 +46,9 @@
}
// ReplaceRE returns a copy of s, replacing all matches of the regular
-// expression pattern with the replacement text repl.
-func (ns *Namespace) ReplaceRE(pattern, repl, s interface{}) (_ string, err error) {
+// expression pattern with the replacement text repl. The number of replacements
+// can be limited with an optional fourth parameter.
+func (ns *Namespace) ReplaceRE(pattern, repl, s interface{}, n ...interface{}) (_ string, err error) {
sp, err := cast.ToStringE(pattern)
if err != nil {
return
@@ -63,12 +64,27 @@
return
}
+ nn := -1
+ if len(n) > 0 {
+ nn, err = cast.ToIntE(n[0])
+ if err != nil {
+ return
+ }
+ }
+
re, err := reCache.Get(sp)
if err != nil {
return "", err
}
- return re.ReplaceAllString(ss, sr), nil
+ return re.ReplaceAllStringFunc(ss, func(str string) string {
+ if nn == 0 {
+ return str
+ }
+
+ nn -= 1
+ return re.ReplaceAllString(str, sr)
+ }), nil
}
// regexpCache represents a cache of regexp objects protected by a mutex.
--- a/tpl/strings/regexp_test.go
+++ b/tpl/strings/regexp_test.go
@@ -46,7 +46,7 @@
}
c.Assert(err, qt.IsNil)
- c.Assert(result, qt.DeepEquals, test.expect)
+ c.Check(result, qt.DeepEquals, test.expect)
}
}
@@ -58,19 +58,29 @@
pattern interface{}
repl interface{}
s interface{}
+ n []interface{}
expect interface{}
}{
- {"^https?://([^/]+).*", "$1", "http://gohugo.io/docs", "gohugo.io"},
- {"^https?://([^/]+).*", "$2", "http://gohugo.io/docs", ""},
- {"(ab)", "AB", "aabbaab", "aABbaAB"},
+ {"^https?://([^/]+).*", "$1", "http://gohugo.io/docs", nil, "gohugo.io"},
+ {"^https?://([^/]+).*", "$2", "http://gohugo.io/docs", nil, ""},
+ {"(ab)", "AB", "aabbaab", nil, "aABbaAB"},
+ {"(ab)", "AB", "aabbaab", []interface{}{1}, "aABbaab"},
// errors
- {"(ab", "AB", "aabb", false}, // invalid re
- {tstNoStringer{}, "$2", "http://gohugo.io/docs", false},
- {"^https?://([^/]+).*", tstNoStringer{}, "http://gohugo.io/docs", false},
- {"^https?://([^/]+).*", "$2", tstNoStringer{}, false},
+ {"(ab", "AB", "aabb", nil, false}, // invalid re
+ {tstNoStringer{}, "$2", "http://gohugo.io/docs", nil, false},
+ {"^https?://([^/]+).*", tstNoStringer{}, "http://gohugo.io/docs", nil, false},
+ {"^https?://([^/]+).*", "$2", tstNoStringer{}, nil, false},
} {
- result, err := ns.ReplaceRE(test.pattern, test.repl, test.s)
+ var (
+ result string
+ err error
+ )
+ if len(test.n) > 0 {
+ result, err = ns.ReplaceRE(test.pattern, test.repl, test.s, test.n...)
+ } else {
+ result, err = ns.ReplaceRE(test.pattern, test.repl, test.s)
+ }
if b, ok := test.expect.(bool); ok && !b {
c.Assert(err, qt.Not(qt.IsNil))
@@ -78,6 +88,6 @@
}
c.Assert(err, qt.IsNil)
- c.Assert(result, qt.Equals, test.expect)
+ c.Check(result, qt.Equals, test.expect)
}
}