shithub: hugo

Download patch

ref: f9ebaaed1be1e4a26eef2aebd2c7554c979f29fa
parent: d39636a5fc6bb82b3e0bd013858c7d116faa0c6b
author: Cameron Moore <moorereason@gmail.com>
date: Thu Aug 27 17:59:20 EDT 2020

tpl: Add limit option to replace template function

Updates #7586

--- a/tpl/strings/init.go
+++ b/tpl/strings/init.go
@@ -63,7 +63,8 @@
 			[][2]string{
 				{
 					`{{ findRE "[G|g]o" "Hugo is a static side generator written in Go." "1" }}`,
-					`[go]`},
+					`[go]`,
+				},
 			},
 		)
 
@@ -87,7 +88,12 @@
 			[][2]string{
 				{
 					`{{ replace "Batman and Robin" "Robin" "Catwoman" }}`,
-					`Batman and Catwoman`},
+					`Batman and Catwoman`,
+				},
+				{
+					`{{ replace "aabbaabb" "a" "z" 2 }}`,
+					`zzbbaabb`,
+				},
 			},
 		)
 
@@ -192,7 +198,6 @@
 		)
 
 		return ns
-
 	}
 
 	internal.AddTemplateFuncsNamespace(f)
--- a/tpl/strings/strings.go
+++ b/tpl/strings/strings.go
@@ -19,14 +19,12 @@
 	"fmt"
 	"html/template"
 	"strings"
-
-	_strings "strings"
 	"unicode/utf8"
 
-	_errors "github.com/pkg/errors"
-
 	"github.com/gohugoio/hugo/deps"
 	"github.com/gohugoio/hugo/helpers"
+
+	_errors "github.com/pkg/errors"
 	"github.com/spf13/cast"
 )
 
@@ -79,7 +77,7 @@
 	}
 
 	counter := 0
-	for _, word := range _strings.Fields(helpers.StripHTML(ss)) {
+	for _, word := range strings.Fields(helpers.StripHTML(ss)) {
 		runeCount := utf8.RuneCountInString(word)
 		if len(word) == runeCount {
 			counter++
@@ -112,7 +110,7 @@
 		return "", err
 	}
 
-	res := _strings.TrimRight(ss, "\r\n")
+	res := strings.TrimRight(ss, "\r\n")
 	switch s.(type) {
 	case template.HTML:
 		return template.HTML(res), nil
@@ -119,7 +117,6 @@
 	default:
 		return res, nil
 	}
-
 }
 
 // Contains reports whether substr is in s.
@@ -134,7 +131,7 @@
 		return false, err
 	}
 
-	return _strings.Contains(ss, su), nil
+	return strings.Contains(ss, su), nil
 }
 
 // ContainsAny reports whether any Unicode code points in chars are within s.
@@ -149,7 +146,7 @@
 		return false, err
 	}
 
-	return _strings.ContainsAny(ss, sc), nil
+	return strings.ContainsAny(ss, sc), nil
 }
 
 // HasPrefix tests whether the input s begins with prefix.
@@ -164,7 +161,7 @@
 		return false, err
 	}
 
-	return _strings.HasPrefix(ss, sx), nil
+	return strings.HasPrefix(ss, sx), nil
 }
 
 // HasSuffix tests whether the input s begins with suffix.
@@ -179,12 +176,13 @@
 		return false, err
 	}
 
-	return _strings.HasSuffix(ss, sx), nil
+	return strings.HasSuffix(ss, sx), nil
 }
 
 // Replace returns a copy of the string s with all occurrences of old replaced
-// with new.
-func (ns *Namespace) Replace(s, old, new interface{}) (string, error) {
+// with new.  The number of replacements can be limited with an optional fourth
+// parameter.
+func (ns *Namespace) Replace(s, old, new interface{}, limit ...interface{}) (string, error) {
 	ss, err := cast.ToStringE(s)
 	if err != nil {
 		return "", err
@@ -200,7 +198,16 @@
 		return "", err
 	}
 
-	return _strings.Replace(ss, so, sn, -1), nil
+	if len(limit) == 0 {
+		return strings.ReplaceAll(ss, so, sn), nil
+	}
+
+	lim, err := cast.ToIntE(limit[0])
+	if err != nil {
+		return "", err
+	}
+
+	return strings.Replace(ss, so, sn, lim), nil
 }
 
 // SliceString slices a string by specifying a half-open range with
@@ -247,7 +254,6 @@
 	} else {
 		return string(asRunes[:]), nil
 	}
-
 }
 
 // Split slices an input string into all substrings separated by delimiter.
@@ -257,7 +263,7 @@
 		return []string{}, err
 	}
 
-	return _strings.Split(aStr, delimiter), nil
+	return strings.Split(aStr, delimiter), nil
 }
 
 // Substr extracts parts of a string, beginning at the character at the specified
@@ -362,7 +368,7 @@
 		return "", err
 	}
 
-	return _strings.ToLower(ss), nil
+	return strings.ToLower(ss), nil
 }
 
 // ToUpper returns a copy of the input s with all Unicode letters mapped to their
@@ -373,7 +379,7 @@
 		return "", err
 	}
 
-	return _strings.ToUpper(ss), nil
+	return strings.ToUpper(ss), nil
 }
 
 // Trim returns a string with all leading and trailing characters defined
@@ -389,7 +395,7 @@
 		return "", err
 	}
 
-	return _strings.Trim(ss, sc), nil
+	return strings.Trim(ss, sc), nil
 }
 
 // TrimLeft returns a slice of the string s with all leading characters
@@ -405,7 +411,7 @@
 		return "", err
 	}
 
-	return _strings.TrimLeft(ss, sc), nil
+	return strings.TrimLeft(ss, sc), nil
 }
 
 // TrimPrefix returns s without the provided leading prefix string. If s doesn't
@@ -421,7 +427,7 @@
 		return "", err
 	}
 
-	return _strings.TrimPrefix(ss, sx), nil
+	return strings.TrimPrefix(ss, sx), nil
 }
 
 // TrimRight returns a slice of the string s with all trailing characters
@@ -437,7 +443,7 @@
 		return "", err
 	}
 
-	return _strings.TrimRight(ss, sc), nil
+	return strings.TrimRight(ss, sc), nil
 }
 
 // TrimSuffix returns s without the provided trailing suffix string. If s
@@ -453,7 +459,7 @@
 		return "", err
 	}
 
-	return _strings.TrimSuffix(ss, sx), nil
+	return strings.TrimSuffix(ss, sx), nil
 }
 
 // Repeat returns a new string consisting of count copies of the string s.
@@ -472,5 +478,5 @@
 		return "", errors.New("strings: negative Repeat count")
 	}
 
-	return _strings.Repeat(ss, sn), nil
+	return strings.Repeat(ss, sn), nil
 }
--- a/tpl/strings/strings_test.go
+++ b/tpl/strings/strings_test.go
@@ -15,11 +15,11 @@
 
 import (
 	"html/template"
-
 	"testing"
 
-	qt "github.com/frankban/quicktest"
 	"github.com/gohugoio/hugo/deps"
+
+	qt "github.com/frankban/quicktest"
 	"github.com/spf13/cast"
 	"github.com/spf13/viper"
 )
@@ -302,18 +302,30 @@
 		s      interface{}
 		old    interface{}
 		new    interface{}
+		limit  interface{}
 		expect interface{}
 	}{
-		{"aab", "a", "b", "bbb"},
-		{"11a11", 1, 2, "22a22"},
-		{12345, 1, 2, "22345"},
+		{"aab", "a", "b", nil, "bbb"},
+		{"11a11", 1, 2, nil, "22a22"},
+		{12345, 1, 2, nil, "22345"},
+		{"aab", "a", "b", 1, "bab"},
+		{"11a11", 1, 2, 2, "22a11"},
 		// errors
-		{tstNoStringer{}, "a", "b", false},
-		{"a", tstNoStringer{}, "b", false},
-		{"a", "b", tstNoStringer{}, false},
+		{tstNoStringer{}, "a", "b", nil, false},
+		{"a", tstNoStringer{}, "b", nil, false},
+		{"a", "b", tstNoStringer{}, nil, false},
 	} {
 
-		result, err := ns.Replace(test.s, test.old, test.new)
+		var (
+			result string
+			err    error
+		)
+
+		if test.limit != nil {
+			result, err = ns.Replace(test.s, test.old, test.new, test.limit)
+		} else {
+			result, err = ns.Replace(test.s, test.old, test.new)
+		}
 
 		if b, ok := test.expect.(bool); ok && !b {
 			c.Assert(err, qt.Not(qt.IsNil))