herb 0.9.5 → 0.9.7

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/ext/herb/extension.c +8 -0
  3. data/ext/herb/extension_helpers.c +1 -0
  4. data/lib/herb/action_view/helper_registry.rb +8107 -0
  5. data/lib/herb/parser_options.rb +7 -2
  6. data/lib/herb/project.rb +2 -5
  7. data/lib/herb/version.rb +1 -1
  8. data/sig/herb/action_view/helper_registry.rbs +1144 -0
  9. data/sig/herb/parser_options.rbs +6 -2
  10. data/src/analyze/action_view/generated_handlers.c +355 -0
  11. data/src/analyze/action_view/generated_handlers.h +16 -0
  12. data/src/analyze/action_view/helper_registry.c +7244 -0
  13. data/src/analyze/action_view/image_tag.c +4 -31
  14. data/src/analyze/action_view/javascript_include_tag.c +1 -42
  15. data/src/analyze/action_view/javascript_tag.c +0 -52
  16. data/src/analyze/action_view/registry.c +2 -2
  17. data/src/analyze/action_view/tag_helpers.c +8 -120
  18. data/src/analyze/action_view/turbo_frame_tag.c +1 -36
  19. data/src/analyze/analyze.c +7 -0
  20. data/src/analyze/postfix_conditionals.c +326 -0
  21. data/src/analyze/ternary_conditionals.c +265 -0
  22. data/src/include/analyze/action_view/helper_registry.h +325 -0
  23. data/src/include/analyze/action_view/tag_helpers.h +0 -1
  24. data/src/include/analyze/postfix_conditionals.h +9 -0
  25. data/src/include/analyze/ternary_conditionals.h +15 -0
  26. data/src/include/parser/parser.h +1 -0
  27. data/src/include/version.h +1 -1
  28. data/src/parser.c +1 -0
  29. data/templates/java/org/herb/ast/HelperRegistry.java.erb +258 -0
  30. data/templates/javascript/packages/core/src/action-view-helpers.ts.erb +171 -0
  31. data/templates/javascript/packages/core/src/nodes.ts.erb +5 -1
  32. data/templates/lib/herb/action_view/helper_registry.rb.erb +288 -0
  33. data/templates/rust/src/action_view_helpers.rs.erb +154 -0
  34. data/templates/src/analyze/action_view/generated_handlers.c.erb +230 -0
  35. data/templates/src/analyze/action_view/generated_handlers.h.erb +12 -0
  36. data/templates/src/analyze/action_view/helper_registry.c.erb +114 -0
  37. data/templates/src/include/analyze/action_view/helper_registry.h.erb +82 -0
  38. data/templates/template.rb +338 -1
  39. metadata +19 -3
  40. data/src/analyze/action_view/content_tag.c +0 -78
  41. data/src/analyze/action_view/tag.c +0 -87
@@ -449,6 +449,337 @@ module Herb
449
449
  end
450
450
  end
451
451
 
452
+ def self.escape_string(string)
453
+ string.to_s.gsub(/["\\]/) do |char|
454
+ case char
455
+ when "\\" then "\\\\"
456
+ when '"' then '\\"'
457
+ else char
458
+ end
459
+ end
460
+ end
461
+
462
+ class HelperArgument
463
+ attr_reader :name, :position, :type, :optional, :default, :splat, :description
464
+
465
+ def initialize(config)
466
+ @name = config.fetch("name")
467
+ @position = config.fetch("position")
468
+ @type = Array(config.fetch("type"))
469
+ @optional = config.fetch("optional", false)
470
+ @default = config.fetch("default", nil)
471
+ @splat = config.fetch("splat", false)
472
+ @description = config.fetch("description", "")
473
+ end
474
+
475
+ def type_display
476
+ @type.join(" | ")
477
+ end
478
+
479
+ def escaped_description
480
+ Template.escape_string(@description)
481
+ end
482
+
483
+ def escaped_default
484
+ @default ? Template.escape_string(@default) : nil
485
+ end
486
+ end
487
+
488
+ class HelperOption
489
+ attr_reader :name, :type, :maps_to, :description
490
+
491
+ def initialize(config)
492
+ @name = config.fetch("name")
493
+ @type = Array(config.fetch("type"))
494
+ @maps_to = config.fetch("maps_to", nil)
495
+ @description = config.fetch("description", "")
496
+ end
497
+
498
+ def type_display
499
+ @type.join(" | ")
500
+ end
501
+
502
+ def escaped_description
503
+ Template.escape_string(@description)
504
+ end
505
+ end
506
+
507
+ class HelperImplicitAttribute
508
+ attr_reader :name, :source, :source_with_block, :wrapper, :skip_wrapping_for,
509
+ :wrapper_quotes_arg
510
+
511
+ def initialize(config)
512
+ @name = config.fetch("name")
513
+ @source = config.fetch("source")
514
+ @source_with_block = config.fetch("source_with_block", nil)
515
+ @wrapper = config.fetch("wrapper")
516
+ @wrapper_quotes_arg = config.fetch("wrapper_quotes_arg", false)
517
+ @skip_wrapping_for = config.fetch("skip_wrapping_for", [])
518
+ end
519
+ end
520
+
521
+ class HelperContent
522
+ attr_reader :source, :arg_position, :skip_if_hash, :to_s_suffix_when_single
523
+
524
+ def initialize(config)
525
+ @source = config.fetch("source")
526
+ @arg_position = config.fetch("arg_position", nil)
527
+ @skip_if_hash = config.fetch("skip_if_hash", false)
528
+ @to_s_suffix_when_single = config.fetch("to_s_suffix_when_single", false)
529
+ end
530
+
531
+ def null?
532
+ false
533
+ end
534
+
535
+ def first_arg?
536
+ @source == "first_arg"
537
+ end
538
+
539
+ def block_or_arg?
540
+ @source == "block_or_arg"
541
+ end
542
+ end
543
+
544
+ class HelperSpecialBehavior
545
+ attr_reader :type, :config
546
+
547
+ def initialize(config)
548
+ if config.is_a?(String)
549
+ @type = config
550
+ @config = {}
551
+ else
552
+ @type = config.fetch("type")
553
+ @config = config
554
+ end
555
+ end
556
+
557
+ def implied_attribute?
558
+ @type == "implied_attribute"
559
+ end
560
+
561
+ def wrap_body?
562
+ @type == "wrap_body"
563
+ end
564
+
565
+ def multiple_elements?
566
+ @type == "multiple_elements"
567
+ end
568
+
569
+ def size_to_dimensions?
570
+ @type == "size_to_dimensions"
571
+ end
572
+
573
+ def remove_non_html_options?
574
+ @type == "remove_non_html_options"
575
+ end
576
+
577
+ def [](key)
578
+ @config[key.to_s]
579
+ end
580
+
581
+ def to_s
582
+ @type
583
+ end
584
+ end
585
+
586
+ class HelperTagInfo
587
+ attr_reader :name, :is_void, :preferred, :detect_style, :implicit_attribute
588
+
589
+ def initialize(config)
590
+ @name = config.fetch("name")
591
+ @is_void = config.fetch("is_void", false)
592
+ @preferred = config.fetch("preferred", false)
593
+ @detect_style = config.fetch("detect_style", nil)
594
+
595
+ implicit_config = config.fetch("implicit_attribute", nil)
596
+ @implicit_attribute = implicit_config ? HelperImplicitAttribute.new(implicit_config) : nil
597
+ end
598
+
599
+ def preferred?
600
+ @preferred
601
+ end
602
+
603
+ def implicit_attribute?
604
+ !@implicit_attribute.nil?
605
+ end
606
+
607
+ def call_name_detect?
608
+ @detect_style == "call_name"
609
+ end
610
+
611
+ def receiver_call_detect?
612
+ @detect_style == "receiver_call"
613
+ end
614
+ end
615
+
616
+ class HelperType
617
+ attr_reader :name, :source, :gem, :output, :visibility, :supports_block,
618
+ :supported, :description, :signature, :documentation_url, :tag,
619
+ :content, :attributes_arg, :attributes_arg_with_block,
620
+ :transform_style, :custom_transform,
621
+ :arguments, :options, :special_behaviors, :aliases
622
+
623
+ def initialize(config)
624
+ @name = config.fetch("name")
625
+ @source = config.fetch("source")
626
+ @gem = config.fetch("gem")
627
+ @output = config.fetch("output", "html")
628
+ @visibility = config.fetch("visibility", "public")
629
+ @supports_block = config.fetch("supports_block", false)
630
+ @supported = config.fetch("supported", false)
631
+ @description = config.fetch("description", "").strip
632
+ @signature = config.fetch("signature")
633
+ @documentation_url = config.fetch("documentation_url")
634
+
635
+ tag_config = config.fetch("tag", nil)
636
+ @tag = tag_config ? HelperTagInfo.new(tag_config) : nil
637
+
638
+ content_config = config.fetch("content", nil)
639
+ @content = content_config ? HelperContent.new(content_config) : nil
640
+
641
+ @attributes_arg = config.fetch("attributes_arg", nil)
642
+ @attributes_arg_with_block = config.fetch("attributes_arg_with_block", nil)
643
+ @transform_style = config.fetch("transform_style", "generic")
644
+ @custom_transform = config.fetch("custom_transform", nil)
645
+
646
+ @arguments = (config.fetch("arguments", []) || []).map { |arg| HelperArgument.new(arg) }
647
+ @options = (config.fetch("options", []) || []).map { |opt| HelperOption.new(opt) }
648
+
649
+ raw_behaviors = config.fetch("special_behaviors", []) || []
650
+ @special_behaviors = raw_behaviors.map { |b| HelperSpecialBehavior.new(b) }
651
+ @aliases = config.fetch("aliases", []) || []
652
+ end
653
+
654
+ def tag_name
655
+ @tag&.name
656
+ end
657
+
658
+ def void?
659
+ @tag&.is_void || false
660
+ end
661
+
662
+ def preferred_for_tag
663
+ @tag&.preferred || false
664
+ end
665
+
666
+ def detect_style
667
+ @tag&.detect_style
668
+ end
669
+
670
+ def implicit_attribute
671
+ @tag&.implicit_attribute
672
+ end
673
+
674
+ # "ActionView::Helpers::UrlHelper#link_to" => "UrlHelper"
675
+ def module_name
676
+ source.split("#").first.split("::").last
677
+ end
678
+
679
+ # "ActionView::Helpers::UrlHelper#link_to" => "url_helper"
680
+ def module_key
681
+ module_name.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase
682
+ end
683
+
684
+ # "link_to" => "link_to", "current_page?" => "current_page_p", "uncacheable!" => "uncacheable_bang"
685
+ def safe_name
686
+ name.gsub("?", "_p").gsub("!", "_bang")
687
+ end
688
+
689
+ # "link_to" => "LINK_TO", "current_page?" => "CURRENT_PAGE_P", "content_for?" => "CONTENT_FOR_P"
690
+ def constant_name
691
+ safe_name.upcase
692
+ end
693
+
694
+ # "link_to" => "LinkTo", "current_page?" => "CurrentPage"
695
+ def camel_case_name
696
+ safe_name.split("_").map(&:capitalize).join
697
+ end
698
+
699
+ # "link_to" => "linkTo", "current_page?" => "currentPage"
700
+ def lower_camel_case_name
701
+ parts = safe_name.split("_")
702
+
703
+ parts.first + parts[1..].map(&:capitalize).join
704
+ end
705
+
706
+ def escaped_description
707
+ Template.escape_string(@description)
708
+ end
709
+
710
+ def escaped_signature
711
+ Template.escape_string(@signature)
712
+ end
713
+
714
+ def public?
715
+ @visibility == "public"
716
+ end
717
+
718
+ def internal?
719
+ @visibility == "internal"
720
+ end
721
+
722
+ def tag?
723
+ !@tag.nil?
724
+ end
725
+
726
+ def preferred_for_tag?
727
+ @tag&.preferred || false
728
+ end
729
+
730
+ def supported?
731
+ @supported
732
+ end
733
+
734
+ def static_tag_name?
735
+ !tag_name.nil?
736
+ end
737
+
738
+ def implicit_attribute?
739
+ !implicit_attribute.nil?
740
+ end
741
+
742
+ def call_name_detect?
743
+ @tag&.call_name_detect? || false
744
+ end
745
+
746
+ def receiver_call_detect?
747
+ @tag&.receiver_call_detect? || false
748
+ end
749
+
750
+ def html_output?
751
+ @output == "html"
752
+ end
753
+
754
+ def text_output?
755
+ @output == "text"
756
+ end
757
+
758
+ def url_output?
759
+ @output == "url"
760
+ end
761
+
762
+ def boolean_output?
763
+ @output == "boolean"
764
+ end
765
+
766
+ def void_output?
767
+ @output == "void"
768
+ end
769
+
770
+ def content?
771
+ !@content.nil?
772
+ end
773
+
774
+ def generic_transform?
775
+ @transform_style == "generic"
776
+ end
777
+
778
+ def custom_transform?
779
+ @transform_style == "custom"
780
+ end
781
+ end
782
+
452
783
  class PrintfMessageTemplate
453
784
  MAX_STRING_SIZE = 128
454
785
 
@@ -541,7 +872,7 @@ module Herb
541
872
  )
542
873
  end
543
874
 
544
- rendered_template = read_template(template_path.to_s).result_with_hash({ nodes: nodes, errors: errors, union_kinds: union_kinds })
875
+ rendered_template = read_template(template_path.to_s).result_with_hash({ nodes: nodes, errors: errors, union_kinds: union_kinds, helpers: helpers })
545
876
  content = heading_for(name, template_file) + rendered_template
546
877
 
547
878
  check_gitignore(name)
@@ -604,6 +935,12 @@ module Herb
604
935
  (config.dig("errors", "types") || []).map { |node| ErrorType.new(node) }
605
936
  end
606
937
 
938
+ def self.helpers
939
+ Dir.glob("config/action_view_helpers/**/*.yml").map do |file|
940
+ HelperType.new(YAML.load_file(file))
941
+ end
942
+ end
943
+
607
944
  def self.config
608
945
  YAML.load_file("config.yml")
609
946
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: herb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.5
4
+ version: 0.9.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marco Roth
@@ -35,6 +35,7 @@ files:
35
35
  - ext/herb/nodes.h
36
36
  - herb.gemspec
37
37
  - lib/herb.rb
38
+ - lib/herb/action_view/helper_registry.rb
38
39
  - lib/herb/ast.rb
39
40
  - lib/herb/ast/erb_content_node.rb
40
41
  - lib/herb/ast/helpers.rb
@@ -72,6 +73,7 @@ files:
72
73
  - lib/herb/visitor.rb
73
74
  - lib/herb/warnings.rb
74
75
  - sig/herb.rbs
76
+ - sig/herb/action_view/helper_registry.rbs
75
77
  - sig/herb/ast.rbs
76
78
  - sig/herb/ast/erb_content_node.rbs
77
79
  - sig/herb/ast/helpers.rbs
@@ -112,13 +114,14 @@ files:
112
114
  - sig/serialized_ast_errors.rbs
113
115
  - sig/serialized_ast_nodes.rbs
114
116
  - src/analyze/action_view/attribute_extraction_helpers.c
115
- - src/analyze/action_view/content_tag.c
117
+ - src/analyze/action_view/generated_handlers.c
118
+ - src/analyze/action_view/generated_handlers.h
119
+ - src/analyze/action_view/helper_registry.c
116
120
  - src/analyze/action_view/image_tag.c
117
121
  - src/analyze/action_view/javascript_include_tag.c
118
122
  - src/analyze/action_view/javascript_tag.c
119
123
  - src/analyze/action_view/link_to.c
120
124
  - src/analyze/action_view/registry.c
121
- - src/analyze/action_view/tag.c
122
125
  - src/analyze/action_view/tag_helper_node_builders.c
123
126
  - src/analyze/action_view/tag_helpers.c
124
127
  - src/analyze/action_view/turbo_frame_tag.c
@@ -132,9 +135,11 @@ files:
132
135
  - src/analyze/invalid_structures.c
133
136
  - src/analyze/missing_end.c
134
137
  - src/analyze/parse_errors.c
138
+ - src/analyze/postfix_conditionals.c
135
139
  - src/analyze/prism_annotate.c
136
140
  - src/analyze/render_nodes.c
137
141
  - src/analyze/strict_locals.c
142
+ - src/analyze/ternary_conditionals.c
138
143
  - src/analyze/transform.c
139
144
  - src/ast/ast_node.c
140
145
  - src/ast/ast_nodes.c
@@ -144,6 +149,7 @@ files:
144
149
  - src/extract.c
145
150
  - src/herb.c
146
151
  - src/include/analyze/action_view/attribute_extraction_helpers.h
152
+ - src/include/analyze/action_view/helper_registry.h
147
153
  - src/include/analyze/action_view/tag_helper_handler.h
148
154
  - src/include/analyze/action_view/tag_helper_node_builders.h
149
155
  - src/include/analyze/action_view/tag_helpers.h
@@ -155,9 +161,11 @@ files:
155
161
  - src/include/analyze/control_type.h
156
162
  - src/include/analyze/helpers.h
157
163
  - src/include/analyze/invalid_structures.h
164
+ - src/include/analyze/postfix_conditionals.h
158
165
  - src/include/analyze/prism_annotate.h
159
166
  - src/include/analyze/render_nodes.h
160
167
  - src/include/analyze/strict_locals.h
168
+ - src/include/analyze/ternary_conditionals.h
161
169
  - src/include/ast/ast_node.h
162
170
  - src/include/ast/ast_nodes.h
163
171
  - src/include/ast/ast_pretty_print.h
@@ -236,9 +244,11 @@ files:
236
244
  - templates/java/nodes.c.erb
237
245
  - templates/java/nodes.h.erb
238
246
  - templates/java/org/herb/ast/Errors.java.erb
247
+ - templates/java/org/herb/ast/HelperRegistry.java.erb
239
248
  - templates/java/org/herb/ast/NodeVisitor.java.erb
240
249
  - templates/java/org/herb/ast/Nodes.java.erb
241
250
  - templates/java/org/herb/ast/Visitor.java.erb
251
+ - templates/javascript/packages/core/src/action-view-helpers.ts.erb
242
252
  - templates/javascript/packages/core/src/errors.ts.erb
243
253
  - templates/javascript/packages/core/src/node-type-guards.ts.erb
244
254
  - templates/javascript/packages/core/src/nodes.ts.erb
@@ -247,9 +257,11 @@ files:
247
257
  - templates/javascript/packages/node/extension/error_helpers.h.erb
248
258
  - templates/javascript/packages/node/extension/nodes.cpp.erb
249
259
  - templates/javascript/packages/node/extension/nodes.h.erb
260
+ - templates/lib/herb/action_view/helper_registry.rb.erb
250
261
  - templates/lib/herb/ast/nodes.rb.erb
251
262
  - templates/lib/herb/errors.rb.erb
252
263
  - templates/lib/herb/visitor.rb.erb
264
+ - templates/rust/src/action_view_helpers.rs.erb
253
265
  - templates/rust/src/ast/nodes.rs.erb
254
266
  - templates/rust/src/errors.rs.erb
255
267
  - templates/rust/src/nodes.rs.erb
@@ -257,11 +269,15 @@ files:
257
269
  - templates/rust/src/visitor.rs.erb
258
270
  - templates/sig/serialized_ast_errors.rbs.erb
259
271
  - templates/sig/serialized_ast_nodes.rbs.erb
272
+ - templates/src/analyze/action_view/generated_handlers.c.erb
273
+ - templates/src/analyze/action_view/generated_handlers.h.erb
274
+ - templates/src/analyze/action_view/helper_registry.c.erb
260
275
  - templates/src/analyze/missing_end.c.erb
261
276
  - templates/src/analyze/transform.c.erb
262
277
  - templates/src/ast/ast_nodes.c.erb
263
278
  - templates/src/ast/ast_pretty_print.c.erb
264
279
  - templates/src/errors.c.erb
280
+ - templates/src/include/analyze/action_view/helper_registry.h.erb
265
281
  - templates/src/include/ast/ast_nodes.h.erb
266
282
  - templates/src/include/ast/ast_pretty_print.h.erb
267
283
  - templates/src/include/errors.h.erb
@@ -1,78 +0,0 @@
1
- #include "../../include/analyze/action_view/tag_helper_handler.h"
2
-
3
- #include <prism.h>
4
- #include <stdbool.h>
5
- #include <stdlib.h>
6
- #include <string.h>
7
-
8
- bool detect_content_tag(pm_call_node_t* call_node, pm_parser_t* parser) {
9
- if (!call_node || !call_node->name) { return false; }
10
-
11
- pm_constant_t* constant = pm_constant_pool_id_to_constant(&parser->constant_pool, call_node->name);
12
- return constant && constant->length == 11 && strncmp((const char*) constant->start, "content_tag", 11) == 0;
13
- }
14
-
15
- char* extract_content_tag_name(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
16
- (void) parser;
17
-
18
- if (!call_node || !call_node->arguments) { return NULL; }
19
-
20
- pm_arguments_node_t* arguments = call_node->arguments;
21
- if (!arguments->arguments.size) { return NULL; }
22
-
23
- pm_node_t* first_argument = arguments->arguments.nodes[0];
24
-
25
- if (first_argument->type == PM_STRING_NODE) {
26
- pm_string_node_t* string_node = (pm_string_node_t*) first_argument;
27
- size_t length = pm_string_length(&string_node->unescaped);
28
- return hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
29
- } else if (first_argument->type == PM_SYMBOL_NODE) {
30
- pm_symbol_node_t* symbol_node = (pm_symbol_node_t*) first_argument;
31
- size_t length = pm_string_length(&symbol_node->unescaped);
32
- return hb_allocator_strndup(allocator, (const char*) pm_string_source(&symbol_node->unescaped), length);
33
- }
34
-
35
- return NULL;
36
- }
37
-
38
- char* extract_content_tag_content(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
39
- (void) parser;
40
-
41
- if (!call_node) { return NULL; }
42
-
43
- char* block_content = extract_inline_block_content(call_node, allocator);
44
- if (block_content) { return block_content; }
45
-
46
- if (call_node->arguments) {
47
- pm_arguments_node_t* arguments = call_node->arguments;
48
-
49
- if (arguments->arguments.size >= 2) {
50
- pm_node_t* second_argument = arguments->arguments.nodes[1];
51
-
52
- if (second_argument->type != PM_KEYWORD_HASH_NODE) {
53
- if (second_argument->type == PM_STRING_NODE) {
54
- pm_string_node_t* string_node = (pm_string_node_t*) second_argument;
55
- size_t length = pm_string_length(&string_node->unescaped);
56
- return hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
57
- }
58
-
59
- size_t source_length = second_argument->location.end - second_argument->location.start;
60
- return hb_allocator_strndup(allocator, (const char*) second_argument->location.start, source_length);
61
- }
62
- }
63
- }
64
-
65
- return NULL;
66
- }
67
-
68
- bool content_tag_supports_block(void) {
69
- return true;
70
- }
71
-
72
- const tag_helper_handler_T content_tag_handler = { .name = "content_tag",
73
- .source =
74
- HB_STRING_LITERAL("ActionView::Helpers::TagHelper#content_tag"),
75
- .detect = detect_content_tag,
76
- .extract_tag_name = extract_content_tag_name,
77
- .extract_content = extract_content_tag_content,
78
- .supports_block = content_tag_supports_block };
@@ -1,87 +0,0 @@
1
- #include "../../include/analyze/action_view/tag_helper_handler.h"
2
- #include "../../include/util/ruby_util.h"
3
-
4
- #include <prism.h>
5
- #include <stdbool.h>
6
- #include <stdlib.h>
7
- #include <string.h>
8
-
9
- bool detect_tag_dot(pm_call_node_t* call_node, pm_parser_t* parser) {
10
- if (!call_node || !call_node->receiver) { return false; }
11
- if (call_node->receiver->type != PM_CALL_NODE) { return false; }
12
-
13
- pm_call_node_t* receiver_node = (pm_call_node_t*) call_node->receiver;
14
- if (!receiver_node->name) { return false; }
15
-
16
- pm_constant_t* constant = pm_constant_pool_id_to_constant(&parser->constant_pool, receiver_node->name);
17
-
18
- return constant && constant->length == 3 && strncmp((const char*) constant->start, "tag", 3) == 0;
19
- }
20
-
21
- char* extract_tag_dot_name(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
22
- if (!call_node || !call_node->name) { return NULL; }
23
-
24
- pm_constant_t* constant = pm_constant_pool_id_to_constant(&parser->constant_pool, call_node->name);
25
- if (!constant) { return NULL; }
26
-
27
- if (is_ruby_introspection_method(hb_string_from_data((const char*) constant->start, constant->length))) {
28
- return NULL;
29
- }
30
-
31
- char* name = hb_allocator_strndup(allocator, (const char*) constant->start, constant->length);
32
-
33
- for (size_t i = 0; i < constant->length && name[i] != '\0'; i++) {
34
- if (name[i] == '_') { name[i] = '-'; }
35
- }
36
-
37
- return name;
38
- }
39
-
40
- // TODO: this should probably be an array of nodes
41
- char* extract_tag_dot_content(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
42
- if (!call_node) { return NULL; }
43
-
44
- if (call_node->name) {
45
- pm_constant_t* constant = pm_constant_pool_id_to_constant(&parser->constant_pool, call_node->name);
46
-
47
- if (constant
48
- && is_ruby_introspection_method(hb_string_from_data((const char*) constant->start, constant->length))) {
49
- return NULL;
50
- }
51
- }
52
-
53
- char* block_content = extract_inline_block_content(call_node, allocator);
54
- if (block_content) { return block_content; }
55
-
56
- if (call_node->arguments) {
57
- pm_arguments_node_t* arguments = call_node->arguments;
58
-
59
- if (arguments->arguments.size) {
60
- pm_node_t* first_argument = arguments->arguments.nodes[0];
61
-
62
- if (first_argument->type == PM_KEYWORD_HASH_NODE) { return NULL; }
63
-
64
- if (first_argument->type == PM_STRING_NODE) {
65
- pm_string_node_t* string_node = (pm_string_node_t*) first_argument;
66
- size_t length = pm_string_length(&string_node->unescaped);
67
- return hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
68
- }
69
-
70
- size_t source_length = first_argument->location.end - first_argument->location.start;
71
- return hb_allocator_strndup(allocator, (const char*) first_argument->location.start, source_length);
72
- }
73
- }
74
-
75
- return NULL;
76
- }
77
-
78
- bool tag_dot_supports_block(void) {
79
- return true;
80
- }
81
-
82
- const tag_helper_handler_T tag_dot_handler = { .name = "tag",
83
- .source = HB_STRING_LITERAL("ActionView::Helpers::TagHelper#tag"),
84
- .detect = detect_tag_dot,
85
- .extract_tag_name = extract_tag_dot_name,
86
- .extract_content = extract_tag_dot_content,
87
- .supports_block = tag_dot_supports_block };