From e770b5b3aa9a2b6a7dcdc95cacd7ad3940ad34c9 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Daniel=20Mart=C3=AD?= Date: Wed, 20 Mar 2019 15:47:56 +0000 Subject: [PATCH] text/template: allow using -}} with many spaces MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit lexSpace consumed all spaces, even if the last one was part of a right delimiter like " -}}". Thus, "3 -}}" wouldn't lex as "3" and a right delimiter, but as "3", "-", and "}}". To fix that, make lexSpace stop if it encounters a right delimiter. Fixes #30948. Change-Id: I80a5546e5809e54f6823e2bf3a57a7e8808329be Reviewed-on: https://go-review.googlesource.com/c/go/+/168457 Reviewed-by: Daniel Martí --- src/text/template/parse/lex.go | 69 +++++++++++++++++---------- src/text/template/parse/parse_test.go | 1 + 2 files changed, 44 insertions(+), 26 deletions(-) diff --git a/src/text/template/parse/lex.go b/src/text/template/parse/lex.go index 92b97f423f..3d57708796 100644 --- a/src/text/template/parse/lex.go +++ b/src/text/template/parse/lex.go @@ -107,17 +107,18 @@ type stateFn func(*lexer) stateFn // lexer holds the state of the scanner. type lexer struct { - name string // the name of the input; used only for error reports - input string // the string being scanned - leftDelim string // start of action - rightDelim string // end of action - pos Pos // current position in the input - start Pos // start position of this item - width Pos // width of last rune read from input - items chan item // channel of scanned items - parenDepth int // nesting depth of ( ) exprs - line int // 1+number of newlines seen - startLine int // start line of this item + name string // the name of the input; used only for error reports + input string // the string being scanned + leftDelim string // start of action + rightDelim string // end of action + trimRightDelim string // end of action with trim marker + pos Pos // current position in the input + start Pos // start position of this item + width Pos // width of last rune read from input + items chan item // channel of scanned items + parenDepth int // nesting depth of ( ) exprs + line int // 1+number of newlines seen + startLine int // start line of this item } // next returns the next rune in the input. @@ -210,13 +211,14 @@ func lex(name, input, left, right string) *lexer { right = rightDelim } l := &lexer{ - name: name, - input: input, - leftDelim: left, - rightDelim: right, - items: make(chan item), - line: 1, - startLine: 1, + name: name, + input: input, + leftDelim: left, + rightDelim: right, + trimRightDelim: rightTrimMarker + right, + items: make(chan item), + line: 1, + startLine: 1, } go l.run() return l @@ -275,14 +277,12 @@ func rightTrimLength(s string) Pos { // atRightDelim reports whether the lexer is at a right delimiter, possibly preceded by a trim marker. func (l *lexer) atRightDelim() (delim, trimSpaces bool) { - if strings.HasPrefix(l.input[l.pos:], l.rightDelim) { - return true, false - } - // The right delim might have the marker before. - if strings.HasPrefix(l.input[l.pos:], rightTrimMarker) && - strings.HasPrefix(l.input[l.pos+trimMarkerLen:], l.rightDelim) { + if strings.HasPrefix(l.input[l.pos:], l.trimRightDelim) { // With trim marker. return true, true } + if strings.HasPrefix(l.input[l.pos:], l.rightDelim) { // Without trim marker. + return true, false + } return false, false } @@ -366,6 +366,7 @@ func lexInsideAction(l *lexer) stateFn { case r == eof || isEndOfLine(r): return l.errorf("unclosed action") case isSpace(r): + l.backup() // Put space back in case we have " -}}". return lexSpace case r == '=': l.emit(itemAssign) @@ -418,10 +419,26 @@ func lexInsideAction(l *lexer) stateFn { } // lexSpace scans a run of space characters. -// One space has already been seen. +// We have not consumed the first space, which is known to be present. +// Take care if there is a trim-marked right delimiter, which starts with a space. func lexSpace(l *lexer) stateFn { - for isSpace(l.peek()) { + var r rune + var numSpaces int + for { + r = l.peek() + if !isSpace(r) { + break + } l.next() + numSpaces++ + } + // Be careful about a trim-marked closing delimiter, which has a minus + // after a space. We know there is a space, so check for the '-' that might follow. + if strings.HasPrefix(l.input[l.pos-1:], l.trimRightDelim) { + l.backup() // Before the space. + if numSpaces == 1 { + return lexRightDelim // On the delim, so go right to that. + } } l.emit(itemSpace) return lexInsideAction diff --git a/src/text/template/parse/parse_test.go b/src/text/template/parse/parse_test.go index 5cb41d0bf5..6932cf232e 100644 --- a/src/text/template/parse/parse_test.go +++ b/src/text/template/parse/parse_test.go @@ -244,6 +244,7 @@ var parseTests = []parseTest{ {"trim left", "x \r\n\t{{- 3}}", noError, `"x"{{3}}`}, {"trim right", "{{3 -}}\n\n\ty", noError, `{{3}}"y"`}, {"trim left and right", "x \r\n\t{{- 3 -}}\n\n\ty", noError, `"x"{{3}}"y"`}, + {"trim with extra spaces", "x\n{{- 3 -}}\ny", noError, `"x"{{3}}"y"`}, {"comment trim left", "x \r\n\t{{- /* hi */}}", noError, `"x"`}, {"comment trim right", "{{/* hi */ -}}\n\n\ty", noError, `"y"`}, {"comment trim left and right", "x \r\n\t{{- /* */ -}}\n\n\ty", noError, `"x""y"`}, -- 2.48.1