Skip to content

Commit 34cbc66

Browse files
committed
Revert whitespace trimming
This commit reverts whitespace trimming after it was reported in issue #40 that it breaks some use cases. Reverted commits: - eb5dd3e - e79bb8c - 6f55900 - 6d69dcf - 438bb0a
1 parent 031c220 commit 34cbc66

File tree

4 files changed

+44
-135
lines changed

4 files changed

+44
-135
lines changed

README.md

-6
Original file line numberDiff line numberDiff line change
@@ -152,12 +152,6 @@ The `<%= %>` block will print your text as escaped HTML, however, sometimes you
152152
To do this, simply wrap your Go expression with `<%==` and `%>` tags.
153153

154154

155-
#### Trim Space
156-
157-
The `<% %>` blocks can be optionally adorned with a `-` on one or both sides to trigger trimming of whitespace on that side.
158-
For example `<%-= r.Name %>` will trim whitespace before the print block, but not after it.
159-
160-
161155
### Components
162156

163157
Simple code and print tags work well for simple templates but it can be difficult to make reusable functionality.

ego.go

+19-47
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"io"
1111
"sort"
1212
"strings"
13-
"unicode"
1413
)
1514

1615
// Template represents an entire Ego template.
@@ -114,8 +113,7 @@ func writeBlocksTo(buf *bytes.Buffer, blks []Block) {
114113
// Normalize joins together adjacent text blocks.
115114
func normalizeBlocks(a []Block) []Block {
116115
a = joinAdjacentTextBlocks(a)
117-
a = trimLeftRight(a)
118-
a = trimEmptyTextBlocks(a)
116+
a = trimTrailingEmptyTextBlocks(a)
119117
return a
120118
}
121119

@@ -144,33 +142,18 @@ func joinAdjacentTextBlocks(a []Block) []Block {
144142
return other
145143
}
146144

147-
func trimLeftRight(a []Block) []Block {
148-
for i, blk := range a {
149-
trimLeft, trimRight := blk.trim()
150-
if trimLeft && i > 1 {
151-
if textBlock, ok := a[i-1].(*TextBlock); ok {
152-
textBlock.Content = strings.TrimRightFunc(textBlock.Content, unicode.IsSpace)
153-
}
154-
}
155-
if trimRight && i+1 < len(a) {
156-
if textBlock, ok := a[i+1].(*TextBlock); ok {
157-
textBlock.Content = strings.TrimLeftFunc(textBlock.Content, unicode.IsSpace)
158-
}
145+
func trimTrailingEmptyTextBlocks(a []Block) []Block {
146+
for len(a) > 0 {
147+
blk, ok := a[len(a)-1].(*TextBlock)
148+
if !ok || strings.TrimSpace(blk.Content) != "" {
149+
break
159150
}
151+
a[len(a)-1] = nil
152+
a = a[:len(a)-1]
160153
}
161154
return a
162155
}
163156

164-
func trimEmptyTextBlocks(a []Block) []Block {
165-
b := make([]Block, 0, len(a))
166-
for _, blk := range a {
167-
if tb, ok := blk.(*TextBlock); !ok || strings.TrimSpace(tb.Content) != "" {
168-
b = append(b, blk)
169-
}
170-
}
171-
return b
172-
}
173-
174157
func injectImports(f *ast.File) {
175158
names := []string{`"fmt"`, `"html"`, `"io"`, `"context"`}
176159

@@ -249,7 +232,6 @@ func removeImportSpecs(decl *ast.GenDecl, names []string) {
249232
// Block represents an element of the template.
250233
type Block interface {
251234
block()
252-
trim() (bool, bool)
253235
}
254236

255237
func (*TextBlock) block() {}
@@ -261,15 +243,6 @@ func (*ComponentEndBlock) block() {}
261243
func (*AttrStartBlock) block() {}
262244
func (*AttrEndBlock) block() {}
263245

264-
func (*TextBlock) trim() (bool, bool) { return false, false }
265-
func (b *CodeBlock) trim() (bool, bool) { return b.TrimLeft, b.TrimRight }
266-
func (b *PrintBlock) trim() (bool, bool) { return b.TrimLeft, b.TrimRight }
267-
func (b *RawPrintBlock) trim() (bool, bool) { return b.TrimLeft, b.TrimRight }
268-
func (*ComponentStartBlock) trim() (bool, bool) { return false, false }
269-
func (*ComponentEndBlock) trim() (bool, bool) { return false, false }
270-
func (*AttrStartBlock) trim() (bool, bool) { return false, false }
271-
func (*AttrEndBlock) trim() (bool, bool) { return false, false }
272-
273246
// TextBlock represents a UTF-8 encoded block of text that is written to the writer as-is.
274247
type TextBlock struct {
275248
Pos Pos
@@ -278,26 +251,20 @@ type TextBlock struct {
278251

279252
// CodeBlock represents a Go code block that is printed as-is to the template.
280253
type CodeBlock struct {
281-
Pos Pos
282-
Content string
283-
TrimLeft bool
284-
TrimRight bool
254+
Pos Pos
255+
Content string
285256
}
286257

287258
// PrintBlock represents a block that will HTML escape the contents before outputting
288259
type PrintBlock struct {
289-
Pos Pos
290-
Content string
291-
TrimLeft bool
292-
TrimRight bool
260+
Pos Pos
261+
Content string
293262
}
294263

295264
// RawPrintBlock represents a block of the template that is printed out to the writer.
296265
type RawPrintBlock struct {
297-
Pos Pos
298-
Content string
299-
TrimLeft bool
300-
TrimRight bool
266+
Pos Pos
267+
Content string
301268
}
302269

303270
// ComponentStartBlock represents the opening block of an ego component.
@@ -438,6 +405,11 @@ func stringSliceContains(a []string, v string) bool {
438405
return false
439406
}
440407

408+
type stackElem struct {
409+
block Block
410+
yield []Block
411+
}
412+
441413
// AttrNames returns a sorted list of names for an attribute set.
442414
func AttrNames(attrs map[string]interface{}) []string {
443415
a := make([]string, 0, len(attrs))

scanner.go

+25-43
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,11 @@ func (s *Scanner) Scan() (Block, error) {
5353
}
5454

5555
// Special handling for ego blocks.
56-
if s.peekN(4) == "<%==" || s.peekN(5) == "<%-==" {
56+
if s.peekN(4) == "<%==" {
5757
return s.scanRawPrintBlock()
58-
} else if s.peekN(3) == "<%=" || s.peekN(4) == "<%-=" {
58+
} else if s.peekN(3) == "<%=" {
5959
return s.scanPrintBlock()
60-
} else if s.peekN(2) == "<%" || s.peekN(3) == "<%-" {
60+
} else if s.peekN(2) == "<%" {
6161
return s.scanCodeBlock()
6262
}
6363

@@ -78,66 +78,45 @@ func (s *Scanner) scanTextBlock() (*TextBlock, error) {
7878
buf.WriteRune(s.read())
7979
}
8080

81-
b.Content = buf.String()
81+
b.Content = string(buf.Bytes())
8282

8383
return b, nil
8484
}
8585

8686
func (s *Scanner) scanCodeBlock() (*CodeBlock, error) {
8787
b := &CodeBlock{Pos: s.pos}
88+
assert(s.readN(2) == "<%")
8889

89-
if s.peekN(3) == "<%-" {
90-
assert(s.readN(3) == "<%-")
91-
b.TrimLeft = true
92-
} else {
93-
assert(s.readN(2) == "<%")
94-
}
95-
96-
content, trimRight, err := s.scanContent()
90+
content, err := s.scanContent()
9791
if err != nil {
9892
return nil, err
9993
}
10094
b.Content = content
101-
b.TrimRight = trimRight
10295

10396
return b, nil
10497
}
10598

10699
func (s *Scanner) scanPrintBlock() (*PrintBlock, error) {
107100
b := &PrintBlock{Pos: s.pos}
101+
assert(s.readN(3) == "<%=")
108102

109-
if s.peekN(3) == "<%-" {
110-
assert(s.readN(4) == "<%-=")
111-
b.TrimLeft = true
112-
} else {
113-
assert(s.readN(3) == "<%=")
114-
}
115-
116-
content, trimRight, err := s.scanContent()
103+
content, err := s.scanContent()
117104
if err != nil {
118105
return nil, err
119106
}
120107
b.Content = content
121-
b.TrimRight = trimRight
122108
return b, nil
123109
}
124110

125111
func (s *Scanner) scanRawPrintBlock() (*RawPrintBlock, error) {
126112
b := &RawPrintBlock{Pos: s.pos}
113+
assert(s.readN(4) == "<%==")
127114

128-
if s.peekN(3) == "<%-" {
129-
assert(s.readN(5) == "<%-==")
130-
b.TrimLeft = true
131-
} else {
132-
assert(s.readN(4) == "<%==")
133-
}
134-
135-
content, trimRight, err := s.scanContent()
115+
content, err := s.scanContent()
136116
if err != nil {
137117
return nil, err
138118
}
139119
b.Content = content
140-
b.TrimRight = trimRight
141120
return b, nil
142121
}
143122

@@ -345,26 +324,28 @@ func (s *Scanner) scanAttrEndBlock() (_ *AttrEndBlock, err error) {
345324
return b, nil
346325
}
347326

348-
// scans the reader until %> or -%> is reached.
349-
func (s *Scanner) scanContent() (string, bool, error) {
327+
// scans the reader until %> is reached.
328+
func (s *Scanner) scanContent() (string, error) {
350329
var buf bytes.Buffer
351-
var trimRight bool
352330
for {
353331
ch := s.read()
354332
if ch == eof {
355-
return "", false, &SyntaxError{Message: "Expected close tag, found EOF", Pos: s.pos}
356-
} else if ch == '-' && s.peekN(2) == "%>" {
357-
s.readN(2)
358-
trimRight = true
359-
break
360-
} else if ch == '%' && s.peek() == '>' {
361-
s.read()
362-
break
333+
return "", &SyntaxError{Message: "Expected close tag, found EOF", Pos: s.pos}
334+
} else if ch == '%' {
335+
ch := s.read()
336+
if ch == eof {
337+
return "", &SyntaxError{Message: "Expected close tag, found EOF", Pos: s.pos}
338+
} else if ch == '>' {
339+
break
340+
} else {
341+
buf.WriteRune('%')
342+
buf.WriteRune(ch)
343+
}
363344
} else {
364345
buf.WriteRune(ch)
365346
}
366347
}
367-
return buf.String(), trimRight, nil
348+
return string(buf.Bytes()), nil
368349
}
369350

370351
func (s *Scanner) scanField() (*Field, error) {
@@ -615,6 +596,7 @@ func (s *Scanner) skipWhitespace() {
615596
for ch := s.peek(); isWhitespace(ch); ch = s.peek() {
616597
s.read()
617598
}
599+
return
618600
}
619601

620602
const eof = rune(0)

scanner_test.go

-39
Original file line numberDiff line numberDiff line change
@@ -62,45 +62,6 @@ func TestScanner(t *testing.T) {
6262
}
6363
})
6464

65-
t.Run("TrimLeft", func(t *testing.T) {
66-
s := ego.NewScanner(bytes.NewBufferString(`<%- x := 1 %>`), "tmpl.ego")
67-
if blk, err := s.Scan(); err != nil {
68-
t.Fatal(err)
69-
} else if blk, ok := blk.(*ego.CodeBlock); !ok {
70-
t.Fatalf("unexpected block type: %T", blk)
71-
} else if blk.Content != " x := 1 " {
72-
t.Fatalf("unexpected content: %s", blk.Content)
73-
} else if !blk.TrimLeft || blk.TrimRight {
74-
t.Fatal("expected TrimLeft only")
75-
}
76-
})
77-
78-
t.Run("TrimRight", func(t *testing.T) {
79-
s := ego.NewScanner(bytes.NewBufferString(`<% x := 1 -%>`), "tmpl.ego")
80-
if blk, err := s.Scan(); err != nil {
81-
t.Fatal(err)
82-
} else if blk, ok := blk.(*ego.CodeBlock); !ok {
83-
t.Fatalf("unexpected block type: %T", blk)
84-
} else if blk.Content != " x := 1 " {
85-
t.Fatalf("unexpected content: %s", blk.Content)
86-
} else if blk.TrimLeft || !blk.TrimRight {
87-
t.Fatal("expected TrimRight only")
88-
}
89-
})
90-
91-
t.Run("Trim", func(t *testing.T) {
92-
s := ego.NewScanner(bytes.NewBufferString(`<%- x := 1 -%>`), "tmpl.ego")
93-
if blk, err := s.Scan(); err != nil {
94-
t.Fatal(err)
95-
} else if blk, ok := blk.(*ego.CodeBlock); !ok {
96-
t.Fatalf("unexpected block type: %T", blk)
97-
} else if blk.Content != " x := 1 " {
98-
t.Fatalf("unexpected content: %s", blk.Content)
99-
} else if !blk.TrimLeft || !blk.TrimRight {
100-
t.Fatal("expected TrimLeft and TrimRight")
101-
}
102-
})
103-
10465
t.Run("UnexpectedEOF/1", func(t *testing.T) {
10566
s := ego.NewScanner(bytes.NewBufferString(`<%`), "tmpl.ego")
10667
if _, err := s.Scan(); err == nil || err.Error() != `Expected close tag, found EOF at tmpl.ego:1` {

0 commit comments

Comments
 (0)