herb 0.9.3-arm-linux-gnu → 0.9.5-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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/config.yml +57 -21
  3. data/ext/herb/nodes.c +93 -55
  4. data/lib/herb/3.0/herb.so +0 -0
  5. data/lib/herb/3.1/herb.so +0 -0
  6. data/lib/herb/3.2/herb.so +0 -0
  7. data/lib/herb/3.3/herb.so +0 -0
  8. data/lib/herb/3.4/herb.so +0 -0
  9. data/lib/herb/4.0/herb.so +0 -0
  10. data/lib/herb/ast/nodes.rb +212 -78
  11. data/lib/herb/engine/compiler.rb +52 -26
  12. data/lib/herb/engine.rb +3 -0
  13. data/lib/herb/project.rb +58 -17
  14. data/lib/herb/version.rb +1 -1
  15. data/lib/herb/visitor.rb +8 -2
  16. data/sig/herb/ast/nodes.rbs +85 -34
  17. data/sig/herb/engine/compiler.rbs +16 -0
  18. data/sig/herb/engine.rbs +3 -0
  19. data/sig/herb/visitor.rbs +5 -2
  20. data/sig/serialized_ast_nodes.rbs +20 -9
  21. data/src/analyze/action_view/javascript_tag.c +38 -0
  22. data/src/analyze/action_view/tag_helper_node_builders.c +23 -2
  23. data/src/analyze/action_view/tag_helpers.c +53 -14
  24. data/src/analyze/analyze.c +23 -3
  25. data/src/analyze/analyze_helpers.c +406 -0
  26. data/src/analyze/builders.c +1 -0
  27. data/src/analyze/missing_end.c +16 -0
  28. data/src/analyze/parse_errors.c +43 -1
  29. data/src/analyze/render_nodes.c +231 -35
  30. data/src/analyze/strict_locals.c +22 -324
  31. data/src/analyze/transform.c +23 -2
  32. data/src/ast/ast_nodes.c +114 -57
  33. data/src/ast/ast_pretty_print.c +109 -25
  34. data/src/include/analyze/action_view/tag_helper_handler.h +3 -0
  35. data/src/include/analyze/action_view/tag_helper_node_builders.h +7 -0
  36. data/src/include/analyze/analyze.h +6 -1
  37. data/src/include/analyze/helpers.h +18 -0
  38. data/src/include/ast/ast_nodes.h +27 -13
  39. data/src/include/version.h +1 -1
  40. data/src/parser/match_tags.c +37 -6
  41. data/src/parser.c +8 -0
  42. data/src/visitor.c +50 -7
  43. metadata +1 -1
@@ -2128,6 +2128,7 @@ module Herb
2128
2128
  #| tag_closing: Herb::Token?,
2129
2129
  #| prism_node: String?,
2130
2130
  #| body: Array[Herb::AST::Node],
2131
+ #| block_arguments: Array[Herb::AST::RubyParameterNode]?,
2131
2132
  #| rescue_clause: Herb::AST::ERBRescueNode?,
2132
2133
  #| else_clause: Herb::AST::ERBElseNode?,
2133
2134
  #| ensure_clause: Herb::AST::ERBEnsureNode?,
@@ -2141,19 +2142,21 @@ module Herb
2141
2142
  attr_reader :tag_closing #: Herb::Token?
2142
2143
  attr_reader :prism_node #: String?
2143
2144
  attr_reader :body #: Array[Herb::AST::Node]
2145
+ attr_reader :block_arguments #: Array[Herb::AST::RubyParameterNode]?
2144
2146
  attr_reader :rescue_clause #: Herb::AST::ERBRescueNode?
2145
2147
  attr_reader :else_clause #: Herb::AST::ERBElseNode?
2146
2148
  attr_reader :ensure_clause #: Herb::AST::ERBEnsureNode?
2147
2149
  attr_reader :end_node #: Herb::AST::ERBEndNode?
2148
2150
 
2149
- #: (String, Location, Array[Herb::Errors::Error], Herb::Token, Herb::Token, Herb::Token, String, Array[Herb::AST::Node], Herb::AST::ERBRescueNode, Herb::AST::ERBElseNode, Herb::AST::ERBEnsureNode, Herb::AST::ERBEndNode) -> void
2150
- def initialize(type, location, errors, tag_opening, content, tag_closing, prism_node, body, rescue_clause, else_clause, ensure_clause, end_node)
2151
+ #: (String, Location, Array[Herb::Errors::Error], Herb::Token, Herb::Token, Herb::Token, String, Array[Herb::AST::Node], Array[Herb::AST::RubyParameterNode], Herb::AST::ERBRescueNode, Herb::AST::ERBElseNode, Herb::AST::ERBEnsureNode, Herb::AST::ERBEndNode) -> void
2152
+ def initialize(type, location, errors, tag_opening, content, tag_closing, prism_node, body, block_arguments, rescue_clause, else_clause, ensure_clause, end_node)
2151
2153
  super(type, location, errors)
2152
2154
  @tag_opening = tag_opening
2153
2155
  @content = content
2154
2156
  @tag_closing = tag_closing
2155
2157
  @prism_node = prism_node
2156
2158
  @body = body
2159
+ @block_arguments = block_arguments
2157
2160
  @rescue_clause = rescue_clause
2158
2161
  @else_clause = else_clause
2159
2162
  @ensure_clause = ensure_clause
@@ -2184,6 +2187,7 @@ module Herb
2184
2187
  tag_closing: tag_closing,
2185
2188
  prism_node: prism_node,
2186
2189
  body: body,
2190
+ block_arguments: block_arguments,
2187
2191
  rescue_clause: rescue_clause,
2188
2192
  else_clause: else_clause,
2189
2193
  ensure_clause: ensure_clause,
@@ -2198,7 +2202,7 @@ module Herb
2198
2202
 
2199
2203
  #: () -> Array[Herb::AST::Node?]
2200
2204
  def child_nodes
2201
- [*(body || []), rescue_clause, else_clause, ensure_clause, end_node]
2205
+ [*(body || []), *(block_arguments || []), rescue_clause, else_clause, ensure_clause, end_node]
2202
2206
  end
2203
2207
 
2204
2208
  #: () -> Array[Herb::AST::Node]
@@ -2242,6 +2246,8 @@ module Herb
2242
2246
  end
2243
2247
  output += white("├── body: ")
2244
2248
  output += inspect_array(body, prefix: "│ ", indent: indent, depth: depth + 1, depth_limit: depth_limit)
2249
+ output += white("├── block_arguments: ")
2250
+ output += inspect_array(block_arguments, prefix: "│ ", indent: indent, depth: depth + 1, depth_limit: depth_limit)
2245
2251
  output += white("├── rescue_clause: ")
2246
2252
  if rescue_clause
2247
2253
  output += "\n"
@@ -3576,12 +3582,7 @@ module Herb
3576
3582
  end
3577
3583
  end
3578
3584
 
3579
- #: type serialized_erb_render_node = {
3580
- #| tag_opening: Herb::Token?,
3581
- #| content: Herb::Token?,
3582
- #| tag_closing: Herb::Token?,
3583
- #| analyzed_ruby: nil,
3584
- #| prism_node: String?,
3585
+ #: type serialized_ruby_render_keywords_node = {
3585
3586
  #| partial: Herb::Token?,
3586
3587
  #| template_path: Herb::Token?,
3587
3588
  #| layout: Herb::Token?,
@@ -3601,14 +3602,9 @@ module Herb
3601
3602
  #| content_type: Herb::Token?,
3602
3603
  #| locals: Array[Herb::AST::RubyRenderLocalNode]?,
3603
3604
  #| }
3604
- class ERBRenderNode < Node
3605
+ class RubyRenderKeywordsNode < Node
3605
3606
  include Colors
3606
3607
 
3607
- attr_reader :tag_opening #: Herb::Token?
3608
- attr_reader :content #: Herb::Token?
3609
- attr_reader :tag_closing #: Herb::Token?
3610
- attr_reader :analyzed_ruby #: nil
3611
- attr_reader :prism_node #: String?
3612
3608
  attr_reader :partial #: Herb::Token?
3613
3609
  attr_reader :template_path #: Herb::Token?
3614
3610
  attr_reader :layout #: Herb::Token?
@@ -3628,14 +3624,9 @@ module Herb
3628
3624
  attr_reader :content_type #: Herb::Token?
3629
3625
  attr_reader :locals #: Array[Herb::AST::RubyRenderLocalNode]?
3630
3626
 
3631
- #: (String, Location, Array[Herb::Errors::Error], Herb::Token, Herb::Token, Herb::Token, nil, String, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Array[Herb::AST::RubyRenderLocalNode]) -> void
3632
- def initialize(type, location, errors, tag_opening, content, tag_closing, analyzed_ruby, prism_node, partial, template_path, layout, file, inline_template, body, plain, html, renderable, collection, object, as_name, spacer_template, formats, variants, handlers, content_type, locals)
3627
+ #: (String, Location, Array[Herb::Errors::Error], Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Array[Herb::AST::RubyRenderLocalNode]) -> void
3628
+ def initialize(type, location, errors, partial, template_path, layout, file, inline_template, body, plain, html, renderable, collection, object, as_name, spacer_template, formats, variants, handlers, content_type, locals)
3633
3629
  super(type, location, errors)
3634
- @tag_opening = tag_opening
3635
- @content = content
3636
- @tag_closing = tag_closing
3637
- @analyzed_ruby = analyzed_ruby
3638
- @prism_node = prism_node
3639
3630
  @partial = partial
3640
3631
  @template_path = template_path
3641
3632
  @layout = layout
@@ -3656,30 +3647,9 @@ module Herb
3656
3647
  @locals = locals
3657
3648
  end
3658
3649
 
3659
- #: () -> Prism::node?
3660
- def deserialized_prism_node
3661
- prism_node = @prism_node
3662
- return nil unless prism_node
3663
- return nil unless source
3664
-
3665
- begin
3666
- require "prism"
3667
- rescue LoadError
3668
- warn "The 'prism' gem is required to deserialize Prism nodes. Add it to your Gemfile or install it with: gem install prism"
3669
- return nil
3670
- end
3671
-
3672
- Prism.load(source, prism_node).value
3673
- end
3674
-
3675
- #: () -> serialized_erb_render_node
3650
+ #: () -> serialized_ruby_render_keywords_node
3676
3651
  def to_hash
3677
3652
  super.merge({
3678
- tag_opening: tag_opening,
3679
- content: content,
3680
- tag_closing: tag_closing,
3681
- analyzed_ruby: analyzed_ruby,
3682
- prism_node: prism_node,
3683
3653
  partial: partial,
3684
3654
  template_path: template_path,
3685
3655
  layout: layout,
@@ -3698,12 +3668,12 @@ module Herb
3698
3668
  handlers: handlers,
3699
3669
  content_type: content_type,
3700
3670
  locals: locals,
3701
- }) #: Herb::serialized_erb_render_node
3671
+ }) #: Herb::serialized_ruby_render_keywords_node
3702
3672
  end
3703
3673
 
3704
3674
  #: (Visitor) -> void
3705
3675
  def accept(visitor)
3706
- visitor.visit_erb_render_node(self)
3676
+ visitor.visit_ruby_render_keywords_node(self)
3707
3677
  end
3708
3678
 
3709
3679
  #: () -> Array[Herb::AST::Node?]
@@ -3736,20 +3706,6 @@ module Herb
3736
3706
 
3737
3707
  output += inspect_errors(prefix: "│ ")
3738
3708
 
3739
- output += white("├── tag_opening: ")
3740
- output += tag_opening ? tag_opening.tree_inspect : magenta("∅")
3741
- output += "\n"
3742
- output += white("├── content: ")
3743
- output += content ? content.tree_inspect : magenta("∅")
3744
- output += "\n"
3745
- output += white("├── tag_closing: ")
3746
- output += tag_closing ? tag_closing.tree_inspect : magenta("∅")
3747
- output += "\n"
3748
- if prism_node && source
3749
- output += white("├── prism_node: ")
3750
- output += Herb::PrismInspect.inspect_prism_serialized(prism_node, source, "│ ")
3751
- output += "\n"
3752
- end
3753
3709
  output += white("├── partial: ")
3754
3710
  output += partial ? partial.tree_inspect : magenta("∅")
3755
3711
  output += "\n"
@@ -3809,42 +3765,222 @@ module Herb
3809
3765
  end
3810
3766
  end
3811
3767
 
3812
- #: type serialized_ruby_strict_local_node = {
3768
+ #: type serialized_erb_render_node = {
3769
+ #| tag_opening: Herb::Token?,
3770
+ #| content: Herb::Token?,
3771
+ #| tag_closing: Herb::Token?,
3772
+ #| analyzed_ruby: nil,
3773
+ #| prism_node: String?,
3774
+ #| keywords: Herb::AST::RubyRenderKeywordsNode?,
3775
+ #| body: Array[Herb::AST::Node],
3776
+ #| block_arguments: Array[Herb::AST::RubyParameterNode]?,
3777
+ #| rescue_clause: Herb::AST::ERBRescueNode?,
3778
+ #| else_clause: Herb::AST::ERBElseNode?,
3779
+ #| ensure_clause: Herb::AST::ERBEnsureNode?,
3780
+ #| end_node: Herb::AST::ERBEndNode?,
3781
+ #| }
3782
+ class ERBRenderNode < Node
3783
+ include Colors
3784
+
3785
+ attr_reader :tag_opening #: Herb::Token?
3786
+ attr_reader :content #: Herb::Token?
3787
+ attr_reader :tag_closing #: Herb::Token?
3788
+ attr_reader :analyzed_ruby #: nil
3789
+ attr_reader :prism_node #: String?
3790
+ attr_reader :keywords #: Herb::AST::RubyRenderKeywordsNode?
3791
+ attr_reader :body #: Array[Herb::AST::Node]
3792
+ attr_reader :block_arguments #: Array[Herb::AST::RubyParameterNode]?
3793
+ attr_reader :rescue_clause #: Herb::AST::ERBRescueNode?
3794
+ attr_reader :else_clause #: Herb::AST::ERBElseNode?
3795
+ attr_reader :ensure_clause #: Herb::AST::ERBEnsureNode?
3796
+ attr_reader :end_node #: Herb::AST::ERBEndNode?
3797
+
3798
+ #: (String, Location, Array[Herb::Errors::Error], Herb::Token, Herb::Token, Herb::Token, nil, String, Herb::AST::RubyRenderKeywordsNode, Array[Herb::AST::Node], Array[Herb::AST::RubyParameterNode], Herb::AST::ERBRescueNode, Herb::AST::ERBElseNode, Herb::AST::ERBEnsureNode, Herb::AST::ERBEndNode) -> void
3799
+ def initialize(type, location, errors, tag_opening, content, tag_closing, analyzed_ruby, prism_node, keywords, body, block_arguments, rescue_clause, else_clause, ensure_clause, end_node)
3800
+ super(type, location, errors)
3801
+ @tag_opening = tag_opening
3802
+ @content = content
3803
+ @tag_closing = tag_closing
3804
+ @analyzed_ruby = analyzed_ruby
3805
+ @prism_node = prism_node
3806
+ @keywords = keywords
3807
+ @body = body
3808
+ @block_arguments = block_arguments
3809
+ @rescue_clause = rescue_clause
3810
+ @else_clause = else_clause
3811
+ @ensure_clause = ensure_clause
3812
+ @end_node = end_node
3813
+ end
3814
+
3815
+ #: () -> Prism::node?
3816
+ def deserialized_prism_node
3817
+ prism_node = @prism_node
3818
+ return nil unless prism_node
3819
+ return nil unless source
3820
+
3821
+ begin
3822
+ require "prism"
3823
+ rescue LoadError
3824
+ warn "The 'prism' gem is required to deserialize Prism nodes. Add it to your Gemfile or install it with: gem install prism"
3825
+ return nil
3826
+ end
3827
+
3828
+ Prism.load(source, prism_node).value
3829
+ end
3830
+
3831
+ #: () -> serialized_erb_render_node
3832
+ def to_hash
3833
+ super.merge({
3834
+ tag_opening: tag_opening,
3835
+ content: content,
3836
+ tag_closing: tag_closing,
3837
+ analyzed_ruby: analyzed_ruby,
3838
+ prism_node: prism_node,
3839
+ keywords: keywords,
3840
+ body: body,
3841
+ block_arguments: block_arguments,
3842
+ rescue_clause: rescue_clause,
3843
+ else_clause: else_clause,
3844
+ ensure_clause: ensure_clause,
3845
+ end_node: end_node,
3846
+ }) #: Herb::serialized_erb_render_node
3847
+ end
3848
+
3849
+ #: (Visitor) -> void
3850
+ def accept(visitor)
3851
+ visitor.visit_erb_render_node(self)
3852
+ end
3853
+
3854
+ #: () -> Array[Herb::AST::Node?]
3855
+ def child_nodes
3856
+ [keywords, *(body || []), *(block_arguments || []), rescue_clause, else_clause, ensure_clause, end_node]
3857
+ end
3858
+
3859
+ #: () -> Array[Herb::AST::Node]
3860
+ def compact_child_nodes
3861
+ child_nodes.compact
3862
+ end
3863
+
3864
+ #: () -> String
3865
+ def inspect
3866
+ tree_inspect.rstrip.gsub(/\s+$/, "")
3867
+ end
3868
+
3869
+ #: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
3870
+ def tree_inspect(indent: 0, depth: 0, depth_limit: 10)
3871
+ output = +""
3872
+
3873
+ output += white("@ #{bold(yellow(node_name.to_s))} #{dimmed("(location: #{location.tree_inspect})")}")
3874
+ output += "\n"
3875
+
3876
+ if depth >= depth_limit
3877
+ output += dimmed("└── [depth limit reached ...]\n\n")
3878
+
3879
+ return output.gsub(/^/, " " * indent)
3880
+ end
3881
+
3882
+ output += inspect_errors(prefix: "│ ")
3883
+
3884
+ output += white("├── tag_opening: ")
3885
+ output += tag_opening ? tag_opening.tree_inspect : magenta("∅")
3886
+ output += "\n"
3887
+ output += white("├── content: ")
3888
+ output += content ? content.tree_inspect : magenta("∅")
3889
+ output += "\n"
3890
+ output += white("├── tag_closing: ")
3891
+ output += tag_closing ? tag_closing.tree_inspect : magenta("∅")
3892
+ output += "\n"
3893
+ if prism_node && source
3894
+ output += white("├── prism_node: ")
3895
+ output += Herb::PrismInspect.inspect_prism_serialized(prism_node, source, "│ ")
3896
+ output += "\n"
3897
+ end
3898
+ output += white("├── keywords: ")
3899
+ if keywords
3900
+ output += "\n"
3901
+ output += "│ └── "
3902
+ output += keywords.tree_inspect(indent: indent, depth: depth + 1, depth_limit: depth_limit).gsub(/^/, " " * (indent + 1)).lstrip.gsub(/^/, "│ ").delete_prefix("│ ")
3903
+ else
3904
+ output += magenta("∅\n")
3905
+ end
3906
+ output += white("├── body: ")
3907
+ output += inspect_array(body, prefix: "│ ", indent: indent, depth: depth + 1, depth_limit: depth_limit)
3908
+ output += white("├── block_arguments: ")
3909
+ output += inspect_array(block_arguments, prefix: "│ ", indent: indent, depth: depth + 1, depth_limit: depth_limit)
3910
+ output += white("├── rescue_clause: ")
3911
+ if rescue_clause
3912
+ output += "\n"
3913
+ output += "│ └── "
3914
+ output += rescue_clause.tree_inspect(indent: indent, depth: depth + 1, depth_limit: depth_limit).gsub(/^/, " " * (indent + 1)).lstrip.gsub(/^/, "│ ").delete_prefix("│ ")
3915
+ else
3916
+ output += magenta("∅\n")
3917
+ end
3918
+ output += white("├── else_clause: ")
3919
+ if else_clause
3920
+ output += "\n"
3921
+ output += "│ └── "
3922
+ output += else_clause.tree_inspect(indent: indent, depth: depth + 1, depth_limit: depth_limit).gsub(/^/, " " * (indent + 1)).lstrip.gsub(/^/, "│ ").delete_prefix("│ ")
3923
+ else
3924
+ output += magenta("∅\n")
3925
+ end
3926
+ output += white("├── ensure_clause: ")
3927
+ if ensure_clause
3928
+ output += "\n"
3929
+ output += "│ └── "
3930
+ output += ensure_clause.tree_inspect(indent: indent, depth: depth + 1, depth_limit: depth_limit).gsub(/^/, " " * (indent + 1)).lstrip.gsub(/^/, "│ ").delete_prefix("│ ")
3931
+ else
3932
+ output += magenta("∅\n")
3933
+ end
3934
+ output += white("└── end_node: ")
3935
+ if end_node
3936
+ output += "\n"
3937
+ output += " └── "
3938
+ output += end_node.tree_inspect(indent: indent, depth: depth + 1, depth_limit: depth_limit).gsub(/^/, " " * (indent + 1)).lstrip.gsub(/^/, " ").delete_prefix(" ")
3939
+ else
3940
+ output += magenta("∅\n")
3941
+ end
3942
+ output += "\n"
3943
+
3944
+ output.gsub(/^/, " " * indent)
3945
+ end
3946
+ end
3947
+
3948
+ #: type serialized_ruby_parameter_node = {
3813
3949
  #| name: Herb::Token?,
3814
3950
  #| default_value: Herb::AST::RubyLiteralNode?,
3951
+ #| kind: String?,
3815
3952
  #| required: bool,
3816
- #| double_splat: bool,
3817
3953
  #| }
3818
- class RubyStrictLocalNode < Node
3954
+ class RubyParameterNode < Node
3819
3955
  include Colors
3820
3956
 
3821
3957
  attr_reader :name #: Herb::Token?
3822
3958
  attr_reader :default_value #: Herb::AST::RubyLiteralNode?
3959
+ attr_reader :kind #: String?
3823
3960
  attr_reader :required #: bool
3824
- attr_reader :double_splat #: bool
3825
3961
 
3826
- #: (String, Location, Array[Herb::Errors::Error], Herb::Token, Herb::AST::RubyLiteralNode, bool, bool) -> void
3827
- def initialize(type, location, errors, name, default_value, required, double_splat)
3962
+ #: (String, Location, Array[Herb::Errors::Error], Herb::Token, Herb::AST::RubyLiteralNode, String, bool) -> void
3963
+ def initialize(type, location, errors, name, default_value, kind, required)
3828
3964
  super(type, location, errors)
3829
3965
  @name = name
3830
3966
  @default_value = default_value
3967
+ @kind = kind&.force_encoding("utf-8")
3831
3968
  @required = required
3832
- @double_splat = double_splat
3833
3969
  end
3834
3970
 
3835
- #: () -> serialized_ruby_strict_local_node
3971
+ #: () -> serialized_ruby_parameter_node
3836
3972
  def to_hash
3837
3973
  super.merge({
3838
3974
  name: name,
3839
3975
  default_value: default_value,
3976
+ kind: kind,
3840
3977
  required: required,
3841
- double_splat: double_splat,
3842
- }) #: Herb::serialized_ruby_strict_local_node
3978
+ }) #: Herb::serialized_ruby_parameter_node
3843
3979
  end
3844
3980
 
3845
3981
  #: (Visitor) -> void
3846
3982
  def accept(visitor)
3847
- visitor.visit_ruby_strict_local_node(self)
3983
+ visitor.visit_ruby_parameter_node(self)
3848
3984
  end
3849
3985
 
3850
3986
  #: () -> Array[Herb::AST::Node?]
@@ -3888,12 +4024,10 @@ module Herb
3888
4024
  else
3889
4025
  output += magenta("∅\n")
3890
4026
  end
3891
- output += white("├── required: ")
4027
+ output += white("├── kind: ") + green("#{kind.inspect}\n")
4028
+ output += white("└── required: ")
3892
4029
  output += [true, false].include?(required) ? bold(magenta(required.to_s)) : magenta("∅")
3893
4030
  output += "\n"
3894
- output += white("└── double_splat: ")
3895
- output += [true, false].include?(double_splat) ? bold(magenta(double_splat.to_s)) : magenta("∅")
3896
- output += "\n"
3897
4031
  output += "\n"
3898
4032
 
3899
4033
  output.gsub(/^/, " " * indent)
@@ -3906,7 +4040,7 @@ module Herb
3906
4040
  #| tag_closing: Herb::Token?,
3907
4041
  #| analyzed_ruby: nil,
3908
4042
  #| prism_node: String?,
3909
- #| locals: Array[Herb::AST::RubyStrictLocalNode]?,
4043
+ #| locals: Array[Herb::AST::RubyParameterNode]?,
3910
4044
  #| }
3911
4045
  class ERBStrictLocalsNode < Node
3912
4046
  include Colors
@@ -3916,9 +4050,9 @@ module Herb
3916
4050
  attr_reader :tag_closing #: Herb::Token?
3917
4051
  attr_reader :analyzed_ruby #: nil
3918
4052
  attr_reader :prism_node #: String?
3919
- attr_reader :locals #: Array[Herb::AST::RubyStrictLocalNode]?
4053
+ attr_reader :locals #: Array[Herb::AST::RubyParameterNode]?
3920
4054
 
3921
- #: (String, Location, Array[Herb::Errors::Error], Herb::Token, Herb::Token, Herb::Token, nil, String, Array[Herb::AST::RubyStrictLocalNode]) -> void
4055
+ #: (String, Location, Array[Herb::Errors::Error], Herb::Token, Herb::Token, Herb::Token, nil, String, Array[Herb::AST::RubyParameterNode]) -> void
3922
4056
  def initialize(type, location, errors, tag_opening, content, tag_closing, analyzed_ruby, prism_node, locals)
3923
4057
  super(type, location, errors)
3924
4058
  @tag_opening = tag_opening
@@ -5,6 +5,12 @@ module Herb
5
5
  class Compiler < ::Herb::Visitor
6
6
  EXPRESSION_TOKEN_TYPES = [:expr, :expr_escaped, :expr_block, :expr_block_escaped].freeze
7
7
 
8
+ TRAILING_WHITESPACE = /[ \t]+\z/
9
+ TRAILING_INDENTATION = /\n[ \t]+\z/
10
+ TRAILING_INDENTATION_CAPTURE = /\n([ \t]+)\z/
11
+ WHITESPACE_ONLY = /\A[ \t]+\z/
12
+ WHITESPACE_ONLY_CAPTURE = /\A([ \t]+)\z/
13
+
8
14
  attr_reader :tokens
9
15
 
10
16
  def initialize(engine, options = {})
@@ -93,11 +99,9 @@ module Herb
93
99
  def visit_html_attribute_value_node(node)
94
100
  push_context(:attribute_value)
95
101
 
96
- add_text(node.open_quote&.value) if node.quoted
97
-
102
+ add_text(node.open_quote&.value || '"') if node.quoted
98
103
  visit_all(node.children)
99
-
100
- add_text(node.close_quote&.value) if node.quoted
104
+ add_text(node.close_quote&.value || '"') if node.quoted
101
105
 
102
106
  pop_context
103
107
  end
@@ -150,9 +154,9 @@ module Herb
150
154
  end
151
155
 
152
156
  def visit_cdata_node(node)
153
- add_text(node.cdata_opening.value)
157
+ add_text(node.tag_opening.value)
154
158
  visit_all(node.children)
155
- add_text(node.cdata_closing.value)
159
+ add_text(node.tag_closing.value)
156
160
  end
157
161
 
158
162
  def visit_erb_content_node(node)
@@ -242,6 +246,8 @@ module Herb
242
246
  def visit_erb_block_node(node)
243
247
  opening = node.tag_opening.value
244
248
 
249
+ check_for_escaped_erb_tag!(opening)
250
+
245
251
  if opening.include?("=")
246
252
  should_escape = should_escape_output?(opening)
247
253
  code = node.content.value.strip
@@ -251,7 +257,9 @@ module Herb
251
257
  else
252
258
  [:expr_block, code, current_context]
253
259
  end
260
+
254
261
  @last_trim_consumed_newline = false
262
+ @trim_next_whitespace = true if right_trim?(node)
255
263
 
256
264
  visit_all(node.body)
257
265
  visit_erb_block_end_node(node.end_node, escaped: should_escape)
@@ -267,9 +275,7 @@ module Herb
267
275
  end
268
276
 
269
277
  def visit_erb_block_end_node(node, escaped: false)
270
- has_left_trim = node.tag_opening.value.start_with?("<%-")
271
-
272
- remove_trailing_whitespace_from_last_token! if has_left_trim
278
+ remove_trailing_whitespace_from_last_token! if left_trim?(node)
273
279
 
274
280
  code = node.content.value.strip
275
281
 
@@ -302,6 +308,15 @@ module Herb
302
308
 
303
309
  private
304
310
 
311
+ def check_for_escaped_erb_tag!(opening)
312
+ return unless opening.start_with?("<%%")
313
+
314
+ raise Herb::Engine::GeneratorTemplateError,
315
+ "This file appears to be a generator template (a template used to generate ERB files) " \
316
+ "rather than a standard ERB template. It contains escaped ERB tags like <%%= %> which " \
317
+ "produce literal ERB output in the generated file."
318
+ end
319
+
305
320
  def current_context
306
321
  @context_stack.last
307
322
  end
@@ -336,10 +351,11 @@ module Herb
336
351
  def process_erb_tag(node, skip_comment_check: false)
337
352
  opening = node.tag_opening.value
338
353
 
354
+ check_for_escaped_erb_tag!(opening)
355
+
339
356
  if !skip_comment_check && erb_comment?(opening)
340
- has_left_trim = opening.start_with?("<%-")
341
357
  follows_newline = leading_space_follows_newline?
342
- remove_trailing_whitespace_from_last_token! if has_left_trim
358
+ remove_trailing_whitespace_from_last_token! if left_trim?(node)
343
359
 
344
360
  if at_line_start?
345
361
  leading_space = extract_and_remove_leading_space!
@@ -485,10 +501,9 @@ module Herb
485
501
  @last_trim_consumed_newline = false
486
502
  end
487
503
 
488
- has_right_trim = node.tag_closing&.value == "-%>"
489
504
  should_escape = should_escape_output?(opening)
490
505
  add_expression_with_escaping(code, should_escape)
491
- @trim_next_whitespace = true if has_right_trim
506
+ @trim_next_whitespace = true if right_trim?(node)
492
507
  end
493
508
 
494
509
  def indicator_for(type)
@@ -521,7 +536,7 @@ module Herb
521
536
  last_value = @tokens.last[1]
522
537
 
523
538
  if last_type == :text
524
- last_value.empty? || last_value.end_with?("\n") || (last_value =~ /\A[ \t]+\z/ && preceding_token_ends_with_newline?) || last_value =~ /\n[ \t]+\z/
539
+ last_value.empty? || last_value.end_with?("\n") || (last_value =~ WHITESPACE_ONLY && preceding_token_ends_with_newline?) || last_value =~ TRAILING_INDENTATION
525
540
  elsif EXPRESSION_TOKEN_TYPES.include?(last_type)
526
541
  @last_trim_consumed_newline
527
542
  else
@@ -540,6 +555,14 @@ module Herb
540
555
  preceding[1].end_with?("\n")
541
556
  end
542
557
 
558
+ def left_trim?(node)
559
+ node.tag_opening.value == "<%-"
560
+ end
561
+
562
+ def right_trim?(node)
563
+ node.tag_closing&.value == "-%>"
564
+ end
565
+
543
566
  def last_text_token
544
567
  return unless @tokens.last && @tokens.last[0] == :text
545
568
 
@@ -552,7 +575,7 @@ module Herb
552
575
 
553
576
  text = token[1]
554
577
 
555
- return Regexp.last_match(1) if text =~ /\n([ \t]+)\z/ || text =~ /\A([ \t]+)\z/
578
+ return Regexp.last_match(1) if text =~ TRAILING_INDENTATION_CAPTURE || text =~ WHITESPACE_ONLY_CAPTURE
556
579
 
557
580
  ""
558
581
  end
@@ -561,7 +584,12 @@ module Herb
561
584
  token = last_text_token
562
585
  return false unless token
563
586
 
564
- token[1].match?(/\n[ \t]+\z/)
587
+ text = token[1]
588
+
589
+ return true if text.match?(TRAILING_INDENTATION)
590
+ return true if @last_trim_consumed_newline && text.match?(WHITESPACE_ONLY)
591
+
592
+ false
565
593
  end
566
594
 
567
595
  def extract_and_remove_leading_space!
@@ -570,9 +598,9 @@ module Herb
570
598
 
571
599
  text = @tokens.last[1]
572
600
 
573
- if text =~ /\n[ \t]+\z/
574
- text.sub!(/[ \t]+\z/, "")
575
- elsif text =~ /\A[ \t]+\z/
601
+ if text =~ TRAILING_INDENTATION
602
+ text.sub!(TRAILING_WHITESPACE, "")
603
+ elsif text =~ WHITESPACE_ONLY
576
604
  text.replace("")
577
605
  end
578
606
 
@@ -582,10 +610,8 @@ module Herb
582
610
  end
583
611
 
584
612
  def apply_trim(node, code)
585
- has_left_trim = node.tag_opening.value.start_with?("<%-")
586
-
587
613
  follows_newline = leading_space_follows_newline?
588
- removed_whitespace = has_left_trim ? remove_trailing_whitespace_from_last_token! : ""
614
+ removed_whitespace = left_trim?(node) ? remove_trailing_whitespace_from_last_token! : ""
589
615
 
590
616
  if at_line_start?
591
617
  leading_space = extract_and_remove_leading_space!
@@ -617,12 +643,12 @@ module Herb
617
643
  return "" unless token
618
644
 
619
645
  text = token[1]
620
- removed = text[/[ \t]+\z/] || ""
646
+ removed = text[TRAILING_WHITESPACE] || ""
621
647
 
622
- if text =~ /\n[ \t]+\z/
623
- text.sub!(/[ \t]+\z/, "")
648
+ if text =~ TRAILING_INDENTATION
649
+ text.sub!(TRAILING_WHITESPACE, "")
624
650
  token[1] = text
625
- elsif text =~ /\A[ \t]+\z/
651
+ elsif text =~ WHITESPACE_ONLY
626
652
  text.replace("")
627
653
  token[1] = text
628
654
  end
data/lib/herb/engine.rb CHANGED
@@ -31,6 +31,9 @@ module Herb
31
31
  class CompilationError < StandardError
32
32
  end
33
33
 
34
+ class GeneratorTemplateError < CompilationError
35
+ end
36
+
34
37
  class InvalidRubyError < CompilationError
35
38
  attr_reader :compiled_source
36
39