diff options
| author | Russ Cox <rsc@golang.org> | 2021-05-20 12:46:33 -0400 |
|---|---|---|
| committer | Russ Cox <rsc@golang.org> | 2021-09-23 02:52:10 +0000 |
| commit | d0dd26a88c019d54f22463daae81e785f5867565 (patch) | |
| tree | 95803033c564f840d9778568f7a0dd8f81c07774 /src/text/template/exec.go | |
| parent | 93453233bd00cc641d2f959f1faf236e0094c2bf (diff) | |
| download | go-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.go | 24 |
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: |
