aboutsummaryrefslogtreecommitdiff
path: root/src/text/template/parse
diff options
context:
space:
mode:
authorrogeryk <rogeryk@outlook.com>2023-11-28 20:41:03 +0800
committerGopher Robot <gobot@golang.org>2024-02-24 21:59:12 +0000
commit08d9397170e5870b72a95233e8e4ec43d2d70e30 (patch)
tree766a6afaf628dcaa79587eb6d0f7074eeebc7e9b /src/text/template/parse
parente0fc269f77af97f7cf33097d82f92ff7e08a2f06 (diff)
downloadgo-08d9397170e5870b72a95233e8e4ec43d2d70e30.tar.xz
text/template: add "else with" action
Add "else with" action will reduce the template complexity in some use cases(#57646). This action will be added: {{with pipeline}} T1 {{else with pipeline}} T0 {{end}}. Fixes #57646 Change-Id: I90ed546ab671805f753343b00bd3c9d1a1d5581d Reviewed-on: https://go-review.googlesource.com/c/go/+/545376 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Dmitri Shuralyov <dmitshur@golang.org> Reviewed-by: Cherry Mui <cherryyz@google.com> Reviewed-by: Rob Pike <r@golang.org> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Reviewed-by: Ian Lance Taylor <iant@google.com>
Diffstat (limited to 'src/text/template/parse')
-rw-r--r--src/text/template/parse/parse.go56
-rw-r--r--src/text/template/parse/parse_test.go4
2 files changed, 34 insertions, 26 deletions
diff --git a/src/text/template/parse/parse.go b/src/text/template/parse/parse.go
index d43d5334ba..b768dd4985 100644
--- a/src/text/template/parse/parse.go
+++ b/src/text/template/parse/parse.go
@@ -521,7 +521,7 @@ func (t *Tree) checkPipeline(pipe *PipeNode, context string) {
}
}
-func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) {
+func (t *Tree) parseControl(context string) (pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) {
defer t.popVars(len(t.vars))
pipe = t.pipeline(context, itemRightDelim)
if context == "range" {
@@ -535,27 +535,30 @@ func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int
switch next.Type() {
case nodeEnd: //done
case nodeElse:
- if allowElseIf {
- // Special case for "else if". If the "else" is followed immediately by an "if",
- // the elseControl will have left the "if" token pending. Treat
- // {{if a}}_{{else if b}}_{{end}}
- // as
- // {{if a}}_{{else}}{{if b}}_{{end}}{{end}}.
- // To do this, parse the if as usual and stop at it {{end}}; the subsequent{{end}}
- // is assumed. This technique works even for long if-else-if chains.
- // TODO: Should we allow else-if in with and range?
- if t.peek().typ == itemIf {
- t.next() // Consume the "if" token.
- elseList = t.newList(next.Position())
- elseList.append(t.ifControl())
- // Do not consume the next item - only one {{end}} required.
- break
+ // Special case for "else if" and "else with".
+ // If the "else" is followed immediately by an "if" or "with",
+ // the elseControl will have left the "if" or "with" token pending. Treat
+ // {{if a}}_{{else if b}}_{{end}}
+ // {{with a}}_{{else with b}}_{{end}}
+ // as
+ // {{if a}}_{{else}}{{if b}}_{{end}}{{end}}
+ // {{with a}}_{{else}}{{with b}}_{{end}}{{end}}.
+ // To do this, parse the "if" or "with" as usual and stop at it {{end}};
+ // the subsequent{{end}} is assumed. This technique works even for long if-else-if chains.
+ if context == "if" && t.peek().typ == itemIf {
+ t.next() // Consume the "if" token.
+ elseList = t.newList(next.Position())
+ elseList.append(t.ifControl())
+ } else if context == "with" && t.peek().typ == itemWith {
+ t.next()
+ elseList = t.newList(next.Position())
+ elseList.append(t.withControl())
+ } else {
+ elseList, next = t.itemList()
+ if next.Type() != nodeEnd {
+ t.errorf("expected end; found %s", next)
}
}
- elseList, next = t.itemList()
- if next.Type() != nodeEnd {
- t.errorf("expected end; found %s", next)
- }
}
return pipe.Position(), pipe.Line, pipe, list, elseList
}
@@ -567,7 +570,7 @@ func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int
//
// If keyword is past.
func (t *Tree) ifControl() Node {
- return t.newIf(t.parseControl(true, "if"))
+ return t.newIf(t.parseControl("if"))
}
// Range:
@@ -577,7 +580,7 @@ func (t *Tree) ifControl() Node {
//
// Range keyword is past.
func (t *Tree) rangeControl() Node {
- r := t.newRange(t.parseControl(false, "range"))
+ r := t.newRange(t.parseControl("range"))
return r
}
@@ -588,7 +591,7 @@ func (t *Tree) rangeControl() Node {
//
// If keyword is past.
func (t *Tree) withControl() Node {
- return t.newWith(t.parseControl(false, "with"))
+ return t.newWith(t.parseControl("with"))
}
// End:
@@ -606,10 +609,11 @@ func (t *Tree) endControl() Node {
//
// Else keyword is past.
func (t *Tree) elseControl() Node {
- // Special case for "else if".
peek := t.peekNonSpace()
- if peek.typ == itemIf {
- // We see "{{else if ... " but in effect rewrite it to {{else}}{{if ... ".
+ // The "{{else if ... " and "{{else with ..." will be
+ // treated as "{{else}}{{if ..." and "{{else}}{{with ...".
+ // So return the else node here.
+ if peek.typ == itemIf || peek.typ == itemWith {
return t.newElse(peek.pos, peek.line)
}
token := t.expect(itemRightDelim, "else")
diff --git a/src/text/template/parse/parse_test.go b/src/text/template/parse/parse_test.go
index 59e0a17412..faf226d1c3 100644
--- a/src/text/template/parse/parse_test.go
+++ b/src/text/template/parse/parse_test.go
@@ -244,6 +244,10 @@ var parseTests = []parseTest{
`{{with .X}}"hello"{{end}}`},
{"with with else", "{{with .X}}hello{{else}}goodbye{{end}}", noError,
`{{with .X}}"hello"{{else}}"goodbye"{{end}}`},
+ {"with with else with", "{{with .X}}hello{{else with .Y}}goodbye{{end}}", noError,
+ `{{with .X}}"hello"{{else}}{{with .Y}}"goodbye"{{end}}{{end}}`},
+ {"with else chain", "{{with .X}}X{{else with .Y}}Y{{else with .Z}}Z{{end}}", noError,
+ `{{with .X}}"X"{{else}}{{with .Y}}"Y"{{else}}{{with .Z}}"Z"{{end}}{{end}}{{end}}`},
// Trimming spaces.
{"trim left", "x \r\n\t{{- 3}}", noError, `"x"{{3}}`},
{"trim right", "{{3 -}}\n\n\ty", noError, `{{3}}"y"`},