Repository: gohugoio/hugo (language: go)

Issue:
Result of delimit is not a valid dict key
I noticed a strange behavior while trying to do some dict manipulation in a template. When a slice of strings is combined back together using the `delimit` function the resulting string is not a valid dictionary key.

To reproduce:

```
{{ $keyItems := slice "key" "parts" }}
{{ $key := delimit $keyItems " " }}
{{ if eq $key "key parts" }}
  Equal!
{{ end }}
{{ dict $key 1 }}
```

This code fails with `execute of template failed at <dict $key 1>: error calling dict: invalid dictionary key `.

The following code works:

```
{{ $key := "key parts" }}
{{ dict $key 1 }}
```

### What version of Hugo are you using (`hugo version`)?

<pre>
$ hugo version
hugo v0.119.0-b84644c008e0dc2c4b67bd69cccf87a41a03937e+extended linux/amd64 BuildDate=2023-09-24T15:20:17Z VendorInfo=brew
</pre>

### Does this issue reproduce with the latest release?
Yes.



Discussion / hints:
The `delimit` function returns the `template.HTML` data type, but the `dict` function expects a `string`.

Do this instead:

```text
{{ $s := slice "k1" "k2" }}
{{ $k := delimit $s " " | string }}
{{ dict $k 42 }}
```

You can use the `warnf` function to see the value and data type:

```
{{ delimit (slice "k1" "k2") " " | warnf "%[1]v (%[1]T)" }}
```

Should the `delimit` function return a `string` instead of `template.HTML`? That's debatable. See [#10876](https://github.com/gohugoio/hugo/issues/10876).


Thank you! I don't have an opinion on which type is better, but the current documentation for `delimit` is a little misleading:

> Loops through any array, slice, or map and returns a string of all the values separated by a delimiter. 

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Fix patch:
diff --git a/tpl/collections/collections.go b/tpl/collections/collections.go
index 279dbb16928..e34753f1777 100644
--- a/tpl/collections/collections.go
+++ b/tpl/collections/collections.go
@@ -17,16 +17,14 @@ package collections
 
 import (
 	"context"
+	"errors"
 	"fmt"
-	"html/template"
 	"math/rand"
 	"net/url"
 	"reflect"
 	"strings"
 	"time"
 
-	"errors"
-
 	"github.com/gohugoio/hugo/common/collections"
 	"github.com/gohugoio/hugo/common/hugo"
 	"github.com/gohugoio/hugo/common/maps"
@@ -101,7 +99,7 @@ func (ns *Namespace) After(n any, l any) (any, error) {
 
 // Delimit takes a given list l and returns a string delimited by sep.
 // If last is passed to the function, it will be used as the final delimiter.
-func (ns *Namespace) Delimit(ctx context.Context, l, sep any, last ...any) (template.HTML, error) {
+func (ns *Namespace) Delimit(ctx context.Context, l, sep any, last ...any) (string, error) {
 	d, err := cast.ToStringE(sep)
 	if err != nil {
 		return "", err
@@ -154,7 +152,7 @@ func (ns *Namespace) Delimit(ctx context.Context, l, sep any, last ...any) (temp
 		return "", fmt.Errorf("can't iterate over %v", l)
 	}
 
-	return template.HTML(str), nil
+	return str, nil
 }
 
 // Dictionary creates a new map from the given parameters by


Test patch:
diff --git a/hugolib/page_test.go b/hugolib/page_test.go
index 7e34f04998b..ca6164d2cf7 100644
--- a/hugolib/page_test.go
+++ b/hugolib/page_test.go
@@ -356,8 +356,8 @@ func normalizeExpected(ext, str string) string {
 }
 
 func testAllMarkdownEnginesForPages(t *testing.T,
-	assertFunc func(t *testing.T, ext string, pages page.Pages), settings map[string]any, pageSources ...string) {
-
+	assertFunc func(t *testing.T, ext string, pages page.Pages), settings map[string]any, pageSources ...string,
+) {
 	engines := []struct {
 		ext           string
 		shouldExecute func() bool
@@ -643,7 +643,6 @@ Simple Page With Some Date`
 }
 
 func TestPageRawContent(t *testing.T) {
-
 	files := `
 -- hugo.toml --
 -- content/basic.md --
@@ -668,7 +667,6 @@ title: "empty"
 
 	b.AssertFileContent("public/basic/index.html", "|**basic**|")
 	b.AssertFileContent("public/empty/index.html", "! title")
-
 }
 
 func TestPageWithShortCodeInSummary(t *testing.T) {
@@ -1954,5 +1952,4 @@ func TestRenderWithoutArgument(t *testing.T) {
 	).BuildE()
 
 	b.Assert(err, qt.IsNotNil)
-
 }
diff --git a/tpl/collections/collections_test.go b/tpl/collections/collections_test.go
index 43f8377f3af..dcdd3bd5cb0 100644
--- a/tpl/collections/collections_test.go
+++ b/tpl/collections/collections_test.go
@@ -71,8 +71,7 @@ func TestAfter(t *testing.T) {
 	}
 }
 
-type tstGrouper struct {
-}
+type tstGrouper struct{}
 
 type tstGroupers []*tstGrouper
 
@@ -81,8 +80,7 @@ func (g tstGrouper) Group(key any, items any) (any, error) {
 	return fmt.Sprintf("%v(%d)", key, ilen), nil
 }
 
-type tstGrouper2 struct {
-}
+type tstGrouper2 struct{}
 
 func (g *tstGrouper2) Group(key any, items any) (any, error) {
 	ilen := reflect.ValueOf(items).Len()
@@ -134,7 +132,7 @@ func TestDelimit(t *testing.T) {
 		seq       any
 		delimiter any
 		last      any
-		expect    template.HTML
+		expect    string
 	}{
 		{[]string{"class1", "class2", "class3"}, " ", nil, "class1 class2 class3"},
 		{[]int{1, 2, 3, 4, 5}, ",", nil, "1,2,3,4,5"},
@@ -163,7 +161,7 @@ func TestDelimit(t *testing.T) {
 	} {
 		errMsg := qt.Commentf("[%d] %v", i, test)
 
-		var result template.HTML
+		var result string
 		var err error
 
 		if test.last == nil {