w_syntax_tree-erb 0.11.0 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
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