herb 0.11.0-arm-linux-gnu → 0.11.1-arm-linux-gnu

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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/config.yml +3 -0
  3. data/ext/herb/nodes.c +34 -17
  4. data/lib/herb/3.2/herb.so +0 -0
  5. data/lib/herb/3.3/herb.so +0 -0
  6. data/lib/herb/3.4/herb.so +0 -0
  7. data/lib/herb/4.0/herb.so +0 -0
  8. data/lib/herb/ast/nodes.rb +32 -6
  9. data/lib/herb/bootstrap.rb +6 -2
  10. data/lib/herb/dev/runner.rb +6 -1
  11. data/lib/herb/engine/compiler.rb +47 -11
  12. data/lib/herb/engine/slots/dynamics_compiler.rb +10 -0
  13. data/lib/herb/engine/slots/markers.rb +24 -0
  14. data/lib/herb/engine/slots/state_compiler.rb +1 -2
  15. data/lib/herb/engine/slots/visitor.rb +39 -28
  16. data/lib/herb/engine.rb +0 -2
  17. data/lib/herb/version.rb +1 -1
  18. data/sig/herb/ast/nodes.rbs +10 -4
  19. data/sig/herb/bootstrap.rbs +2 -0
  20. data/sig/herb/dev/runner.rbs +3 -0
  21. data/sig/herb/engine/compiler.rbs +14 -3
  22. data/sig/herb/engine/slots/dynamics_compiler.rbs +3 -0
  23. data/sig/herb/engine/slots/markers.rbs +14 -0
  24. data/sig/herb/engine/slots/visitor.rbs +17 -13
  25. data/sig/serialized_ast_nodes.rbs +1 -0
  26. data/src/analyze/action_view/tag_helper_node_builders.c +6 -3
  27. data/src/analyze/action_view/tag_helpers.c +76 -20
  28. data/src/analyze/analyze.c +5 -25
  29. data/src/analyze/analyze_helpers.c +94 -232
  30. data/src/analyze/analyzed_ruby.c +2 -0
  31. data/src/analyze/builders.c +1 -0
  32. data/src/analyze/control_type.c +17 -7
  33. data/src/analyze/prism_annotate.c +32 -9
  34. data/src/ast/ast_nodes.c +3 -1
  35. data/src/ast/ast_pretty_print.c +5 -1
  36. data/src/extract.c +204 -143
  37. data/src/include/analyze/analyze.h +0 -22
  38. data/src/include/analyze/analyzed_ruby.h +25 -0
  39. data/src/include/analyze/helpers.h +2 -22
  40. data/src/include/ast/ast_nodes.h +2 -1
  41. data/src/include/lexer/lexer.h +1 -0
  42. data/src/include/prism/herb_prism_node.h +6 -1
  43. data/src/include/version.h +1 -1
  44. data/src/lexer.c +32 -0
  45. data/src/prism/prism_helpers.c +91 -0
  46. data/templates/ext/herb/nodes.c.erb +1 -1
  47. data/templates/java/nodes.c.erb +1 -1
  48. data/templates/javascript/packages/core/src/nodes.ts.erb +21 -1
  49. data/templates/javascript/packages/node/extension/nodes.cpp.erb +1 -1
  50. data/templates/rust/src/ast/nodes.rs.erb +1 -4
  51. data/templates/wasm/nodes.cpp.erb +5 -15
  52. metadata +1 -1
@@ -1202,6 +1202,11 @@ module Herb
1202
1202
  )
1203
1203
  end
1204
1204
 
1205
+ #: (Array[untyped], Integer) -> String
1206
+ def attribute_spacing(children, position)
1207
+ position.positive? && children[position - 1].is_a?(Herb::AST::WhitespaceNode) ? "" : " "
1208
+ end
1209
+
1205
1210
  private
1206
1211
 
1207
1212
  def visit_children_with_paths(children)
@@ -1212,6 +1217,7 @@ module Herb
1212
1217
  @states.check_state_assignment(child)
1213
1218
 
1214
1219
  @path.push(index)
1220
+ record_slot(child, :child) if child.is_a?(Herb::AST::RubyLiteralNode)
1215
1221
  visit(child)
1216
1222
  @path.pop
1217
1223
  end
@@ -1521,7 +1527,7 @@ module Herb
1521
1527
  def convert_helper_boolean_attribute(node)
1522
1528
  open_tag = @current_open_tag
1523
1529
 
1524
- return nil unless @in_open_tag && open_tag.is_a?(Herb::AST::ERBOpenTagNode)
1530
+ return nil unless @in_open_tag && (open_tag.is_a?(Herb::AST::ERBOpenTagNode) || open_tag.is_a?(Herb::AST::HTMLOpenTagNode))
1525
1531
  return nil if continuation?(node)
1526
1532
  return nil unless node.respond_to?(:subsequent) && node.subsequent.nil?
1527
1533
 
@@ -1540,7 +1546,7 @@ module Herb
1540
1546
  return nil unless name && Herb::HTML::Util.boolean_attribute?(name)
1541
1547
 
1542
1548
  condition = @states.condition_expression(node)
1543
- replacement = erb_output_node(%[(" #{name}" if #{condition})])
1549
+ replacement = erb_output_node(%[("#{attribute_spacing(children, position)}#{name}" if #{condition})])
1544
1550
 
1545
1551
  children[position] = replacement
1546
1552
  record_slot(replacement, :attribute)
@@ -1864,23 +1870,25 @@ module Herb
1864
1870
  outputs.fetch(0).content&.value&.strip
1865
1871
  end
1866
1872
 
1867
- def insert_markers(node)
1873
+ #: (untyped, ?Symbol) -> untyped
1874
+ def insert_markers(node, namespace = :html)
1868
1875
  anchored = content_anchor_index(node)
1876
+ namespace = Markers.namespace_within(namespace, node.tag_name&.value&.downcase.to_s) if node.is_a?(Herb::AST::HTMLElementNode)
1869
1877
 
1870
- each_child_array(node) do |array|
1878
+ each_child_array(node, namespace) do |array|
1871
1879
  index = 0
1872
1880
 
1873
1881
  while index < array.size
1874
1882
  child = array[index]
1875
1883
  slot_index = @standing[child]&.survivor&.index
1876
1884
 
1877
- insert_markers(child)
1878
- wrap_items(child, slot_index)
1879
- mark_branches(child, slot_index)
1885
+ insert_markers(child, namespace)
1886
+ wrap_items(child, slot_index, namespace)
1887
+ mark_branches(child, slot_index, namespace)
1880
1888
 
1881
1889
  if slot_index && slot_index != anchored
1882
1890
  if @slots[slot_index].type == :keyed
1883
- park_keyed(slot_index, child)
1891
+ park_keyed(slot_index, child, namespace)
1884
1892
 
1885
1893
  array.insert(
1886
1894
  index,
@@ -1921,27 +1929,29 @@ module Herb
1921
1929
  annotation.index
1922
1930
  end
1923
1931
 
1924
- #: (untyped, Integer?) -> void
1925
- def mark_branches(node, slot_index)
1932
+ #: (untyped, Integer?, ?Symbol) -> void
1933
+ def mark_branches(node, slot_index, namespace = :html)
1926
1934
  return unless slot_index
1927
1935
  return unless @slots[slot_index].type == :conditional
1928
1936
 
1929
1937
  branch_bodies(node).each_with_index do |body, branch_index|
1930
- body.unshift(text_node(@markers.branch(slot_index, branch_index)))
1938
+ marker = @markers.branch(slot_index, branch_index)
1931
1939
 
1932
- park_branch(@markers.statics_key(slot_index, branch_index), body)
1940
+ park_branch(@markers.statics_key(slot_index, branch_index), marker, body, namespace)
1941
+
1942
+ body.unshift(text_node(marker))
1933
1943
  end
1934
1944
  end
1935
1945
 
1936
- #: (String, Array[untyped]) -> void
1937
- def park_branch(key, body)
1946
+ #: (String, String, Array[untyped], Symbol) -> void
1947
+ def park_branch(key, marker, body, namespace)
1938
1948
  statics = @statics
1939
1949
  return unless statics
1940
1950
 
1941
1951
  markup = Statics.new(@standing).markup(body)
1942
1952
  return unless markup
1943
1953
 
1944
- statics[key] = markup
1954
+ statics[key] = "#{marker}#{@markers.foreign(markup, namespace)}"
1945
1955
  end
1946
1956
 
1947
1957
  #: (Herb::AST::DocumentNode) -> void
@@ -2062,8 +2072,8 @@ module Herb
2062
2072
  bodies
2063
2073
  end
2064
2074
 
2065
- #: (Herb::AST::Node, Integer?) -> void
2066
- def wrap_items(node, slot_index)
2075
+ #: (Herb::AST::Node, Integer?, ?Symbol) -> void
2076
+ def wrap_items(node, slot_index, namespace = :html)
2067
2077
  return unless slot_index
2068
2078
 
2069
2079
  slot = @slots[slot_index]
@@ -2076,7 +2086,7 @@ module Herb
2076
2086
  next unless body.is_a?(Array) && !body.empty?
2077
2087
 
2078
2088
  key = @markers.item_statics_key(slot_index)
2079
- parked = park_item(key, body)
2089
+ parked = park_item(key, body, namespace)
2080
2090
 
2081
2091
  body.unshift(
2082
2092
  text_node(@markers.item_open_prefix(slot_index)),
@@ -2112,19 +2122,19 @@ module Herb
2112
2122
  segments.size > 1 ? segments : nil
2113
2123
  end
2114
2124
 
2115
- #: (Integer, untyped) -> void
2116
- def park_keyed(slot_index, element)
2125
+ #: (Integer, untyped, Symbol) -> void
2126
+ def park_keyed(slot_index, element, namespace)
2117
2127
  statics = @statics
2118
2128
  return unless statics
2119
2129
 
2120
2130
  markup = Statics.new(@standing).markup([element.open_tag, *element.body, element.close_tag].compact)
2121
2131
  return unless markup
2122
2132
 
2123
- statics[@markers.item_statics_key(slot_index)] = "#{@markers.branch(slot_index, Markers::ITEM_STATICS)}#{markup}"
2133
+ statics[@markers.item_statics_key(slot_index)] = "#{@markers.branch(slot_index, Markers::ITEM_STATICS)}#{@markers.foreign(markup, namespace)}"
2124
2134
  end
2125
2135
 
2126
- #: (untyped, untyped) -> Array[untyped]?
2127
- def park_item(key, body)
2136
+ #: (untyped, untyped, Symbol) -> Array[untyped]?
2137
+ def park_item(key, body, namespace)
2128
2138
  statics = @statics
2129
2139
  return unless statics
2130
2140
 
@@ -2132,13 +2142,14 @@ module Herb
2132
2142
  markup = Statics.new(@standing).markup(body)
2133
2143
  return unless markup
2134
2144
 
2135
- statics[key] = [
2136
- @markers.branch(slot_index, Markers::ITEM_STATICS),
2145
+ item = [
2137
2146
  @markers.item_open_prefix(slot_index),
2138
2147
  @markers.item_open_suffix,
2139
2148
  markup,
2140
2149
  @markers.item_close(slot_index)
2141
2150
  ].join
2151
+
2152
+ statics[key] = "#{@markers.branch(slot_index, Markers::ITEM_STATICS)}#{@markers.foreign(item, namespace)}"
2142
2153
  end
2143
2154
 
2144
2155
  def anchor_attributes(node, content_index = nil)
@@ -2173,8 +2184,8 @@ module Herb
2173
2184
  name
2174
2185
  end
2175
2186
 
2176
- #: (Herb::AST::Node) { (Array[untyped]) -> void } -> void
2177
- def each_child_array(node)
2187
+ #: (Herb::AST::Node, ?Symbol) { (Array[untyped]) -> void } -> void
2188
+ def each_child_array(node, namespace = :html)
2178
2189
  BRANCH_BODY_PROPERTIES.each do |property|
2179
2190
  next unless node.respond_to?(property)
2180
2191
 
@@ -2186,7 +2197,7 @@ module Herb
2186
2197
  next unless node.respond_to?(property)
2187
2198
 
2188
2199
  child = node.send(property)
2189
- insert_markers(child) if child
2200
+ insert_markers(child, namespace) if child
2190
2201
  end
2191
2202
  end
2192
2203
 
data/lib/herb/engine.rb CHANGED
@@ -220,8 +220,6 @@ module Herb
220
220
  @src << "\n" << code
221
221
  @src << "\n" unless code.end_with?("\n")
222
222
  else
223
- @src.chomp! if @src.end_with?("\n") && code.start_with?(" ") && !code.end_with?("\n")
224
-
225
223
  @src << " " unless code.match?(/\A\n+\z/)
226
224
  @src << code
227
225
 
data/lib/herb/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # typed: true
3
3
 
4
4
  module Herb
5
- VERSION = "0.11.0"
5
+ VERSION = "0.11.1"
6
6
  end
@@ -2133,6 +2133,7 @@ module Herb
2133
2133
  # | tag_opening: Herb::Token?,
2134
2134
  # | content: Herb::Token?,
2135
2135
  # | tag_closing: Herb::Token?,
2136
+ # | prism_node: String?,
2136
2137
  # | }
2137
2138
  class ERBYieldNode < Node
2138
2139
  include Colors
@@ -2143,14 +2144,19 @@ module Herb
2143
2144
 
2144
2145
  attr_reader tag_closing: Herb::Token?
2145
2146
 
2146
- # : (String, Location, Array[Herb::Errors::Error], Herb::Token?, Herb::Token?, Herb::Token?) -> void
2147
- def initialize: (String, Location, Array[Herb::Errors::Error], Herb::Token?, Herb::Token?, Herb::Token?) -> void
2147
+ attr_reader prism_node: String?
2148
+
2149
+ # : (String, Location, Array[Herb::Errors::Error], Herb::Token?, Herb::Token?, Herb::Token?, String?) -> void
2150
+ def initialize: (String, Location, Array[Herb::Errors::Error], Herb::Token?, Herb::Token?, Herb::Token?, String?) -> void
2148
2151
 
2149
2152
  # : () -> String
2150
2153
  def self.type: () -> String
2151
2154
 
2152
- # : (?tag_opening: Herb::Token?, ?content: Herb::Token?, ?tag_closing: Herb::Token?, ?location: Location, ?errors: Array[Herb::Errors::Error]) -> ERBYieldNode
2153
- def self.build: (?tag_opening: Herb::Token?, ?content: Herb::Token?, ?tag_closing: Herb::Token?, ?location: Location, ?errors: Array[Herb::Errors::Error]) -> ERBYieldNode
2155
+ # : (?tag_opening: Herb::Token?, ?content: Herb::Token?, ?tag_closing: Herb::Token?, ?prism_node: String?, ?location: Location, ?errors: Array[Herb::Errors::Error]) -> ERBYieldNode
2156
+ def self.build: (?tag_opening: Herb::Token?, ?content: Herb::Token?, ?tag_closing: Herb::Token?, ?prism_node: String?, ?location: Location, ?errors: Array[Herb::Errors::Error]) -> ERBYieldNode
2157
+
2158
+ # : () -> Prism::node?
2159
+ def deserialized_prism_node: () -> Prism::node?
2154
2160
 
2155
2161
  # : () -> serialized_erb_yield_node
2156
2162
  def to_hash: () -> serialized_erb_yield_node
@@ -26,6 +26,8 @@ module Herb
26
26
 
27
27
  def self.find_prism_as_bundler_sibling: () -> untyped
28
28
 
29
+ def self.prism_source?: (untyped path) -> untyped
30
+
29
31
  def self.find_prism_from_gem_spec: () -> untyped
30
32
  end
31
33
  end
@@ -36,6 +36,9 @@ module Herb
36
36
  # : () -> bool
37
37
  def interactive?: () -> bool
38
38
 
39
+ # : () -> bool
40
+ def process_manager?: () -> bool
41
+
39
42
  # : (String) -> void
40
43
  def terminal: (String) -> void
41
44
 
@@ -65,6 +65,8 @@ module Herb
65
65
 
66
66
  def visit_erb_comment_node: (untyped node) -> untyped
67
67
 
68
+ def visit_erb_strict_locals_node: (untyped node) -> untyped
69
+
68
70
  def visit_erb_control_node: (untyped node) ?{ (?) -> untyped } -> untyped
69
71
 
70
72
  def visit_erb_if_node: (untyped node) -> untyped
@@ -105,9 +107,9 @@ module Herb
105
107
 
106
108
  def visit_erb_block_end_node: (untyped node, ?escaped: untyped) -> untyped
107
109
 
108
- def case_parts: (untyped node) -> untyped
110
+ def visit_erb_case_parts: (untyped node) -> untyped
109
111
 
110
- def visit_erb_control_with_parts: (untyped node, *untyped parts) -> untyped
112
+ def visit_erb_control_with_parts: (untyped node, *untyped parts) ?{ (?) -> untyped } -> untyped
111
113
 
112
114
  private
113
115
 
@@ -188,7 +190,7 @@ module Herb
188
190
 
189
191
  def leading_space_follows_newline?: () -> untyped
190
192
 
191
- def preceding_text_ends_with_newline?: () -> untyped
193
+ def preceding_token_value_ends_with_newline?: () -> untyped
192
194
 
193
195
  def extract_and_remove_leading_space!: () -> untyped
194
196
 
@@ -205,6 +207,15 @@ module Herb
205
207
  # : (bool) -> void
206
208
  def settle_pending_trim_newline!: (bool) -> void
207
209
 
210
+ # : (Array[untyped]) -> void
211
+ def skip_case_children: (Array[untyped]) -> void
212
+
213
+ # : (untyped) -> bool
214
+ def follows_trimmed_statement_tag?: (untyped) -> bool
215
+
216
+ # : () -> void
217
+ def drop_pending_trim_newline!: () -> void
218
+
208
219
  def save_pending_leading_whitespace!: (untyped whitespace) -> untyped
209
220
 
210
221
  def restore_pending_leading_whitespace!: () -> untyped
@@ -132,6 +132,9 @@ module Herb
132
132
  # : (untyped) -> void
133
133
  def visit_erb_content_node: (untyped) -> void
134
134
 
135
+ # : (untyped) -> void
136
+ def visit_ruby_literal_node: (untyped) -> void
137
+
135
138
  # : (untyped) -> void
136
139
  def visit_erb_yield_node: (untyped) -> void
137
140
 
@@ -14,15 +14,29 @@ module Herb
14
14
 
15
15
  COMMENT: Regexp
16
16
 
17
+ FOREIGN_ATTRIBUTE: String
18
+
19
+ FOREIGN_ROOTS: Hash[Symbol, String]
20
+
21
+ SVG_INTEGRATION_POINTS: Array[String]
22
+
23
+ MATH_INTEGRATION_POINTS: Array[String]
24
+
17
25
  # : (String) -> bool
18
26
  def self.marker?: (String) -> bool
19
27
 
20
28
  # : (String) -> String
21
29
  def self.seeds_expression: (String) -> String
22
30
 
31
+ # : (Symbol, String) -> Symbol
32
+ def self.namespace_within: (Symbol, String) -> Symbol
33
+
23
34
  # : (Integer, Symbol) -> String
24
35
  def slot_open: (Integer, Symbol) -> String
25
36
 
37
+ # : (String, Symbol) -> String
38
+ def foreign: (String, Symbol) -> String
39
+
26
40
  # : (Array[[Integer, Symbol, String?]]) -> String
27
41
  def element_anchors: (Array[[ Integer, Symbol, String? ]]) -> String
28
42
 
@@ -404,6 +404,9 @@ module Herb
404
404
 
405
405
  def erb_output_node: (untyped code) -> untyped
406
406
 
407
+ # : (Array[untyped], Integer) -> String
408
+ def attribute_spacing: (Array[untyped], Integer) -> String
409
+
407
410
  private
408
411
 
409
412
  def visit_children_with_paths: (untyped children) -> untyped
@@ -526,16 +529,17 @@ module Herb
526
529
  # : (untyped) -> String?
527
530
  def attribute_expression_for: (untyped) -> String?
528
531
 
529
- def insert_markers: (untyped node) -> untyped
532
+ # : (untyped, ?Symbol) -> untyped
533
+ def insert_markers: (untyped, ?Symbol) -> untyped
530
534
 
531
535
  # : (untyped) -> Integer?
532
536
  def content_anchor_index: (untyped) -> Integer?
533
537
 
534
- # : (untyped, Integer?) -> void
535
- def mark_branches: (untyped, Integer?) -> void
538
+ # : (untyped, Integer?, ?Symbol) -> void
539
+ def mark_branches: (untyped, Integer?, ?Symbol) -> void
536
540
 
537
- # : (String, Array[untyped]) -> void
538
- def park_branch: (String, Array[untyped]) -> void
541
+ # : (String, String, Array[untyped], Symbol) -> void
542
+ def park_branch: (String, String, Array[untyped], Symbol) -> void
539
543
 
540
544
  # : (Herb::AST::DocumentNode) -> void
541
545
  def append_statics: (Herb::AST::DocumentNode) -> void
@@ -558,25 +562,25 @@ module Herb
558
562
  # : (untyped) -> Array[Array[Herb::AST::Node]]
559
563
  def branch_bodies: (untyped) -> Array[Array[Herb::AST::Node]]
560
564
 
561
- # : (Herb::AST::Node, Integer?) -> void
562
- def wrap_items: (Herb::AST::Node, Integer?) -> void
565
+ # : (Herb::AST::Node, Integer?, ?Symbol) -> void
566
+ def wrap_items: (Herb::AST::Node, Integer?, ?Symbol) -> void
563
567
 
564
568
  # : (untyped) -> Array[String]?
565
569
  def attribute_segments: (untyped) -> Array[String]?
566
570
 
567
- # : (Integer, untyped) -> void
568
- def park_keyed: (Integer, untyped) -> void
571
+ # : (Integer, untyped, Symbol) -> void
572
+ def park_keyed: (Integer, untyped, Symbol) -> void
569
573
 
570
- # : (untyped, untyped) -> Array[untyped]?
571
- def park_item: (untyped, untyped) -> Array[untyped]?
574
+ # : (untyped, untyped, Symbol) -> Array[untyped]?
575
+ def park_item: (untyped, untyped, Symbol) -> Array[untyped]?
572
576
 
573
577
  def anchor_attributes: (untyped node, ?untyped content_index) -> untyped
574
578
 
575
579
  # : (Slot) -> String?
576
580
  def anchor_name_for: (Slot) -> String?
577
581
 
578
- # : (Herb::AST::Node) { (Array[untyped]) -> void } -> void
579
- def each_child_array: (Herb::AST::Node) { (Array[untyped]) -> void } -> void
582
+ # : (Herb::AST::Node, ?Symbol) { (Array[untyped]) -> void } -> void
583
+ def each_child_array: (Herb::AST::Node, ?Symbol) { (Array[untyped]) -> void } -> void
580
584
 
581
585
  # : (Herb::AST::DocumentNode) -> void
582
586
  def wrap_region: (Herb::AST::DocumentNode) -> void
@@ -360,6 +360,7 @@ module Herb
360
360
  tag_opening: Herb::Token,
361
361
  content: Herb::Token,
362
362
  tag_closing: Herb::Token,
363
+ prism_node: String,
363
364
  }
364
365
 
365
366
  type serialized_erb_in_node = serialized_node & {
@@ -368,11 +368,14 @@ static AST_HTML_ATTRIBUTE_VALUE_NODE_T* create_interpolated_attribute_value(
368
368
  }
369
369
  }
370
370
 
371
+ token_T* open_quote = create_synthetic_token(allocator, "\"", TOKEN_QUOTE, start_position, start_position);
372
+ token_T* close_quote = create_synthetic_token(allocator, "\"", TOKEN_QUOTE, end_position, end_position);
373
+
371
374
  return ast_html_attribute_value_node_init(
372
- NULL,
375
+ open_quote,
373
376
  value_children,
374
- NULL,
375
- false,
377
+ close_quote,
378
+ true,
376
379
  start_position,
377
380
  end_position,
378
381
  hb_array_init(0, allocator),
@@ -132,6 +132,50 @@ static bool build_scope_options_from_context(
132
132
  return built;
133
133
  }
134
134
 
135
+ static bool symbol_node_equals(pm_node_t* node, const char* name) {
136
+ if (!node || node->type != PM_SYMBOL_NODE) { return false; }
137
+
138
+ pm_symbol_node_t* symbol = (pm_symbol_node_t*) node;
139
+ size_t length = pm_string_length(&symbol->unescaped);
140
+
141
+ return length == strlen(name) && strncmp((const char*) pm_string_source(&symbol->unescaped), name, length) == 0;
142
+ }
143
+
144
+ static bool asset_tag_resolves_at_render_time(pm_call_node_t* call_node, const char* helper_name) {
145
+ bool is_stylesheet_link_tag = string_equals(helper_name, "stylesheet_link_tag");
146
+
147
+ if (!is_stylesheet_link_tag && !string_equals(helper_name, "javascript_include_tag")) { return false; }
148
+ if (!call_node || !call_node->arguments) { return false; }
149
+
150
+ pm_node_list_t* arguments = &call_node->arguments->arguments;
151
+
152
+ for (size_t i = 0; i < arguments->size; i++) {
153
+ pm_node_t* argument = arguments->nodes[i];
154
+
155
+ if (argument->type == PM_SPLAT_NODE || argument->type == PM_ARRAY_NODE) { return true; }
156
+
157
+ if (is_stylesheet_link_tag && i == 0
158
+ && (symbol_node_equals(argument, "all") || symbol_node_equals(argument, "app"))) {
159
+ return true;
160
+ }
161
+
162
+ if (argument->type != PM_KEYWORD_HASH_NODE) { continue; }
163
+
164
+ pm_keyword_hash_node_t* hash = (pm_keyword_hash_node_t*) argument;
165
+
166
+ for (size_t j = 0; j < hash->elements.size; j++) {
167
+ pm_node_t* element = hash->elements.nodes[j];
168
+ if (element->type != PM_ASSOC_NODE) { continue; }
169
+
170
+ pm_assoc_node_t* assoc = (pm_assoc_node_t*) element;
171
+
172
+ if (symbol_node_equals(assoc->key, "integrity") && assoc->value->type != PM_STRING_NODE) { return true; }
173
+ }
174
+ }
175
+
176
+ return false;
177
+ }
178
+
135
179
  static tag_helper_parse_context_T* parse_tag_helper_content(
136
180
  const char* content_string,
137
181
  const char* original_source,
@@ -181,7 +225,8 @@ static tag_helper_parse_context_T* parse_tag_helper_content(
181
225
  .found = false };
182
226
  pm_visit_node(parse_context->root, search_tag_helper_node, &search);
183
227
 
184
- if (!search.found) {
228
+ if (!search.found
229
+ || asset_tag_resolves_at_render_time(parse_context->info->call_node, search.matched_handler->name)) {
185
230
  tag_helper_info_free(&parse_context->info);
186
231
  pm_node_destroy(&parse_context->parser, parse_context->root);
187
232
  pm_parser_free(&parse_context->parser);
@@ -252,13 +297,17 @@ position_T byte_offset_to_position(const char* source, size_t offset) {
252
297
 
253
298
  if (!source) { return position; }
254
299
 
255
- for (size_t i = 0; i < offset && source[i] != '\0'; i++) {
256
- if (source[i] == '\n') {
257
- position.line++;
258
- position.column = 1;
259
- } else if (!utf8_is_valid_continuation_byte((unsigned char) source[i])) {
260
- position.column++;
261
- }
300
+ const char* cursor = source;
301
+ const char* end = source + strnlen(source, offset);
302
+ const char* newline = NULL;
303
+
304
+ while ((newline = memchr(cursor, '\n', (size_t) (end - cursor))) != NULL) {
305
+ position.line++;
306
+ cursor = newline + 1;
307
+ }
308
+
309
+ for (; cursor < end; cursor++) {
310
+ if (!utf8_is_valid_continuation_byte((unsigned char) *cursor)) { position.column++; }
262
311
  }
263
312
 
264
313
  return position;
@@ -277,33 +326,40 @@ position_T prism_location_to_position_with_offset(
277
326
  size_t offset_in_erb = (size_t) (pm_location->start - erb_content_source);
278
327
  size_t total_offset = erb_content_offset + offset_in_erb;
279
328
 
280
- size_t source_length = strlen(original_source);
281
- if (total_offset > source_length) { return byte_offset_to_position(original_source, erb_content_offset); }
329
+ if (strnlen(original_source, total_offset) < total_offset) {
330
+ return byte_offset_to_position(original_source, erb_content_offset);
331
+ }
282
332
 
283
333
  return byte_offset_to_position(original_source, total_offset);
284
334
  }
285
335
 
286
336
  size_t calculate_byte_offset_from_position(const char* source, position_T position) {
287
337
  if (!source) { return 0; }
338
+ if (position.line == 0) { return strlen(source); }
339
+
340
+ const char* line_start = source;
341
+
342
+ for (uint32_t line = 1; line < position.line; line++) {
343
+ const char* newline = strchr(line_start, '\n');
288
344
 
289
- size_t offset = 0;
290
- uint32_t line = 1;
345
+ if (!newline) { return (size_t) (line_start - source) + strlen(line_start); }
346
+
347
+ line_start = newline + 1;
348
+ }
349
+
350
+ size_t offset = (size_t) (line_start - source);
291
351
  uint32_t column = 1;
292
352
 
293
353
  while (source[offset] != '\0') {
294
- if (line == position.line && column == position.column) { return offset; }
354
+ if (column == position.column) { return offset; }
355
+ if (source[offset] == '\n') { break; }
295
356
 
296
- if (source[offset] == '\n') {
297
- line++;
298
- column = 1;
299
- } else if (!utf8_is_valid_continuation_byte((unsigned char) source[offset])) {
300
- column++;
301
- }
357
+ if (!utf8_is_valid_continuation_byte((unsigned char) source[offset])) { column++; }
302
358
 
303
359
  offset++;
304
360
  }
305
361
 
306
- return offset;
362
+ return offset + strlen(source + offset);
307
363
  }
308
364
 
309
365
  static void prism_node_location_to_positions(
@@ -37,31 +37,11 @@
37
37
  static analyzed_ruby_T* herb_analyze_ruby(hb_string_T source) {
38
38
  analyzed_ruby_T* analyzed = init_analyzed_ruby(source);
39
39
 
40
- pm_visit_node(analyzed->root, search_if_nodes, analyzed);
41
- pm_visit_node(analyzed->root, search_block_nodes, analyzed);
42
- pm_visit_node(analyzed->root, search_case_nodes, analyzed);
43
- pm_visit_node(analyzed->root, search_case_match_nodes, analyzed);
44
- pm_visit_node(analyzed->root, search_while_nodes, analyzed);
45
- pm_visit_node(analyzed->root, search_for_nodes, analyzed);
46
- pm_visit_node(analyzed->root, search_until_nodes, analyzed);
47
- pm_visit_node(analyzed->root, search_begin_nodes, analyzed);
48
- pm_visit_node(analyzed->root, search_unless_nodes, analyzed);
49
- pm_visit_node(analyzed->root, search_when_nodes, analyzed);
50
- pm_visit_node(analyzed->root, search_in_nodes, analyzed);
51
-
52
- search_unexpected_elsif_nodes(analyzed);
53
- search_unexpected_else_nodes(analyzed);
54
- search_unexpected_end_nodes(analyzed);
55
- search_unexpected_when_nodes(analyzed);
56
- search_unexpected_in_nodes(analyzed);
57
-
58
- search_unexpected_rescue_nodes(analyzed);
59
- search_unexpected_ensure_nodes(analyzed);
60
- search_yield_nodes(analyzed->root, analyzed);
61
- search_then_keywords(analyzed->root, analyzed);
62
- search_unexpected_block_closing_nodes(analyzed);
63
-
64
- if (!analyzed->valid) { pm_visit_node(analyzed->root, search_unclosed_control_flows, analyzed); }
40
+ if (analyzed->valid) { return analyzed; }
41
+
42
+ pm_visit_node(analyzed->root, search_control_flow_nodes, analyzed);
43
+ search_unexpected_keyword_errors(analyzed);
44
+ pm_visit_node(analyzed->root, search_unclosed_control_flows, analyzed);
65
45
 
66
46
  return analyzed;
67
47
  }