w_syntax_tree-erb 0.10.5 → 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: d75bcbb66fc50b3559fa73a7ecb906aaa208a16bf041ae95793aed008bdd60f6
4
- data.tar.gz: d2f882155d6e2ab774de0367586926c853a367c55123f7bc05edebebbba9fe6d
3
+ metadata.gz: b83deb85beac0716fe402b7c52cb54b9b097343cb22d7c533690fe98ecbc2d82
4
+ data.tar.gz: 5b0d14ae4c9da04a0f478d4b1676cd8405fc3a608cbf1602d7ef98d28badb7e6
5
5
  SHA512:
6
- metadata.gz: d7e9cc9b0c675244216ffd52db0751ee59b6ba79af00cb2b56ff955e351aa51c5a5d25d2737084283fa00707d51f4fb38fb7b6bbddf0c6bcc9f2831afd1fe9e1
7
- data.tar.gz: 2cd81b89e3ebdd1b1010f7c7509c9ef9a81eacf30d2285c34c36d3a97b7e86cb8d7d4f460ea3a7ba2ad1dd333b2e2e6a709056d328ff373b8692218701892d57
6
+ metadata.gz: 6fcd8ed3ced75d085b893878d540e99936ad7d9a4a6747949e8f5c2cb51068a78a5f66a02ab477d8a2467c62c5455b121491a3f078aec913c3addabd983c52f1
7
+ data.tar.gz: 37449987cd5f235d02709bfc65ac07b65234df303df850b36378d00a347efb1f87915aa48ca7e6562a90c74ea10bca3578c78ef7832f2df9319783b4f7df6c8c
@@ -16,6 +16,7 @@ jobs:
16
16
  - "3.0"
17
17
  - "3.1"
18
18
  - "3.2"
19
+ - "3.3"
19
20
  name: CI
20
21
  runs-on: ubuntu-latest
21
22
  env:
data/.tool-versions ADDED
@@ -0,0 +1 @@
1
+ ruby 3.3.1
data/CHANGELOG.md CHANGED
@@ -6,6 +6,17 @@ 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
+
15
+ ## [0.11.0] - 2024-04-23
16
+
17
+ - ErbContent now has its value as child_nodes instead of empty array.
18
+ - Allow html void tags and format self-closing tags
19
+
9
20
  ## [0.10.5] - 2023-09-03
10
21
 
11
22
  - Handle ERB-tags inside HTML-tags, like `<div <%= "class='foo'" %>>`
@@ -109,7 +120,9 @@ Output:
109
120
  - Can format a lot of .html.erb-syntax and works as a plugin to syntax_tree.
110
121
  - This is still early and there are a lot of different weird syntaxes out there.
111
122
 
112
- [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
113
126
  [0.10.5]: https://github.com/davidwessman/syntax_tree-erb/compare/v0.10.4...v0.10.5
114
127
  [0.10.4]: https://github.com/davidwessman/syntax_tree-erb/compare/v0.10.3...v0.10.4
115
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.10.5)
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,20 +9,21 @@ GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
11
  docile (1.4.0)
12
- minitest (5.19.0)
12
+ minitest (5.22.3)
13
13
  prettier_print (1.2.1)
14
- rake (13.0.6)
14
+ rake (13.2.1)
15
15
  simplecov (0.22.0)
16
16
  docile (~> 1.1)
17
17
  simplecov-html (~> 0.11)
18
18
  simplecov_json_formatter (~> 0.1)
19
19
  simplecov-html (0.12.3)
20
20
  simplecov_json_formatter (0.1.4)
21
- syntax_tree (6.1.1)
21
+ syntax_tree (6.2.0)
22
22
  prettier_print (>= 1.2.0)
23
23
 
24
24
  PLATFORMS
25
25
  arm64-darwin-21
26
+ arm64-darwin-23
26
27
  x86_64-darwin-21
27
28
  x86_64-darwin-22
28
29
  x86_64-linux
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"
@@ -79,6 +87,12 @@ puts failures
79
87
 
80
88
  ## Development
81
89
 
90
+ Install `husky`:
91
+
92
+ ```sh
93
+ npm i -g husky
94
+ ```
95
+
82
96
  Setup linting:
83
97
 
84
98
  ```sh
@@ -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)
@@ -203,6 +213,10 @@ module SyntaxTree
203
213
  q.text(" ")
204
214
  end
205
215
 
216
+ # If element is a valid void element, but not currently self-closing
217
+ # format to be self-closing
218
+ q.text(" /") if node.is_void_element? and node.closing.value == ">"
219
+
206
220
  visit(node.closing)
207
221
  end
208
222
  end
@@ -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)
@@ -185,6 +143,25 @@ module SyntaxTree
185
143
  # potentially contain an opening tag that self-closes, in which case the
186
144
  # content and closing tag will be nil.
187
145
  class HtmlNode < Block
146
+ # These elements do not require a closing tag
147
+ # https://developer.mozilla.org/en-US/docs/Glossary/Void_element
148
+ HTML_VOID_ELEMENTS = %w[
149
+ area
150
+ base
151
+ br
152
+ col
153
+ embed
154
+ hr
155
+ img
156
+ input
157
+ link
158
+ meta
159
+ param
160
+ source
161
+ track
162
+ wbr
163
+ ]
164
+
188
165
  # The opening tag of an element. It contains the opening character (<),
189
166
  # the name of the element, any optional attributes, and the closing
190
167
  # token (either > or />).
@@ -214,6 +191,10 @@ module SyntaxTree
214
191
  [opening, name, *attributes, closing]
215
192
  end
216
193
 
194
+ def is_void_element?
195
+ HTML_VOID_ELEMENTS.include?(name.value)
196
+ end
197
+
217
198
  alias deconstruct child_nodes
218
199
 
219
200
  def deconstruct_keys(keys)
@@ -253,6 +234,10 @@ module SyntaxTree
253
234
  end
254
235
  end
255
236
 
237
+ def is_void_element?
238
+ false
239
+ end
240
+
256
241
  def without_new_line
257
242
  self.class.new(
258
243
  **deconstruct_keys([]).merge(
@@ -295,6 +280,7 @@ module SyntaxTree
295
280
  )
296
281
  end
297
282
 
283
+ @content = content
298
284
  @content = prepare_content(content)
299
285
  @closing_tag = closing_tag
300
286
  end
@@ -343,9 +329,28 @@ module SyntaxTree
343
329
  end
344
330
  rescue SyntaxTree::Parser::ParseError
345
331
  # Try to add the keyword to see if it parses
346
- result = ErbContent.new(value: [keyword, *content])
347
- @keyword = nil
348
- 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
349
354
  end
350
355
  end
351
356
 
@@ -462,7 +467,17 @@ module SyntaxTree
462
467
  def initialize(value:)
463
468
  if value.is_a?(Array)
464
469
  value =
465
- 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
466
481
  end
467
482
  @value = SyntaxTree.parse(value.strip)
468
483
  end
@@ -481,7 +496,7 @@ module SyntaxTree
481
496
  end
482
497
 
483
498
  def child_nodes
484
- []
499
+ [@value].compact
485
500
  end
486
501
 
487
502
  alias deconstruct child_nodes
@@ -491,6 +506,21 @@ module SyntaxTree
491
506
  end
492
507
  end
493
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
+
494
524
  # An HtmlAttribute is a key-value pair within a tag. It contains the key, the
495
525
  # equals sign, and the value.
496
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 =
@@ -419,21 +487,31 @@ module SyntaxTree
419
487
  def parse_html_element
420
488
  opening = parse_html_opening_tag
421
489
 
422
- if opening.closing.value == ">"
490
+ if opening.closing.value == "/>"
491
+ HtmlNode.new(opening: opening, location: opening.location)
492
+ elsif opening.is_void_element?
493
+ HtmlNode.new(opening: opening, location: opening.location)
494
+ else
423
495
  elements = many { parse_any_tag }
424
496
  closing = maybe { parse_html_closing }
425
497
 
426
498
  if closing.nil?
427
499
  raise(
428
- ParseError,
429
- "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
+ )
430
505
  )
431
506
  end
432
507
 
433
508
  if closing.name.value != opening.name.value
434
509
  raise(
435
- ParseError,
436
- "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
+ )
437
515
  )
438
516
  end
439
517
 
@@ -443,8 +521,6 @@ module SyntaxTree
443
521
  closing: closing,
444
522
  location: opening.location.to(closing.location)
445
523
  )
446
- else
447
- HtmlNode.new(opening: opening, location: opening.location)
448
524
  end
449
525
  end
450
526
 
@@ -457,9 +533,13 @@ module SyntaxTree
457
533
 
458
534
  unless erb_tag.is_a?(ErbCaseWhen) || erb_tag.is_a?(ErbElse) ||
459
535
  erb_tag.is_a?(ErbEnd)
536
+ location = erb_tag&.location || erb_node.location
460
537
  raise(
461
- ParseError,
462
- "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
+ )
463
543
  )
464
544
  end
465
545
 
@@ -480,8 +560,11 @@ module SyntaxTree
480
560
  )
481
561
  else
482
562
  raise(
483
- ParseError,
484
- "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
+ )
485
568
  )
486
569
  end
487
570
  end
@@ -497,8 +580,11 @@ module SyntaxTree
497
580
 
498
581
  unless erb_tag.is_a?(ErbControl) || erb_tag.is_a?(ErbEnd)
499
582
  raise(
500
- ParseError,
501
- "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
+ )
502
588
  )
503
589
  end
504
590
 
@@ -526,8 +612,11 @@ module SyntaxTree
526
612
  )
527
613
  else
528
614
  raise(
529
- ParseError,
530
- "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
+ )
531
620
  )
532
621
  end
533
622
  end
@@ -539,8 +628,11 @@ module SyntaxTree
539
628
 
540
629
  unless erb_end.is_a?(ErbEnd)
541
630
  raise(
542
- ParseError,
543
- "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
+ )
544
636
  )
545
637
  end
546
638
 
@@ -578,8 +670,11 @@ module SyntaxTree
578
670
 
579
671
  if !closing_tag.is_a?(ErbClose)
580
672
  raise(
581
- ParseError,
582
- "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
+ )
583
678
  )
584
679
  end
585
680
 
@@ -611,8 +706,11 @@ module SyntaxTree
611
706
 
612
707
  unless erb_end.is_a?(ErbEnd)
613
708
  raise(
614
- ParseError,
615
- "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
+ )
616
714
  )
617
715
  end
618
716
 
@@ -626,17 +724,6 @@ module SyntaxTree
626
724
  erb_node
627
725
  end
628
726
  end
629
- rescue MissingTokenError => error
630
- # If we have parsed tokens that we cannot process after we parsed <%, we should throw a ParseError
631
- # and not let it be handled by a `maybe`.
632
- if opening_tag
633
- raise(
634
- ParseError,
635
- "Could not parse ERB-tag at #{opening_tag.location}"
636
- )
637
- else
638
- raise(error)
639
- end
640
727
  end
641
728
 
642
729
  def parse_until_erb_close
@@ -646,7 +733,7 @@ module SyntaxTree
646
733
  result =
647
734
  atleast do
648
735
  maybe { parse_erb_do_close } || maybe { parse_erb_close } ||
649
- maybe { consume(:erb_code) }
736
+ maybe { parse_erb_yield } || maybe { consume(:erb_code) }
650
737
  end
651
738
 
652
739
  items << result
@@ -699,6 +786,14 @@ module SyntaxTree
699
786
  )
700
787
  end
701
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
+
702
797
  def parse_html_string
703
798
  opening =
704
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.10.5"
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.10.5
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: 2023-09-03 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
@@ -118,10 +118,10 @@ files:
118
118
  - ".github/ISSUE_TEMPLATE/formatting-report.md"
119
119
  - ".github/ISSUE_TEMPLATE/general.md"
120
120
  - ".github/dependabot.yml"
121
- - ".github/workflows/auto-merge.yml"
122
121
  - ".github/workflows/main.yml"
123
122
  - ".gitignore"
124
123
  - ".husky/pre-commit"
124
+ - ".tool-versions"
125
125
  - ".vscode/launch.json"
126
126
  - CHANGELOG.md
127
127
  - Gemfile
@@ -159,7 +159,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
159
159
  - !ruby/object:Gem::Version
160
160
  version: '0'
161
161
  requirements: []
162
- rubygems_version: 3.4.19
162
+ rubygems_version: 3.5.9
163
163
  signing_key:
164
164
  specification_version: 4
165
165
  summary: Syntax Tree support for ERB
@@ -1,22 +0,0 @@
1
- name: Dependabot auto-merge
2
- on: pull_request
3
-
4
- permissions:
5
- contents: write
6
- pull-requests: write
7
-
8
- jobs:
9
- dependabot:
10
- runs-on: ubuntu-latest
11
- if: ${{ github.actor == 'dependabot[bot]' }}
12
- steps:
13
- - name: Dependabot metadata
14
- id: metadata
15
- uses: dependabot/fetch-metadata@v1.3.3
16
- with:
17
- github-token: "${{ secrets.GITHUB_TOKEN }}"
18
- - name: Enable auto-merge for Dependabot PRs
19
- run: gh pr merge --auto --merge "$PR_URL"
20
- env:
21
- PR_URL: ${{github.event.pull_request.html_url}}
22
- GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}