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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 459993e3b6d5a09ea5a902d6038acac34eeaddb7fa878cbd41a40039712c2d9a
4
- data.tar.gz: 48843275b9b5ab46a23d61677c9e7b8cd93e7e4f5095376d128d9f525408cae9
3
+ metadata.gz: b83deb85beac0716fe402b7c52cb54b9b097343cb22d7c533690fe98ecbc2d82
4
+ data.tar.gz: 5b0d14ae4c9da04a0f478d4b1676cd8405fc3a608cbf1602d7ef98d28badb7e6
5
5
  SHA512:
6
- metadata.gz: 373abd1743f157914a3394f127a9e6b7d5c15fce3179894f74de072a220e5a6458fb2d09a25f4b7a2b5764c0f81f870045cba6c6f6756931be8fdbbb23e260eb
7
- data.tar.gz: 38d635a29da6044441e6304fd488e8281b6a1ffa0718cea508541c242a9fc9d53d618956185ed1cf8cbf16efa357368ee99c5fb8d6d99a00e212e23eb92ac8a5
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.10.5...HEAD
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.11.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.20.0)
12
+ minitest (5.22.3)
13
13
  prettier_print (1.2.1)
14
- rake (13.1.0)
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.10", require: false
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
- From code:
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 = node.child_nodes.sort_by(&:location)
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
- output_rows(rows)
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
- result = ErbContent.new(value: [keyword, *content])
374
- @keyword = nil
375
- result
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.map { |token| token.is_a?(Token) ? token.value : token }.join
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(ParseError, "Only one doctype element is allowed")
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 :blank_line, $&, index, line
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 :new_line, $&, index, line
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 :html_comment, $&, index, line
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 :doctype, $&, index, line
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 :erb_comment, $&, index, line
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 :erb_open, $&, index, line
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 :slash_open, $&, index, line
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 :open, $&, index, line
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 :whitespace, $&, index, line
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 :text, $&, index, line
129
+ enum.yield(:text, $&, index, line, column_index)
125
130
  else
126
- raise ParseError,
127
- "Unexpected character at #{index}: #{source[index]}"
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 :erb_if, $&, index, line
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 :erb_unless, $&, index, line
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 :erb_elsif, $&, index, line
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 :erb_else, $&, index, line
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 :erb_case, $&, index, line
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 :erb_when, $&, index, line
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 :erb_end, $&, index, line
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 :erb_code, $&, index, line
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 :erb_do_close, $&, index, line
184
+ enum.yield(:erb_do_close, $&, index, line, column_index)
175
185
  state.pop
176
186
  when /\A-?%>/
177
- enum.yield :erb_close, $&, index, line
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 :erb_code, $&, index, line
194
+ enum.yield(:erb_code, $&, index, line, column_index)
183
195
  else
184
- enum.yield :erb_code, source[index], index, line
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
- # whitespace
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 :string_close_single_quote, $&, index, line
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 :erb_open, $&, index, line
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 :text, $&, index, line
224
+ enum.yield(:text, $&, index, line, column_index)
207
225
  else
208
- raise ParseError,
209
- "Unexpected character in string at #{index}: #{source[index]}"
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
- # whitespace
215
- enum.yield :whitespace, $&, index, line
237
+ enum.yield(:whitespace, $&, index, line, column_index)
216
238
  line += $&.count("\n")
217
239
  when /\A\"/
218
- # the end of a quoted string
219
- enum.yield :string_close_double_quote, $&, index, line
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 :erb_open, $&, index, line
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 :text, $&, index, line
256
+ enum.yield(:text, $&, index, line, column_index)
230
257
  else
231
- raise ParseError,
232
- "Unexpected character in string at #{index}: #{source[index]}"
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 :erb_close, $&, index, line
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 :close, $&, index, line
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 :special_close, $&, index, line
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 :slash_close, $&, index, line
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 :string_open_double_quote, $&, index, line
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 :string_open_single_quote, $&, index, line
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 ParseError,
286
- "Unexpected character at #{index}: #{source[index]}"
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 :EOF, nil, index, line
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 MissingTokenError, "expected #{expected} got #{type}"
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
- raise MissingTokenError if result.nil?
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 ParseError, "Invalid html-tag name #{name}"
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
- "Missing closing tag for <#{opening.name.value}> at #{opening.location}"
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
- "Expected closing tag for <#{opening.name.value}> but got <#{closing.name.value}> at #{closing.location}"
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
- "Found no matching erb-tag to the if-tag at #{erb_node.location}"
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
- "Found no matching when- or else-tag to the case-tag at #{erb_node.location}"
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
- "Found no matching erb-tag to the if-tag at #{erb_node.location}"
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
- "Found no matching elsif- or else-tag to the if-tag at #{erb_node.location}"
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
- "Found no matching end-tag for the else-tag at #{erb_node.location}"
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
- "Found no matching closing tag for the erb-tag at #{opening_tag.location}"
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
- "Found no matching end-tag for the do-tag at #{erb_node.location}"
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) } ||
@@ -143,6 +143,10 @@ module SyntaxTree
143
143
  visit_node("erb_do_close", node)
144
144
  end
145
145
 
146
+ def visit_erb_yield(node)
147
+ visit_node("erb_yield", node)
148
+ end
149
+
146
150
  # Visit a Doctype node.
147
151
  def visit_doctype(node)
148
152
  visit_node("doctype", node)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module SyntaxTree
4
4
  module ERB
5
- VERSION = "0.11.0"
5
+ VERSION = "0.12.0"
6
6
  end
7
7
  end
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.11.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-04-23 00:00:00.000000000 Z
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.4.19
162
+ rubygems_version: 3.5.9
162
163
  signing_key:
163
164
  specification_version: 4
164
165
  summary: Syntax Tree support for ERB