diff --git a/tpl/strings/init.go b/tpl/strings/init.go index 09419a51f28..26d63519a0e 100644 --- a/tpl/strings/init.go +++ b/tpl/strings/init.go @@ -99,7 +99,16 @@ func init() { 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, diff --git a/tpl/strings/regexp.go b/tpl/strings/regexp.go index 7b52c9f6e05..c6d731a0d6f 100644 --- a/tpl/strings/regexp.go +++ b/tpl/strings/regexp.go @@ -46,8 +46,9 @@ func (ns *Namespace) FindRE(expr string, content interface{}, limit ...interface } // 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 @@ func (ns *Namespace) ReplaceRE(pattern, repl, s interface{}) (_ string, err erro 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. diff --git a/tpl/strings/regexp_test.go b/tpl/strings/regexp_test.go index e05b00fb100..433181f67ee 100644 --- a/tpl/strings/regexp_test.go +++ b/tpl/strings/regexp_test.go @@ -46,7 +46,7 @@ func TestFindRE(t *testing.T) { } c.Assert(err, qt.IsNil) - c.Assert(result, qt.DeepEquals, test.expect) + c.Check(result, qt.DeepEquals, test.expect) } } @@ -58,19 +58,29 @@ func TestReplaceRE(t *testing.T) { 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 @@ func TestReplaceRE(t *testing.T) { } c.Assert(err, qt.IsNil) - c.Assert(result, qt.Equals, test.expect) + c.Check(result, qt.Equals, test.expect) } }