w_syntax_tree-erb 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.tool-versions +1 -0
- data/CHANGELOG.md +9 -1
- data/Gemfile.lock +3 -3
- data/README.md +11 -3
- data/lib/syntax_tree/erb/format.rb +13 -3
- data/lib/syntax_tree/erb/nodes.rb +49 -46
- data/lib/syntax_tree/erb/parser.rb +184 -91
- data/lib/syntax_tree/erb/pretty_print.rb +4 -0
- data/lib/syntax_tree/erb/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b83deb85beac0716fe402b7c52cb54b9b097343cb22d7c533690fe98ecbc2d82
|
4
|
+
data.tar.gz: 5b0d14ae4c9da04a0f478d4b1676cd8405fc3a608cbf1602d7ef98d28badb7e6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6fcd8ed3ced75d085b893878d540e99936ad7d9a4a6747949e8f5c2cb51068a78a5f66a02ab477d8a2467c62c5455b121491a3f078aec913c3addabd983c52f1
|
7
|
+
data.tar.gz: 37449987cd5f235d02709bfc65ac07b65234df303df850b36378d00a347efb1f87915aa48ca7e6562a90c74ea10bca3578c78ef7832f2df9319783b4f7df6c8c
|
data/.tool-versions
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby 3.3.1
|
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## [0.12.0] - 2024-05-07
|
10
|
+
|
11
|
+
- Support Ruby 3.3 by handling yield in ERB specifically
|
12
|
+
- Use SyntaxTree own class for ParseError to get better error feedback
|
13
|
+
- Adds handling for keeping track of column index, to support better error messages
|
14
|
+
|
9
15
|
## [0.11.0] - 2024-04-23
|
10
16
|
|
11
17
|
- ErbContent now has its value as child_nodes instead of empty array.
|
@@ -114,7 +120,9 @@ Output:
|
|
114
120
|
- Can format a lot of .html.erb-syntax and works as a plugin to syntax_tree.
|
115
121
|
- This is still early and there are a lot of different weird syntaxes out there.
|
116
122
|
|
117
|
-
[unreleased]: https://github.com/davidwessman/syntax_tree-erb/compare/v0.
|
123
|
+
[unreleased]: https://github.com/davidwessman/syntax_tree-erb/compare/v0.12.0...HEAD
|
124
|
+
[0.12.0]: https://github.com/davidwessman/syntax_tree-erb/compare/v0.11.0...v0.12.0
|
125
|
+
[0.11.0]: https://github.com/davidwessman/syntax_tree-erb/compare/v0.10.5...v0.11.0
|
118
126
|
[0.10.5]: https://github.com/davidwessman/syntax_tree-erb/compare/v0.10.4...v0.10.5
|
119
127
|
[0.10.4]: https://github.com/davidwessman/syntax_tree-erb/compare/v0.10.3...v0.10.4
|
120
128
|
[0.10.3]: https://github.com/davidwessman/syntax_tree-erb/compare/v0.10.2...v0.10.3
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
w_syntax_tree-erb (0.
|
4
|
+
w_syntax_tree-erb (0.12.0)
|
5
5
|
prettier_print (~> 1.2, >= 1.2.0)
|
6
6
|
syntax_tree (~> 6.1, >= 6.1.1)
|
7
7
|
|
@@ -9,9 +9,9 @@ GEM
|
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
11
|
docile (1.4.0)
|
12
|
-
minitest (5.
|
12
|
+
minitest (5.22.3)
|
13
13
|
prettier_print (1.2.1)
|
14
|
-
rake (13.1
|
14
|
+
rake (13.2.1)
|
15
15
|
simplecov (0.22.0)
|
16
16
|
docile (~> 1.1)
|
17
17
|
simplecov-html (~> 0.11)
|
data/README.md
CHANGED
@@ -30,18 +30,26 @@ Currently handles
|
|
30
30
|
Add this line to your application's Gemfile:
|
31
31
|
|
32
32
|
```ruby
|
33
|
-
gem "w_syntax_tree-erb", "~> 0.
|
33
|
+
gem "w_syntax_tree-erb", "~> 0.11", require: false
|
34
34
|
```
|
35
35
|
|
36
36
|
> I added the `w_` prefix to avoid conflicts if there will ever be an official `syntax_tree-erb` gem.
|
37
37
|
|
38
38
|
## Usage
|
39
39
|
|
40
|
+
### Parsing
|
41
|
+
|
42
|
+
```sh
|
43
|
+
bundle exec stree ast --plugins=erb "./**/*.html.erb"
|
44
|
+
```
|
45
|
+
|
46
|
+
### Format
|
47
|
+
|
40
48
|
```sh
|
41
|
-
bundle exec stree --plugins=erb "./**/*.html.erb"
|
49
|
+
bundle exec stree write --plugins=erb "./**/*.html.erb"
|
42
50
|
```
|
43
51
|
|
44
|
-
|
52
|
+
### In code
|
45
53
|
|
46
54
|
```ruby
|
47
55
|
require "syntax_tree/erb"
|
@@ -20,7 +20,8 @@ module SyntaxTree
|
|
20
20
|
|
21
21
|
# Visit a Document node.
|
22
22
|
def visit_document(node)
|
23
|
-
child_nodes =
|
23
|
+
child_nodes =
|
24
|
+
node.child_nodes.sort_by { |node| node.location.start_char }
|
24
25
|
|
25
26
|
handle_child_nodes(child_nodes)
|
26
27
|
|
@@ -131,6 +132,10 @@ module SyntaxTree
|
|
131
132
|
visit(node.closing)
|
132
133
|
end
|
133
134
|
|
135
|
+
def visit_erb_yield(node)
|
136
|
+
q.text("yield")
|
137
|
+
end
|
138
|
+
|
134
139
|
# Visit an ErbEnd node.
|
135
140
|
def visit_erb_end(node)
|
136
141
|
visit(node.opening_tag)
|
@@ -169,9 +174,14 @@ module SyntaxTree
|
|
169
174
|
|
170
175
|
formatter.format(statement)
|
171
176
|
formatter.flush
|
172
|
-
rows = formatter.output.join.split("\n")
|
173
177
|
|
174
|
-
|
178
|
+
formatted =
|
179
|
+
formatter.output.join.gsub(
|
180
|
+
SyntaxTree::ERB::ErbYield::PLACEHOLDER,
|
181
|
+
"yield"
|
182
|
+
)
|
183
|
+
|
184
|
+
output_rows(formatted.split("\n"))
|
175
185
|
end
|
176
186
|
|
177
187
|
def output_rows(rows)
|
@@ -2,48 +2,6 @@
|
|
2
2
|
|
3
3
|
module SyntaxTree
|
4
4
|
module ERB
|
5
|
-
# A Location represents a position for a node in the source file.
|
6
|
-
class Location
|
7
|
-
attr_reader :start_char, :end_char, :start_line, :end_line
|
8
|
-
|
9
|
-
def initialize(start_char:, end_char:, start_line:, end_line:)
|
10
|
-
@start_char = start_char
|
11
|
-
@end_char = end_char
|
12
|
-
@start_line = start_line
|
13
|
-
@end_line = end_line
|
14
|
-
end
|
15
|
-
|
16
|
-
def deconstruct_keys(keys)
|
17
|
-
{
|
18
|
-
start_char: start_char,
|
19
|
-
end_char: end_char,
|
20
|
-
start_line: start_line,
|
21
|
-
end_line: end_line
|
22
|
-
}
|
23
|
-
end
|
24
|
-
|
25
|
-
def to(other)
|
26
|
-
Location.new(
|
27
|
-
start_char: start_char,
|
28
|
-
start_line: start_line,
|
29
|
-
end_char: other.end_char,
|
30
|
-
end_line: other.end_line
|
31
|
-
)
|
32
|
-
end
|
33
|
-
|
34
|
-
def <=>(other)
|
35
|
-
start_char <=> other.start_char
|
36
|
-
end
|
37
|
-
|
38
|
-
def to_s
|
39
|
-
if start_line == end_line
|
40
|
-
"line #{start_line}, char #{start_char}..#{end_char}"
|
41
|
-
else
|
42
|
-
"line #{start_line},char #{start_char} to line #{end_line}, char #{end_char}"
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
5
|
# A parent node that contains a bit of shared functionality.
|
48
6
|
class Node
|
49
7
|
def format(q)
|
@@ -322,6 +280,7 @@ module SyntaxTree
|
|
322
280
|
)
|
323
281
|
end
|
324
282
|
|
283
|
+
@content = content
|
325
284
|
@content = prepare_content(content)
|
326
285
|
@closing_tag = closing_tag
|
327
286
|
end
|
@@ -370,9 +329,28 @@ module SyntaxTree
|
|
370
329
|
end
|
371
330
|
rescue SyntaxTree::Parser::ParseError
|
372
331
|
# Try to add the keyword to see if it parses
|
373
|
-
|
374
|
-
|
375
|
-
|
332
|
+
begin
|
333
|
+
result = ErbContent.new(value: [keyword, *content])
|
334
|
+
@keyword = nil
|
335
|
+
|
336
|
+
result
|
337
|
+
rescue SyntaxTree::Parser::ParseError => error
|
338
|
+
opening_location = opening_tag.location
|
339
|
+
content_location = content.first&.location || opening_location
|
340
|
+
raise(
|
341
|
+
SyntaxTree::Parser::ParseError.new(
|
342
|
+
"Could not parse ERB-tag: #{error.message}",
|
343
|
+
@opening_tag.location.start_line + error.lineno - 1,
|
344
|
+
(
|
345
|
+
if opening_location.start_line == error.lineno
|
346
|
+
opening_location.start_column + error.column - 1
|
347
|
+
else
|
348
|
+
content_location.start_column + error.column - 1
|
349
|
+
end
|
350
|
+
)
|
351
|
+
)
|
352
|
+
)
|
353
|
+
end
|
376
354
|
end
|
377
355
|
end
|
378
356
|
|
@@ -489,7 +467,17 @@ module SyntaxTree
|
|
489
467
|
def initialize(value:)
|
490
468
|
if value.is_a?(Array)
|
491
469
|
value =
|
492
|
-
value
|
470
|
+
value
|
471
|
+
.map do |token|
|
472
|
+
if token.is_a?(Token)
|
473
|
+
token.value
|
474
|
+
elsif token.is_a?(ErbYield)
|
475
|
+
ErbYield::PLACEHOLDER
|
476
|
+
else
|
477
|
+
token
|
478
|
+
end
|
479
|
+
end
|
480
|
+
.join
|
493
481
|
end
|
494
482
|
@value = SyntaxTree.parse(value.strip)
|
495
483
|
end
|
@@ -518,6 +506,21 @@ module SyntaxTree
|
|
518
506
|
end
|
519
507
|
end
|
520
508
|
|
509
|
+
class ErbYield < Element
|
510
|
+
PLACEHOLDER = "qqqqy"
|
511
|
+
def initialize(new_line:, location:)
|
512
|
+
super(new_line: new_line, location: location)
|
513
|
+
end
|
514
|
+
|
515
|
+
def accept(visitor)
|
516
|
+
visitor.visit_erb_yield(self)
|
517
|
+
end
|
518
|
+
|
519
|
+
def child_nodes
|
520
|
+
[]
|
521
|
+
end
|
522
|
+
end
|
523
|
+
|
521
524
|
# An HtmlAttribute is a key-value pair within a tag. It contains the key, the
|
522
525
|
# equals sign, and the value.
|
523
526
|
class HtmlAttribute < Node
|
@@ -5,14 +5,12 @@ module SyntaxTree
|
|
5
5
|
class Parser
|
6
6
|
# This is the parent class of any kind of errors that will be raised by
|
7
7
|
# the parser.
|
8
|
-
class ParseError < StandardError
|
9
|
-
end
|
10
8
|
|
11
9
|
# This error occurs when a certain token is expected in a certain place
|
12
10
|
# but is not found. Sometimes this is handled internally because some
|
13
11
|
# elements are optional. Other times it is not and it is raised to end the
|
14
12
|
# parsing process.
|
15
|
-
class MissingTokenError < ParseError
|
13
|
+
class MissingTokenError < SyntaxTree::Parser::ParseError
|
16
14
|
end
|
17
15
|
|
18
16
|
attr_reader :source, :tokens
|
@@ -52,7 +50,13 @@ module SyntaxTree
|
|
52
50
|
|
53
51
|
if tag.is_a?(Doctype)
|
54
52
|
if @found_doctype
|
55
|
-
raise(
|
53
|
+
raise(
|
54
|
+
SyntaxTree::Parser::ParseError.new(
|
55
|
+
"Duplicate doctype declaration",
|
56
|
+
tag.location.start_line,
|
57
|
+
tag.location.start_column
|
58
|
+
)
|
59
|
+
)
|
56
60
|
else
|
57
61
|
@found_doctype = true
|
58
62
|
end
|
@@ -69,6 +73,7 @@ module SyntaxTree
|
|
69
73
|
def make_tokens
|
70
74
|
Enumerator.new do |enum|
|
71
75
|
index = 0
|
76
|
+
column_index = 0
|
72
77
|
line = 1
|
73
78
|
state = %i[outside]
|
74
79
|
|
@@ -78,83 +83,88 @@ module SyntaxTree
|
|
78
83
|
case source[index..]
|
79
84
|
when /\A\n{2,}/
|
80
85
|
# two or more newlines should be ONE blank line
|
81
|
-
enum.yield
|
86
|
+
enum.yield(:blank_line, $&, index, line, column_index)
|
82
87
|
line += $&.count("\n")
|
83
88
|
when /\A\n/
|
84
89
|
# newlines
|
85
|
-
enum.yield
|
90
|
+
enum.yield(:new_line, $&, index, line, column_index)
|
86
91
|
line += 1
|
87
92
|
when /\A<!--(.|\r?\n)*?-->/m
|
88
93
|
# comments
|
89
94
|
# <!-- this is a comment -->
|
90
|
-
enum.yield
|
95
|
+
enum.yield(:html_comment, $&, index, line, column_index)
|
91
96
|
line += $&.count("\n")
|
92
97
|
when /\A<!DOCTYPE/, /\A<!doctype/
|
93
98
|
# document type tags
|
94
99
|
# <!DOCTYPE
|
95
|
-
enum.yield
|
100
|
+
enum.yield(:doctype, $&, index, line, column_index)
|
96
101
|
state << :inside
|
97
102
|
when /\A<%#[\s\S]*?%>/
|
98
103
|
# An ERB-comment
|
99
104
|
# <%# this is an ERB comment %>
|
100
|
-
enum.yield
|
105
|
+
enum.yield(:erb_comment, $&, index, line, column_index)
|
101
106
|
when /\A<%={1,2}/, /\A<%-/, /\A<%/
|
102
107
|
# the beginning of an ERB tag
|
103
108
|
# <%
|
104
109
|
# <%=, <%==
|
105
|
-
enum.yield
|
110
|
+
enum.yield(:erb_open, $&, index, line, column_index)
|
106
111
|
state << :erb_start
|
107
112
|
line += $&.count("\n")
|
108
113
|
when %r{\A</}
|
109
114
|
# the beginning of a closing tag
|
110
115
|
# </
|
111
|
-
enum.yield
|
116
|
+
enum.yield(:slash_open, $&, index, line, column_index)
|
112
117
|
state << :inside
|
113
118
|
when /\A</
|
114
119
|
# the beginning of an opening tag
|
115
120
|
# <
|
116
|
-
enum.yield
|
121
|
+
enum.yield(:open, $&, index, line, column_index)
|
117
122
|
state << :inside
|
118
123
|
when /\A(?: |\t|\r)+/m
|
119
124
|
# whitespace
|
120
|
-
enum.yield
|
125
|
+
enum.yield(:whitespace, $&, index, line, column_index)
|
121
126
|
when /\A(?!\s+$)[^<\n]+/
|
122
127
|
# plain text content, but do not allow only white space
|
123
128
|
# abc
|
124
|
-
enum.yield
|
129
|
+
enum.yield(:text, $&, index, line, column_index)
|
125
130
|
else
|
126
|
-
raise
|
127
|
-
|
131
|
+
raise(
|
132
|
+
SyntaxTree::Parser::ParseError.new(
|
133
|
+
"Unexpected character: #{source[index]}",
|
134
|
+
line,
|
135
|
+
column_index
|
136
|
+
)
|
137
|
+
)
|
128
138
|
end
|
129
139
|
in :erb_start
|
130
140
|
case source[index..]
|
131
141
|
when /\A\s*if/
|
132
142
|
# if statement
|
133
|
-
enum.yield
|
143
|
+
enum.yield(:erb_if, $&, index, line, column_index)
|
134
144
|
state.pop
|
135
145
|
state << :erb
|
136
146
|
when /\A\s*unless/
|
137
|
-
enum.yield
|
147
|
+
enum.yield(:erb_unless, $&, index, line, column_index)
|
138
148
|
state.pop
|
139
149
|
state << :erb
|
140
150
|
when /\A\s*elsif/
|
141
|
-
enum.yield
|
151
|
+
enum.yield(:erb_elsif, $&, index, line, column_index)
|
142
152
|
state.pop
|
143
153
|
state << :erb
|
144
154
|
when /\A\s*else/
|
145
|
-
enum.yield
|
155
|
+
enum.yield(:erb_else, $&, index, line, column_index)
|
146
156
|
state.pop
|
147
157
|
state << :erb
|
148
158
|
when /\A\s*case/
|
149
|
-
enum.yield
|
159
|
+
enum.yield(:erb_case, $&, index, line, column_index)
|
150
160
|
state.pop
|
151
161
|
state << :erb
|
152
162
|
when /\A\s*when/
|
153
|
-
enum.yield
|
163
|
+
enum.yield(:erb_when, $&, index, line, column_index)
|
154
164
|
state.pop
|
155
165
|
state << :erb
|
156
166
|
when /\A\s*end/
|
157
|
-
enum.yield
|
167
|
+
enum.yield(:erb_end, $&, index, line, column_index)
|
158
168
|
state.pop
|
159
169
|
state << :erb
|
160
170
|
else
|
@@ -168,68 +178,90 @@ module SyntaxTree
|
|
168
178
|
case source[index..]
|
169
179
|
when /\A[\n]+/
|
170
180
|
# newlines
|
171
|
-
enum.yield
|
181
|
+
enum.yield(:erb_code, $&, index, line, column_index)
|
172
182
|
line += $&.count("\n")
|
173
183
|
when /\Ado\b(\s*\|[\w\s,]+\|)?\s*-?%>/
|
174
|
-
enum.yield
|
184
|
+
enum.yield(:erb_do_close, $&, index, line, column_index)
|
175
185
|
state.pop
|
176
186
|
when /\A-?%>/
|
177
|
-
enum.yield
|
187
|
+
enum.yield(:erb_close, $&, index, line, column_index)
|
178
188
|
state.pop
|
189
|
+
when /\Ayield\b/
|
190
|
+
enum.yield(:erb_yield, $&, index, line, column_index)
|
179
191
|
when /\A[\p{L}\w]*\b/
|
180
192
|
# Split by word boundary while parsing the code
|
181
193
|
# This allows us to separate what_to_do vs do
|
182
|
-
enum.yield
|
194
|
+
enum.yield(:erb_code, $&, index, line, column_index)
|
183
195
|
else
|
184
|
-
enum.yield
|
196
|
+
enum.yield(:erb_code, source[index], index, line, column_index)
|
185
197
|
index += 1
|
198
|
+
column_index += 1
|
186
199
|
next
|
187
200
|
end
|
188
201
|
in :string_single_quote
|
189
202
|
case source[index..]
|
190
203
|
when /\A(?: |\t|\n|\r\n)+/m
|
191
|
-
|
192
|
-
enum.yield :whitespace, $&, index, line
|
204
|
+
enum.yield(:whitespace, $&, index, line, column_index)
|
193
205
|
line += $&.count("\n")
|
194
206
|
when /\A\'/
|
195
207
|
# the end of a quoted string
|
196
|
-
enum.yield
|
208
|
+
enum.yield(
|
209
|
+
:string_close_single_quote,
|
210
|
+
$&,
|
211
|
+
index,
|
212
|
+
line,
|
213
|
+
column_index
|
214
|
+
)
|
197
215
|
state.pop
|
198
216
|
when /\A<%[=]?/
|
199
217
|
# the beginning of an ERB tag
|
200
218
|
# <%
|
201
|
-
enum.yield
|
219
|
+
enum.yield(:erb_open, $&, index, line, column_index)
|
202
220
|
state << :erb_start
|
203
221
|
when /\A[^<']+/
|
204
222
|
# plain text content
|
205
223
|
# abc
|
206
|
-
enum.yield
|
224
|
+
enum.yield(:text, $&, index, line, column_index)
|
207
225
|
else
|
208
|
-
raise
|
209
|
-
|
226
|
+
raise(
|
227
|
+
SyntaxTree::Parser::ParseError.new(
|
228
|
+
"Unexpected character, #{source[index]}, when looking for closing single quote",
|
229
|
+
line,
|
230
|
+
column_index
|
231
|
+
)
|
232
|
+
)
|
210
233
|
end
|
211
234
|
in :string_double_quote
|
212
235
|
case source[index..]
|
213
236
|
when /\A(?: |\t|\n|\r\n)+/m
|
214
|
-
|
215
|
-
enum.yield :whitespace, $&, index, line
|
237
|
+
enum.yield(:whitespace, $&, index, line, column_index)
|
216
238
|
line += $&.count("\n")
|
217
239
|
when /\A\"/
|
218
|
-
|
219
|
-
|
240
|
+
enum.yield(
|
241
|
+
:string_close_double_quote,
|
242
|
+
$&,
|
243
|
+
index,
|
244
|
+
line,
|
245
|
+
column_index
|
246
|
+
)
|
220
247
|
state.pop
|
221
248
|
when /\A<%[=]?/
|
222
249
|
# the beginning of an ERB tag
|
223
250
|
# <%
|
224
|
-
enum.yield
|
251
|
+
enum.yield(:erb_open, $&, index, line, column_index)
|
225
252
|
state << :erb_start
|
226
253
|
when /\A[^<"]+/
|
227
254
|
# plain text content
|
228
255
|
# abc
|
229
|
-
enum.yield
|
256
|
+
enum.yield(:text, $&, index, line, column_index)
|
230
257
|
else
|
231
|
-
raise
|
232
|
-
|
258
|
+
raise(
|
259
|
+
SyntaxTree::Parser::ParseError.new(
|
260
|
+
"Unexpected character, #{source[index]}, when looking for closing double quote",
|
261
|
+
line,
|
262
|
+
column_index
|
263
|
+
)
|
264
|
+
)
|
233
265
|
end
|
234
266
|
in :inside
|
235
267
|
case source[index..]
|
@@ -239,58 +271,76 @@ module SyntaxTree
|
|
239
271
|
when /\A-?%>/
|
240
272
|
# the end of an ERB tag
|
241
273
|
# -%> or %>
|
242
|
-
enum.yield
|
274
|
+
enum.yield(:erb_close, $&, index, line, column_index)
|
243
275
|
state.pop
|
244
276
|
when /\A>/
|
245
277
|
# the end of a tag
|
246
278
|
# >
|
247
|
-
enum.yield
|
279
|
+
enum.yield(:close, $&, index, line, column_index)
|
248
280
|
state.pop
|
249
281
|
when /\A\?>/
|
250
282
|
# the end of a tag
|
251
283
|
# ?>
|
252
|
-
enum.yield
|
284
|
+
enum.yield(:special_close, $&, index, line, column_index)
|
253
285
|
state.pop
|
254
286
|
when %r{\A/>}
|
255
287
|
# the end of a self-closing tag
|
256
|
-
enum.yield
|
288
|
+
enum.yield(:slash_close, $&, index, line, column_index)
|
257
289
|
state.pop
|
258
290
|
when %r{\A/}
|
259
291
|
# a forward slash
|
260
292
|
# /
|
261
|
-
enum.yield :slash, $&, index, line
|
293
|
+
enum.yield :slash, $&, index, line, column_index
|
262
294
|
when /\A=/
|
263
295
|
# an equals sign
|
264
296
|
# =
|
265
|
-
enum.yield :equals, $&, index, line
|
297
|
+
enum.yield :equals, $&, index, line, column_index
|
266
298
|
when /\A[@#]*[:\w\.\-\_]+\b/
|
267
299
|
# a name for an element or an attribute
|
268
300
|
# strong, vue-component-kebab, VueComponentPascal
|
269
301
|
# abc, #abc, @abc, :abc
|
270
|
-
enum.yield :name, $&, index, line
|
302
|
+
enum.yield :name, $&, index, line, column_index
|
271
303
|
when /\A<%/
|
272
304
|
# the beginning of an ERB tag
|
273
305
|
# <%
|
274
|
-
enum.yield :erb_open, $&, index, line
|
306
|
+
enum.yield :erb_open, $&, index, line, column_index
|
275
307
|
state << :erb_start
|
276
308
|
when /\A"/
|
277
309
|
# the beginning of a string
|
278
|
-
enum.yield
|
310
|
+
enum.yield(
|
311
|
+
:string_open_double_quote,
|
312
|
+
$&,
|
313
|
+
index,
|
314
|
+
line,
|
315
|
+
column_index
|
316
|
+
)
|
279
317
|
state << :string_double_quote
|
280
318
|
when /\A'/
|
281
319
|
# the beginning of a string
|
282
|
-
enum.yield
|
320
|
+
enum.yield(
|
321
|
+
:string_open_single_quote,
|
322
|
+
$&,
|
323
|
+
index,
|
324
|
+
line,
|
325
|
+
column_index
|
326
|
+
)
|
283
327
|
state << :string_single_quote
|
284
328
|
else
|
285
|
-
raise
|
286
|
-
|
329
|
+
raise(
|
330
|
+
SyntaxTree::Parser::ParseError.new(
|
331
|
+
"Unexpected character, #{source[index]}, when parsing HTML- or ERB-tag",
|
332
|
+
line,
|
333
|
+
column_index
|
334
|
+
)
|
335
|
+
)
|
287
336
|
end
|
288
337
|
end
|
289
338
|
|
290
339
|
index += $&.length
|
340
|
+
column_index = $&.rindex("\n") || column_index + $&.length
|
291
341
|
end
|
292
342
|
|
293
|
-
enum.yield
|
343
|
+
enum.yield(:EOF, nil, index, line, column_index)
|
294
344
|
end
|
295
345
|
end
|
296
346
|
|
@@ -299,14 +349,22 @@ module SyntaxTree
|
|
299
349
|
# return the new Token. Otherwise we're going to raise a
|
300
350
|
# MissingTokenError.
|
301
351
|
def consume(expected)
|
302
|
-
type, value, index, line = tokens.peek
|
352
|
+
type, value, index, line, column = tokens.peek
|
303
353
|
|
304
354
|
if expected != type
|
305
|
-
raise
|
355
|
+
raise(
|
356
|
+
MissingTokenError.new(
|
357
|
+
"expected #{expected} got #{type}",
|
358
|
+
line,
|
359
|
+
index
|
360
|
+
)
|
361
|
+
)
|
306
362
|
end
|
307
363
|
|
308
364
|
tokens.next
|
309
365
|
|
366
|
+
rindex = value.rindex("\n")
|
367
|
+
|
310
368
|
Token.new(
|
311
369
|
type: type,
|
312
370
|
value: value,
|
@@ -315,7 +373,9 @@ module SyntaxTree
|
|
315
373
|
start_char: index,
|
316
374
|
end_char: index + value.length,
|
317
375
|
start_line: line,
|
318
|
-
end_line: line + value.count("\n")
|
376
|
+
end_line: line + value.count("\n"),
|
377
|
+
start_column: column,
|
378
|
+
end_column: rindex ? value.length - rindex : column + value.length
|
319
379
|
)
|
320
380
|
)
|
321
381
|
end
|
@@ -333,7 +393,9 @@ module SyntaxTree
|
|
333
393
|
# Otherwise we'll return the value returned by the block.
|
334
394
|
def atleast
|
335
395
|
result = yield
|
336
|
-
|
396
|
+
if result.nil?
|
397
|
+
raise(MissingTokenError.new("No matching token", nil, nil))
|
398
|
+
end
|
337
399
|
result
|
338
400
|
end
|
339
401
|
|
@@ -370,7 +432,13 @@ module SyntaxTree
|
|
370
432
|
name = consume(:name)
|
371
433
|
|
372
434
|
if name.value =~ /\A[@:#]/
|
373
|
-
raise
|
435
|
+
raise(
|
436
|
+
SyntaxTree::Parser::ParseError.new(
|
437
|
+
"Invalid HTML-tag name #{name.value}",
|
438
|
+
name.location.start_line,
|
439
|
+
name.location.start_column
|
440
|
+
)
|
441
|
+
)
|
374
442
|
end
|
375
443
|
|
376
444
|
attributes =
|
@@ -429,15 +497,21 @@ module SyntaxTree
|
|
429
497
|
|
430
498
|
if closing.nil?
|
431
499
|
raise(
|
432
|
-
ParseError
|
433
|
-
|
500
|
+
SyntaxTree::Parser::ParseError.new(
|
501
|
+
"Missing closing tag for <#{opening.name.value}>",
|
502
|
+
opening.location.start_line,
|
503
|
+
opening.location.start_column
|
504
|
+
)
|
434
505
|
)
|
435
506
|
end
|
436
507
|
|
437
508
|
if closing.name.value != opening.name.value
|
438
509
|
raise(
|
439
|
-
ParseError
|
440
|
-
|
510
|
+
SyntaxTree::Parser::ParseError.new(
|
511
|
+
"Expected closing tag for <#{opening.name.value}> but got <#{closing.name.value}>",
|
512
|
+
closing.location.start_line,
|
513
|
+
closing.location.start_column
|
514
|
+
)
|
441
515
|
)
|
442
516
|
end
|
443
517
|
|
@@ -459,9 +533,13 @@ module SyntaxTree
|
|
459
533
|
|
460
534
|
unless erb_tag.is_a?(ErbCaseWhen) || erb_tag.is_a?(ErbElse) ||
|
461
535
|
erb_tag.is_a?(ErbEnd)
|
536
|
+
location = erb_tag&.location || erb_node.location
|
462
537
|
raise(
|
463
|
-
ParseError
|
464
|
-
|
538
|
+
SyntaxTree::Parser::ParseError.new(
|
539
|
+
"No matching ERB-tag for the <% #{erb_node.keyword.value} %>",
|
540
|
+
location.start_line,
|
541
|
+
location.start_column
|
542
|
+
)
|
465
543
|
)
|
466
544
|
end
|
467
545
|
|
@@ -482,8 +560,11 @@ module SyntaxTree
|
|
482
560
|
)
|
483
561
|
else
|
484
562
|
raise(
|
485
|
-
ParseError
|
486
|
-
|
563
|
+
SyntaxTree::Parser::ParseError.new(
|
564
|
+
"No matching when- or else-tag for the case-tag",
|
565
|
+
erb_node.location.start_line,
|
566
|
+
erb_node.location.start_column
|
567
|
+
)
|
487
568
|
)
|
488
569
|
end
|
489
570
|
end
|
@@ -499,8 +580,11 @@ module SyntaxTree
|
|
499
580
|
|
500
581
|
unless erb_tag.is_a?(ErbControl) || erb_tag.is_a?(ErbEnd)
|
501
582
|
raise(
|
502
|
-
ParseError
|
503
|
-
|
583
|
+
SyntaxTree::Parser::ParseError.new(
|
584
|
+
"No matching ERB-tag for the <% if %>",
|
585
|
+
erb_node.location.start_line,
|
586
|
+
erb_node.location.start_column
|
587
|
+
)
|
504
588
|
)
|
505
589
|
end
|
506
590
|
|
@@ -528,8 +612,11 @@ module SyntaxTree
|
|
528
612
|
)
|
529
613
|
else
|
530
614
|
raise(
|
531
|
-
ParseError
|
532
|
-
|
615
|
+
SyntaxTree::Parser::ParseError.new(
|
616
|
+
"No matching <% elsif %> or <% else %> for the <% if %>",
|
617
|
+
erb_node.location.start_line,
|
618
|
+
erb_node.location.start_column
|
619
|
+
)
|
533
620
|
)
|
534
621
|
end
|
535
622
|
end
|
@@ -541,8 +628,11 @@ module SyntaxTree
|
|
541
628
|
|
542
629
|
unless erb_end.is_a?(ErbEnd)
|
543
630
|
raise(
|
544
|
-
ParseError
|
545
|
-
|
631
|
+
SyntaxTree::Parser::ParseError.new(
|
632
|
+
"No matching <% end %> for the <% else %>",
|
633
|
+
erb_node.location.start_line,
|
634
|
+
erb_node.location.start_column
|
635
|
+
)
|
546
636
|
)
|
547
637
|
end
|
548
638
|
|
@@ -580,8 +670,11 @@ module SyntaxTree
|
|
580
670
|
|
581
671
|
if !closing_tag.is_a?(ErbClose)
|
582
672
|
raise(
|
583
|
-
ParseError
|
584
|
-
|
673
|
+
SyntaxTree::Parser::ParseError.new(
|
674
|
+
"No matching closing tag for the <% #{keyword.value} %>",
|
675
|
+
closing_tag.location.start_line,
|
676
|
+
closing_tag.location.start_column
|
677
|
+
)
|
585
678
|
)
|
586
679
|
end
|
587
680
|
|
@@ -613,8 +706,11 @@ module SyntaxTree
|
|
613
706
|
|
614
707
|
unless erb_end.is_a?(ErbEnd)
|
615
708
|
raise(
|
616
|
-
ParseError
|
617
|
-
|
709
|
+
SyntaxTree::Parser::ParseError.new(
|
710
|
+
"No matching <% end %> for the <% do %>",
|
711
|
+
erb_node.location.start_line,
|
712
|
+
erb_node.location.start_column
|
713
|
+
)
|
618
714
|
)
|
619
715
|
end
|
620
716
|
|
@@ -628,17 +724,6 @@ module SyntaxTree
|
|
628
724
|
erb_node
|
629
725
|
end
|
630
726
|
end
|
631
|
-
rescue MissingTokenError => error
|
632
|
-
# If we have parsed tokens that we cannot process after we parsed <%, we should throw a ParseError
|
633
|
-
# and not let it be handled by a `maybe`.
|
634
|
-
if opening_tag
|
635
|
-
raise(
|
636
|
-
ParseError,
|
637
|
-
"Could not parse ERB-tag at #{opening_tag.location}"
|
638
|
-
)
|
639
|
-
else
|
640
|
-
raise(error)
|
641
|
-
end
|
642
727
|
end
|
643
728
|
|
644
729
|
def parse_until_erb_close
|
@@ -648,7 +733,7 @@ module SyntaxTree
|
|
648
733
|
result =
|
649
734
|
atleast do
|
650
735
|
maybe { parse_erb_do_close } || maybe { parse_erb_close } ||
|
651
|
-
maybe { consume(:erb_code) }
|
736
|
+
maybe { parse_erb_yield } || maybe { consume(:erb_code) }
|
652
737
|
end
|
653
738
|
|
654
739
|
items << result
|
@@ -701,6 +786,14 @@ module SyntaxTree
|
|
701
786
|
)
|
702
787
|
end
|
703
788
|
|
789
|
+
def parse_erb_yield
|
790
|
+
token = consume(:erb_yield)
|
791
|
+
|
792
|
+
new_line = maybe { parse_new_line }
|
793
|
+
|
794
|
+
ErbYield.new(location: token.location, new_line: new_line)
|
795
|
+
end
|
796
|
+
|
704
797
|
def parse_html_string
|
705
798
|
opening =
|
706
799
|
maybe { consume(:string_open_double_quote) } ||
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: w_syntax_tree-erb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.12.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Newton
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2024-
|
12
|
+
date: 2024-05-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: prettier_print
|
@@ -121,6 +121,7 @@ files:
|
|
121
121
|
- ".github/workflows/main.yml"
|
122
122
|
- ".gitignore"
|
123
123
|
- ".husky/pre-commit"
|
124
|
+
- ".tool-versions"
|
124
125
|
- ".vscode/launch.json"
|
125
126
|
- CHANGELOG.md
|
126
127
|
- Gemfile
|
@@ -158,7 +159,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
158
159
|
- !ruby/object:Gem::Version
|
159
160
|
version: '0'
|
160
161
|
requirements: []
|
161
|
-
rubygems_version: 3.
|
162
|
+
rubygems_version: 3.5.9
|
162
163
|
signing_key:
|
163
164
|
specification_version: 4
|
164
165
|
summary: Syntax Tree support for ERB
|