aboutsummaryrefslogtreecommitdiff
path: root/src/text/template/exec.go
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2021-05-20 12:46:33 -0400
committerRuss Cox <rsc@golang.org>2021-09-23 02:52:10 +0000
commitd0dd26a88c019d54f22463daae81e785f5867565 (patch)
tree95803033c564f840d9778568f7a0dd8f81c07774 /src/text/template/exec.go
parent93453233bd00cc641d2f959f1faf236e0094c2bf (diff)
downloadgo-d0dd26a88c019d54f22463daae81e785f5867565.tar.xz
html/template, text/template: implement break and continue for range loops
Break and continue for range loops was accepted as a proposal in June 2017. It was implemented in CL 66410 (Oct 2017) but then rolled back in CL 92155 (Feb 2018) because html/template changes had not been implemented. This CL reimplements break and continue in text/template and then adds support for them in html/template as well. Fixes #20531. Change-Id: I05330482a976f1c078b4b49c2287bd9031bb7616 Reviewed-on: https://go-review.googlesource.com/c/go/+/321491 Trust: Russ Cox <rsc@golang.org> Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Rob Pike <r@golang.org>
Diffstat (limited to 'src/text/template/exec.go')
-rw-r--r--src/text/template/exec.go24
1 files changed, 23 insertions, 1 deletions
diff --git a/src/text/template/exec.go b/src/text/template/exec.go
index 6e005b57d7..e03920964e 100644
--- a/src/text/template/exec.go
+++ b/src/text/template/exec.go
@@ -5,6 +5,7 @@
package template
import (
+ "errors"
"fmt"
"internal/fmtsort"
"io"
@@ -243,6 +244,12 @@ func (t *Template) DefinedTemplates() string {
return b.String()
}
+// Sentinel errors for use with panic to signal early exits from range loops.
+var (
+ walkBreak = errors.New("break")
+ walkContinue = errors.New("continue")
+)
+
// Walk functions step through the major pieces of the template structure,
// generating output as they go.
func (s *state) walk(dot reflect.Value, node parse.Node) {
@@ -255,7 +262,11 @@ func (s *state) walk(dot reflect.Value, node parse.Node) {
if len(node.Pipe.Decl) == 0 {
s.printValue(node, val)
}
+ case *parse.BreakNode:
+ panic(walkBreak)
case *parse.CommentNode:
+ case *parse.ContinueNode:
+ panic(walkContinue)
case *parse.IfNode:
s.walkIfOrWith(parse.NodeIf, dot, node.Pipe, node.List, node.ElseList)
case *parse.ListNode:
@@ -334,6 +345,11 @@ func isTrue(val reflect.Value) (truth, ok bool) {
func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) {
s.at(r)
+ defer func() {
+ if r := recover(); r != nil && r != walkBreak {
+ panic(r)
+ }
+ }()
defer s.pop(s.mark())
val, _ := indirect(s.evalPipeline(dot, r.Pipe))
// mark top of stack before any variables in the body are pushed.
@@ -347,8 +363,14 @@ func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) {
if len(r.Pipe.Decl) > 1 {
s.setTopVar(2, index)
}
+ defer s.pop(mark)
+ defer func() {
+ // Consume panic(walkContinue)
+ if r := recover(); r != nil && r != walkContinue {
+ panic(r)
+ }
+ }()
s.walk(elem, r.List)
- s.pop(mark)
}
switch val.Kind() {
case reflect.Array, reflect.Slice: