shithub: hugo

Download patch

ref: 5bfe16ef8d37b7030da9bdf1b68bdb18ff0079a3
parent: 54750b078059734c3f2047ae3f56cac64a9f0666
author: digitalcraftsman <[email protected]>
date: Tue Apr 5 14:02:18 EDT 2016

tpl: Add findRE template func

--- a/docs/content/templates/functions.md
+++ b/docs/content/templates/functions.md
@@ -474,6 +474,36 @@
 
 e.g. `{{ "cat" | pluralize }}` → "cats"
 
+### findRE
+Returns a list of strings that match the regular expression. By default all matches will be included. The number of matches can be limitted with an optional third parameter.
+
+The example below returns a list of all second level headers (`<h2>`) in the content:
+
+    {{ findRE "<h2.*?>(.|\n)*?</h2>" .Content }}
+
+We can limit the number of matches in that list with a third parameter. Let's say we want to have at most one match (or none if no substring matched):
+
+    {{ findRE "<h2.*?>(.|\n)*?</h2>" .Content 1 }}
+    <!-- returns ["<h2 id="#foo">Foo</h2>"] -->
+
+`findRe` allows us to build an automatically generated table of contents that could be used for a simple scrollspy:
+
+    {{ $headers := findRE "<h2.*?>(.|\n)*?</h2>" .Content }}
+
+    {{ if ge (len $headers) 1 }}
+        <ul>
+        {{ range $headers }}
+            <li>
+                <a href="#{{ . | plainify | urlize }}">
+                    {{ . | plainify }}
+                </a>
+            </li>
+        {{ end }}
+        </ul>
+    {{ end }}
+
+First, we try to find all second-level headers and generate a list if at least one header was found. `plainify` strips the HTML and `urlize` converts the header into an a valid URL.
+
 ### replace
 Replaces all occurrences of the search string with the replacement string.
 
--- a/tpl/template_funcs.go
+++ b/tpl/template_funcs.go
@@ -437,6 +437,22 @@
 	return seqv.Slice(0, limitv).Interface(), nil
 }
 
+// findRE returns a list of strings that match the regular expression. By default all matches
+// will be included. The number of matches can be limitted with an optional third parameter.
+func findRE(expr string, content interface{}, limit ...int) ([]string, error) {
+	re, err := reCache.Get(expr)
+	if err != nil {
+		return nil, err
+	}
+
+	conv := cast.ToString(content)
+	if len(limit) > 0 {
+		return re.FindAllString(conv, limit[0]), nil
+	}
+
+	return re.FindAllString(conv, -1), nil
+}
+
 // last returns the last N items in a rangeable list.
 func last(limit interface{}, seq interface{}) (interface{}, error) {
 	if limit == nil || seq == nil {
@@ -1729,6 +1745,7 @@
 		"echoParam":    returnWhenSet,
 		"emojify":      emojify,
 		"eq":           eq,
+		"findRE":       findRE,
 		"first":        first,
 		"ge":           ge,
 		"getCSV":       getCSV,
--- a/tpl/template_funcs_test.go
+++ b/tpl/template_funcs_test.go
@@ -89,6 +89,7 @@
 div: {{div 6 3}}
 emojify: {{ "I :heart: Hugo" | emojify }}
 eq: {{ if eq .Section "blog" }}current{{ end }}
+findRE: {{ findRE "[G|g]o" "Hugo is a static side generator written in Go." 1 }}
 hasPrefix 1: {{ hasPrefix "Hugo" "Hu" }}
 hasPrefix 2: {{ hasPrefix "Hugo" "Fu" }}
 in: {{ if in "this string contains a substring" "substring" }}Substring found!{{ end }}
@@ -138,6 +139,7 @@
 div: 2
 emojify: I ❤️  Hugo
 eq: current
+findRE: [go]
 hasPrefix 1: true
 hasPrefix 2: false
 in: Substring found!
@@ -1798,6 +1800,30 @@
 			t.Errorf("[%d] %s", i, err)
 		}
 		assert.Equal(t, val.expect, v)
+	}
+}
+
+func TestFindRE(t *testing.T) {
+	for i, this := range []struct {
+		expr    string
+		content string
+		limit   int
+		expect  []string
+		ok      bool
+	}{
+		{"[G|g]o", "Hugo is a static side generator written in Go.", 2, []string{"go", "Go"}, true},
+		{"[G|g]o", "Hugo is a static side generator written in Go.", -1, []string{"go", "Go"}, true},
+		{"[G|g]o", "Hugo is a static side generator written in Go.", 1, []string{"go"}, true},
+		{"[G|g]o", "Hugo is a static side generator written in Go.", 0, []string(nil), true},
+		{"[G|go", "Hugo is a static side generator written in Go.", 0, []string(nil), false},
+	} {
+		res, err := findRE(this.expr, this.content, this.limit)
+
+		if err != nil && this.ok {
+			t.Errorf("[%d] returned an unexpected error: %s", i, err)
+		}
+
+		assert.Equal(t, this.expect, res)
 	}
 }