diff --git a/docs/content/en/functions/strings.Repeat.md b/docs/content/en/functions/strings.Repeat.md new file mode 100644 index 000000000..8dcb8eaa2 --- /dev/null +++ b/docs/content/en/functions/strings.Repeat.md @@ -0,0 +1,31 @@ +--- +title: strings.Repeat +# linktitle: +description: Returns a string consisting of count copies of the string s. +godocref: +date: 2018-05-31 +publishdate: 2018-05-31 +lastmod: 2018-05-31 +categories: [functions] +menu: + docs: + parent: "functions" +keywords: [strings] +signature: ["strings.Repeat INPUT COUNT"] +workson: [] +hugoversion: +relatedfuncs: [] +deprecated: false +--- + +`strings.Repeat` provides the Go [`strings.Repeat`](https://golang.org/pkg/strings/#Repeat) function for Hugo templates. It takes a string and a count, and returns a string with consisting of count copies of the string argument. + +``` +{{ strings.Repeat "yo" 3 }} → "yoyoyo" +``` + +`strings.Repeat` *requires* the second argument, which tells the function how many times to repeat the first argument; there is no default. However, it can be used as a pipeline: + +``` +{{ "yo" | strings.Repeat 3 }} → "yoyoyo" +``` diff --git a/docs/data/docs.json b/docs/data/docs.json index 2a7f727b7..4eddf8eae 100644 --- a/docs/data/docs.json +++ b/docs/data/docs.json @@ -3410,6 +3410,20 @@ ] ] }, + "Repeat": { + "Description": "Repeat returns a new string consisting of count copies of the string s.", + "Args": [ + "s", + "n" + ], + "Aliases": null, + "Examples": [ + [ + "{{ \"yo\" | strings.Repeat 4 }}", + "yoyoyoyo" + ] + ] + }, "Truncate": { "Description": "Truncate truncates a given string to the specified length.", "Args": [ diff --git a/tpl/strings/init.go b/tpl/strings/init.go index 54009b897..883ce76d1 100644 --- a/tpl/strings/init.go +++ b/tpl/strings/init.go @@ -158,6 +158,13 @@ func init() { }, ) + ns.AddMethodMapping(ctx.Repeat, + nil, + [][2]string{ + {`{{ "yo" | strings.Repeat 4 }}`, `yoyoyoyo`}, + }, + ) + ns.AddMethodMapping(ctx.ToUpper, []string{"upper"}, [][2]string{ diff --git a/tpl/strings/strings.go b/tpl/strings/strings.go index 6d423391f..796b8ff62 100644 --- a/tpl/strings/strings.go +++ b/tpl/strings/strings.go @@ -17,6 +17,7 @@ import ( "errors" "fmt" "html/template" + "math" _strings "strings" "unicode/utf8" @@ -417,3 +418,23 @@ func (ns *Namespace) TrimSuffix(suffix, s interface{}) (string, error) { return _strings.TrimSuffix(ss, sx), nil } + +// Repeat returns a new string consisting of count copies of the string s. +// The count is limited to an in16 value (up to 32767). +func (ns *Namespace) Repeat(n, s interface{}) (string, error) { + ss, err := cast.ToStringE(s) + if err != nil { + return "", err + } + + sn, err := cast.ToIntE(n) + if err != nil { + return "", err + } + + if sn > math.MaxInt16 { + return "", fmt.Errorf("Cannot repeat string more than %d times", math.MaxInt16) + } + + return _strings.Repeat(ss, sn), nil +} diff --git a/tpl/strings/strings_test.go b/tpl/strings/strings_test.go index 91e71fd01..bf19ad562 100644 --- a/tpl/strings/strings_test.go +++ b/tpl/strings/strings_test.go @@ -16,6 +16,7 @@ package strings import ( "fmt" "html/template" + "math" "testing" "github.com/gohugoio/hugo/deps" @@ -709,3 +710,38 @@ func TestTrimSuffix(t *testing.T) { assert.Equal(t, test.expect, result, errMsg) } } + +func TestRepeat(t *testing.T) { + t.Parallel() + + for i, test := range []struct { + s interface{} + n interface{} + expect interface{} + }{ + {"yo", "2", "yoyo"}, + {"~", "16", "~~~~~~~~~~~~~~~~"}, + {"", "0", ""}, + {"yay", "1", "yay"}, + {1221, "1", "1221"}, + {1221, 2, "12211221"}, + {template.HTML(""), "2", ""}, + {[]byte(""), 2, ""}, + // errors + {"", tstNoStringer{}, false}, + {tstNoStringer{}, "", false}, + {"hi", math.MaxInt16 + 1, false}, + } { + errMsg := fmt.Sprintf("[%d] %v", i, test) + + result, err := ns.Repeat(test.n, test.s) + + if b, ok := test.expect.(bool); ok && !b { + require.Error(t, err, errMsg) + continue + } + + require.NoError(t, err, errMsg) + assert.Equal(t, test.expect, result, errMsg) + } +}