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
This commit is contained in:
Cameron Moore 2020-08-28 09:29:26 -05:00 committed by Bjørn Erik Pedersen
parent 047af7cfe5
commit cdfd1c99ba
3 changed files with 49 additions and 14 deletions

View file

@ -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,

View file

@ -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.

View file

@ -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)
}
}