herb 0.9.4-arm-linux-gnu → 0.9.6-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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/config.yml +57 -21
  3. data/ext/herb/extension.c +8 -0
  4. data/ext/herb/extension_helpers.c +1 -0
  5. data/ext/herb/nodes.c +93 -55
  6. data/lib/herb/3.0/herb.so +0 -0
  7. data/lib/herb/3.1/herb.so +0 -0
  8. data/lib/herb/3.2/herb.so +0 -0
  9. data/lib/herb/3.3/herb.so +0 -0
  10. data/lib/herb/3.4/herb.so +0 -0
  11. data/lib/herb/4.0/herb.so +0 -0
  12. data/lib/herb/action_view/helper_registry.rb +8107 -0
  13. data/lib/herb/ast/nodes.rb +212 -78
  14. data/lib/herb/engine/compiler.rb +4 -6
  15. data/lib/herb/parser_options.rb +7 -2
  16. data/lib/herb/project.rb +2 -5
  17. data/lib/herb/version.rb +1 -1
  18. data/lib/herb/visitor.rb +8 -2
  19. data/sig/herb/action_view/helper_registry.rbs +1144 -0
  20. data/sig/herb/ast/nodes.rbs +85 -34
  21. data/sig/herb/parser_options.rbs +6 -2
  22. data/sig/herb/visitor.rbs +5 -2
  23. data/sig/serialized_ast_nodes.rbs +20 -9
  24. data/src/analyze/action_view/generated_handlers.c +355 -0
  25. data/src/analyze/action_view/generated_handlers.h +16 -0
  26. data/src/analyze/action_view/helper_registry.c +7244 -0
  27. data/src/analyze/action_view/image_tag.c +4 -31
  28. data/src/analyze/action_view/javascript_include_tag.c +1 -42
  29. data/src/analyze/action_view/javascript_tag.c +26 -40
  30. data/src/analyze/action_view/registry.c +2 -2
  31. data/src/analyze/action_view/tag_helper_node_builders.c +23 -2
  32. data/src/analyze/action_view/tag_helpers.c +61 -134
  33. data/src/analyze/action_view/turbo_frame_tag.c +1 -36
  34. data/src/analyze/analyze.c +28 -0
  35. data/src/analyze/analyze_helpers.c +406 -0
  36. data/src/analyze/builders.c +1 -0
  37. data/src/analyze/missing_end.c +16 -0
  38. data/src/analyze/parse_errors.c +3 -2
  39. data/src/analyze/postfix_conditionals.c +326 -0
  40. data/src/analyze/render_nodes.c +231 -35
  41. data/src/analyze/strict_locals.c +22 -338
  42. data/src/analyze/ternary_conditionals.c +265 -0
  43. data/src/analyze/transform.c +23 -2
  44. data/src/ast/ast_nodes.c +114 -57
  45. data/src/ast/ast_pretty_print.c +109 -25
  46. data/src/include/analyze/action_view/helper_registry.h +325 -0
  47. data/src/include/analyze/action_view/tag_helper_handler.h +3 -0
  48. data/src/include/analyze/action_view/tag_helper_node_builders.h +7 -0
  49. data/src/include/analyze/action_view/tag_helpers.h +0 -1
  50. data/src/include/analyze/helpers.h +18 -0
  51. data/src/include/analyze/postfix_conditionals.h +9 -0
  52. data/src/include/analyze/ternary_conditionals.h +15 -0
  53. data/src/include/ast/ast_nodes.h +27 -13
  54. data/src/include/parser/parser.h +1 -0
  55. data/src/include/version.h +1 -1
  56. data/src/parser/match_tags.c +37 -6
  57. data/src/parser.c +9 -0
  58. data/src/visitor.c +50 -7
  59. data/templates/java/org/herb/ast/HelperRegistry.java.erb +258 -0
  60. data/templates/javascript/packages/core/src/action-view-helpers.ts.erb +171 -0
  61. data/templates/javascript/packages/core/src/nodes.ts.erb +5 -1
  62. data/templates/lib/herb/action_view/helper_registry.rb.erb +288 -0
  63. data/templates/rust/src/action_view_helpers.rs.erb +154 -0
  64. data/templates/src/analyze/action_view/generated_handlers.c.erb +230 -0
  65. data/templates/src/analyze/action_view/generated_handlers.h.erb +12 -0
  66. data/templates/src/analyze/action_view/helper_registry.c.erb +114 -0
  67. data/templates/src/include/analyze/action_view/helper_registry.h.erb +82 -0
  68. data/templates/template.rb +338 -1
  69. metadata +19 -3
  70. data/src/analyze/action_view/content_tag.c +0 -78
  71. data/src/analyze/action_view/tag.c +0 -87
@@ -0,0 +1,288 @@
1
+ module Herb
2
+ module ActionView
3
+ module HelperType
4
+ <%- helpers.each do |helper| -%>
5
+ <%= helper.constant_name %> = :<%= helper.name %>
6
+ <%- end -%>
7
+
8
+ ALL = [
9
+ <%- helpers.each do |helper| -%>
10
+ <%= helper.constant_name %>,
11
+ <%- end -%>
12
+ ].freeze #: Array[Symbol]
13
+ end
14
+
15
+ class HelperArgument
16
+ attr_reader :name #: String
17
+ attr_reader :position #: Integer
18
+ attr_reader :type #: String
19
+ attr_reader :default #: String?
20
+ attr_reader :description #: String
21
+
22
+ #: (String, Integer, String, bool, String?, bool, String) -> void
23
+ def initialize(name, position, type, optional, default, splat, description)
24
+ @name = name
25
+ @position = position
26
+ @type = type
27
+ @optional = optional
28
+ @default = default
29
+ @splat = splat
30
+ @description = description
31
+ end
32
+
33
+ #: () -> bool
34
+ def optional? = @optional
35
+
36
+ #: () -> bool
37
+ def splat? = @splat
38
+ end
39
+
40
+ class HelperOption
41
+ attr_reader :name #: String
42
+ attr_reader :type #: String
43
+ attr_reader :maps_to #: String?
44
+ attr_reader :description #: String
45
+
46
+ #: (String, String, String?, String) -> void
47
+ def initialize(name, type, maps_to, description)
48
+ @name = name
49
+ @type = type
50
+ @maps_to = maps_to
51
+ @description = description
52
+ end
53
+
54
+ #: () -> bool
55
+ def maps_to_data_attribute? = maps_to&.start_with?("data-") || false
56
+
57
+ #: () -> bool
58
+ def maps_to_aria_attribute? = maps_to&.start_with?("aria-") || false
59
+ end
60
+
61
+ class HelperImplicitAttribute
62
+ attr_reader :name #: String
63
+ attr_reader :source #: String
64
+ attr_reader :source_with_block #: String?
65
+ attr_reader :wrapper #: String
66
+ attr_reader :skip_wrapping_for #: Array[String]
67
+
68
+ #: (String, String, String?, String, Array[String]) -> void
69
+ def initialize(name, source, source_with_block, wrapper, skip_wrapping_for)
70
+ @name = name
71
+ @source = source
72
+ @source_with_block = source_with_block
73
+ @wrapper = wrapper
74
+ @skip_wrapping_for = skip_wrapping_for
75
+ end
76
+ end
77
+
78
+ class HelperEntry
79
+ attr_reader :name #: String
80
+ attr_reader :type #: Symbol
81
+ attr_reader :source #: String
82
+ attr_reader :gem #: String
83
+ attr_reader :output #: Symbol
84
+ attr_reader :visibility #: String
85
+ attr_reader :tag_name #: String?
86
+ attr_reader :detect_style #: Symbol
87
+ attr_reader :description #: String
88
+ attr_reader :signature #: String
89
+ attr_reader :documentation_url #: String
90
+ attr_reader :implicit_attribute #: HelperImplicitAttribute?
91
+ attr_reader :arguments #: Array[HelperArgument]
92
+ attr_reader :options #: Array[HelperOption]
93
+ attr_reader :special_behaviors #: Array[Symbol]
94
+ attr_reader :aliases #: Array[String]
95
+
96
+ #: (name: String, type: Symbol, source: String, gem: String, output: Symbol, visibility: String, tag_name: String?, is_void: bool, supports_block: bool, preferred_for_tag: bool, supported: bool, detect_style: Symbol, description: String, signature: String, documentation_url: String, implicit_attribute: HelperImplicitAttribute?, arguments: Array[HelperArgument], options: Array[HelperOption], special_behaviors: Array[Symbol], aliases: Array[String]) -> void
97
+ def initialize( # rubocop:disable Metrics/ParameterLists
98
+ name:, type:, source:, gem:, output:, visibility:, tag_name:, is_void:, supports_block:,
99
+ preferred_for_tag:, supported:, detect_style:, description:, signature:, documentation_url:,
100
+ implicit_attribute:, arguments:, options:, special_behaviors:, aliases:
101
+ )
102
+ @name = name
103
+ @type = type
104
+ @source = source
105
+ @gem = gem
106
+ @output = output
107
+ @visibility = visibility
108
+ @tag_name = tag_name
109
+ @is_void = is_void
110
+ @supports_block = supports_block
111
+ @preferred_for_tag = preferred_for_tag
112
+ @supported = supported
113
+ @detect_style = detect_style
114
+ @description = description
115
+ @signature = signature
116
+ @documentation_url = documentation_url
117
+ @implicit_attribute = implicit_attribute
118
+ @arguments = arguments
119
+ @options = options
120
+ @special_behaviors = special_behaviors
121
+ @aliases = aliases
122
+ end
123
+
124
+ #: () -> bool
125
+ def void? = @is_void
126
+
127
+ #: () -> bool
128
+ def supports_block? = @supports_block
129
+
130
+ #: () -> bool
131
+ def preferred_for_tag? = @preferred_for_tag
132
+
133
+ #: () -> bool
134
+ def supported? = @supported
135
+
136
+ #: () -> bool
137
+ def static_tag_name? = !@tag_name.nil?
138
+
139
+ #: () -> bool
140
+ def implicit_attribute? = !@implicit_attribute.nil?
141
+
142
+ #: () -> bool
143
+ def call_name_detect? = @detect_style == :call_name
144
+
145
+ #: () -> bool
146
+ def receiver_call_detect? = @detect_style == :receiver_call
147
+ end
148
+
149
+ module HelperRegistry
150
+ <%- helpers.each do |helper| -%>
151
+ <%= helper.constant_name %> = HelperEntry.new(
152
+ name: "<%= helper.name %>",
153
+ type: HelperType::<%= helper.constant_name %>,
154
+ source: "<%= helper.source %>",
155
+ gem: "<%= helper.gem %>",
156
+ output: :<%= helper.output %>,
157
+ visibility: "<%= helper.visibility %>",
158
+ tag_name: <%= helper.tag_name ? "\"#{helper.tag_name}\"" : "nil" %>,
159
+ is_void: <%= helper.void? %>,
160
+ supports_block: <%= helper.supports_block %>,
161
+ preferred_for_tag: <%= helper.preferred_for_tag %>,
162
+ supported: <%= helper.supported %>,
163
+ detect_style: :<%= helper.detect_style || "call_name" %>,
164
+ description: "<%= helper.escaped_description %>",
165
+ signature: "<%= helper.escaped_signature %>",
166
+ documentation_url: "<%= helper.documentation_url %>",
167
+ <%- if helper.implicit_attribute? -%>
168
+ implicit_attribute: HelperImplicitAttribute.new(
169
+ "<%= helper.implicit_attribute.name %>",
170
+ "<%= helper.implicit_attribute.source %>",
171
+ <%= helper.implicit_attribute.source_with_block ? "\"#{helper.implicit_attribute.source_with_block}\"" : "nil" %>,
172
+ "<%= helper.implicit_attribute.wrapper %>",
173
+ [<%= helper.implicit_attribute.skip_wrapping_for.map { |s| "\"#{s}\"" }.join(", ") %>]
174
+ ),
175
+ <%- else -%>
176
+ implicit_attribute: nil,
177
+ <%- end -%>
178
+ arguments: [
179
+ <%- helper.arguments.each do |arg| -%>
180
+ HelperArgument.new("<%= arg.name %>", <%= arg.position %>, "<%= arg.type_display %>", <%= arg.optional %>, <%= arg.default ? "\"#{arg.escaped_default}\"" : "nil" %>, <%= arg.splat %>, "<%= arg.escaped_description %>"),
181
+ <%- end -%>
182
+ ],
183
+ options: [
184
+ <%- helper.options.each do |opt| -%>
185
+ HelperOption.new("<%= opt.name %>", "<%= opt.type_display %>", <%= opt.maps_to ? "\"#{opt.maps_to}\"" : "nil" %>, "<%= opt.escaped_description %>"),
186
+ <%- end -%>
187
+ ],
188
+ special_behaviors: [<%= helper.special_behaviors.map { |b| ":#{b}" }.join(", ") %>],
189
+ aliases: [<%= helper.aliases.map { |a| "\"#{a}\"" }.join(", ") %>],
190
+ ).freeze #: HelperEntry
191
+
192
+ <%- end -%>
193
+ ENTRIES = {
194
+ <%- helpers.each do |helper| -%>
195
+ "<%= helper.name %>" => <%= helper.constant_name %>,
196
+ <%- helper.aliases.each do |alias_name| -%>
197
+ "<%= alias_name %>" => <%= helper.constant_name %>,
198
+ <%- end -%>
199
+ <%- end -%>
200
+ }.freeze #: Hash[String, HelperEntry]
201
+
202
+ BY_SOURCE = {
203
+ <%- helpers.each do |helper| -%>
204
+ "<%= helper.source %>" => <%= helper.constant_name %>,
205
+ <%- end -%>
206
+ }.freeze #: Hash[String, HelperEntry]
207
+
208
+ <%- helpers_by_tag = helpers.select(&:static_tag_name?).group_by(&:tag_name) -%>
209
+
210
+ BY_TAG_NAME = {
211
+ <%- helpers_by_tag.each do |tag_name, helpers| -%>
212
+ "<%= tag_name %>" => [<%= helpers.map(&:constant_name).join(", ") %>].freeze,
213
+ <%- end -%>
214
+ }.freeze #: Hash[String, Array[HelperEntry]]
215
+
216
+ class << self
217
+ #: (String) -> HelperEntry?
218
+ def get(name)
219
+ ENTRIES[name.to_s]
220
+ end
221
+
222
+ #: (String) -> HelperEntry?
223
+ def find_by_source(source)
224
+ BY_SOURCE[source]
225
+ end
226
+
227
+ #: () -> Array[HelperEntry]
228
+ def entries
229
+ ENTRIES.values
230
+ end
231
+
232
+ #: () { (HelperEntry) -> void } -> void
233
+ def each(&block)
234
+ entries.each(&block)
235
+ end
236
+
237
+ #: (String) -> Array[HelperEntry]
238
+ def helpers_for_tag(tag_name)
239
+ BY_TAG_NAME[tag_name] || []
240
+ end
241
+
242
+ #: (String) -> HelperEntry?
243
+ def find_preferred_for_tag(tag_name)
244
+ helpers = BY_TAG_NAME[tag_name]
245
+ return nil unless helpers
246
+
247
+ helpers.find(&:preferred_for_tag?) || helpers.first
248
+ end
249
+
250
+ #: () -> Integer
251
+ def count
252
+ ENTRIES.size
253
+ end
254
+
255
+ #: (String) -> bool
256
+ def supported?(name)
257
+ entry = get(name)
258
+ entry&.supported? || false
259
+ end
260
+
261
+ #: (String) -> bool
262
+ def exists?(name)
263
+ ENTRIES.key?(name.to_s)
264
+ end
265
+
266
+ #: () -> Array[HelperEntry]
267
+ def supported
268
+ entries.select(&:supported?)
269
+ end
270
+
271
+ #: (String) -> Array[HelperEntry]
272
+ def by_gem(gem_name)
273
+ entries.select { |entry| entry.gem == gem_name }
274
+ end
275
+
276
+ #: (Symbol) -> Array[HelperEntry]
277
+ def by_output(output)
278
+ entries.select { |entry| entry.output == output }
279
+ end
280
+
281
+ #: (String) -> Array[HelperEntry]
282
+ def by_module(module_name)
283
+ entries.select { |entry| entry.source.split("#").first.split("::").last == module_name }
284
+ end
285
+ end
286
+ end
287
+ end
288
+ end
@@ -0,0 +1,154 @@
1
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2
+ pub enum HelperType {
3
+ <%- helpers.each do |helper| -%>
4
+ /// `<%= helper.name %>` — <%= helper.source %>
5
+ <%= helper.camel_case_name %>,
6
+ <%- end -%>
7
+ }
8
+
9
+ impl HelperType {
10
+ pub fn as_str(&self) -> &'static str {
11
+ match self {
12
+ <%- helpers.each do |helper| -%>
13
+ HelperType::<%= helper.camel_case_name %> => "<%= helper.name %>",
14
+ <%- end -%>
15
+ }
16
+ }
17
+
18
+ pub fn from_name(name: &str) -> Option<HelperType> {
19
+ match name {
20
+ <%- helpers.each do |helper| -%>
21
+ "<%= helper.name %>" => Some(HelperType::<%= helper.camel_case_name %>),
22
+ <%- end -%>
23
+ _ => None,
24
+ }
25
+ }
26
+ }
27
+
28
+ #[derive(Debug, Clone)]
29
+ pub struct HelperArgument {
30
+ pub name: &'static str,
31
+ pub position: usize,
32
+ pub argument_type: &'static str,
33
+ pub optional: bool,
34
+ pub default: Option<&'static str>,
35
+ pub splat: bool,
36
+ pub description: &'static str,
37
+ }
38
+
39
+ #[derive(Debug, Clone)]
40
+ pub struct HelperOption {
41
+ pub name: &'static str,
42
+ pub option_type: &'static str,
43
+ pub maps_to: Option<&'static str>,
44
+ pub description: &'static str,
45
+ }
46
+
47
+ #[derive(Debug, Clone)]
48
+ pub struct HelperImplicitAttribute {
49
+ pub name: &'static str,
50
+ pub source: &'static str,
51
+ pub source_with_block: Option<&'static str>,
52
+ pub wrapper: &'static str,
53
+ pub skip_wrapping_for: &'static [&'static str],
54
+ }
55
+
56
+ #[derive(Debug, Clone)]
57
+ pub struct HelperEntry {
58
+ pub name: &'static str,
59
+ pub helper_type: HelperType,
60
+ pub source: &'static str,
61
+ pub gem: &'static str,
62
+ pub output: &'static str,
63
+ pub visibility: &'static str,
64
+ pub tag_name: Option<&'static str>,
65
+ pub is_void: bool,
66
+ pub supports_block: bool,
67
+ pub preferred_for_tag: bool,
68
+ pub supported: bool,
69
+ pub detect_style: &'static str,
70
+ pub description: &'static str,
71
+ pub signature: &'static str,
72
+ pub documentation_url: &'static str,
73
+ pub implicit_attribute: Option<&'static HelperImplicitAttribute>,
74
+ pub arguments: &'static [HelperArgument],
75
+ pub options: &'static [HelperOption],
76
+ pub special_behaviors: &'static [&'static str],
77
+ pub aliases: &'static [&'static str],
78
+ }
79
+
80
+ <%- helpers.each do |helper| -%>
81
+ <%- if helper.implicit_attribute? -%>
82
+ static <%= helper.constant_name %>_IMPLICIT_ATTR: HelperImplicitAttribute = HelperImplicitAttribute {
83
+ name: "<%= helper.implicit_attribute.name %>",
84
+ source: "<%= helper.implicit_attribute.source %>",
85
+ source_with_block: <%= helper.implicit_attribute.source_with_block ? "Some(\"#{helper.implicit_attribute.source_with_block}\")" : "None" %>,
86
+ wrapper: "<%= helper.implicit_attribute.wrapper %>",
87
+ skip_wrapping_for: &[<%= helper.implicit_attribute.skip_wrapping_for.map { |s| "\"#{s}\"" }.join(", ") %>],
88
+ };
89
+
90
+ <%- end -%>
91
+ <%- end -%>
92
+
93
+ static REGISTRY: [HelperEntry; <%= helpers.size %>] = [
94
+ <%- helpers.each do |helper| -%>
95
+ HelperEntry {
96
+ name: "<%= helper.name %>",
97
+ helper_type: HelperType::<%= helper.camel_case_name %>,
98
+ source: "<%= helper.source %>",
99
+ gem: "<%= helper.gem %>",
100
+ output: "<%= helper.output %>",
101
+ visibility: "<%= helper.visibility %>",
102
+ tag_name: <%= helper.tag_name ? "Some(\"#{helper.tag_name}\")" : "None" %>,
103
+ is_void: <%= helper.void? %>,
104
+ supports_block: <%= helper.supports_block %>,
105
+ preferred_for_tag: <%= helper.preferred_for_tag %>,
106
+ supported: <%= helper.supported %>,
107
+ detect_style: "<%= helper.detect_style || "call_name" %>",
108
+ description: "<%= helper.escaped_description %>",
109
+ signature: "<%= helper.escaped_signature %>",
110
+ documentation_url: "<%= helper.documentation_url %>",
111
+ implicit_attribute: <%= helper.implicit_attribute? ? "Some(&#{helper.constant_name}_IMPLICIT_ATTR)" : "None" %>,
112
+ arguments: &[
113
+ <%- helper.arguments.each do |arg| -%>
114
+ HelperArgument { name: "<%= arg.name %>", position: <%= arg.position %>, argument_type: "<%= arg.type_display %>", optional: <%= arg.optional %>, default: <%= arg.default ? "Some(\"#{arg.escaped_default}\")" : "None" %>, splat: <%= arg.splat %>, description: "<%= arg.escaped_description %>" },
115
+ <%- end -%>
116
+ ],
117
+ options: &[
118
+ <%- helper.options.each do |opt| -%>
119
+ HelperOption { name: "<%= opt.name %>", option_type: "<%= opt.type_display %>", maps_to: <%= opt.maps_to ? "Some(\"#{opt.maps_to}\")" : "None" %>, description: "<%= opt.escaped_description %>" },
120
+ <%- end -%>
121
+ ],
122
+ special_behaviors: &[<%= helper.special_behaviors.map { |b| "\"#{b}\"" }.join(", ") %>],
123
+ aliases: &[<%= helper.aliases.map { |a| "\"#{a}\"" }.join(", ") %>],
124
+ },
125
+ <%- end -%>
126
+ ];
127
+
128
+ pub fn get(helper_type: HelperType) -> &'static HelperEntry {
129
+ &REGISTRY[helper_type as usize]
130
+ }
131
+
132
+ pub fn find_by_name(name: &str) -> Option<&'static HelperEntry> {
133
+ REGISTRY.iter().find(|entry| entry.name == name)
134
+ }
135
+
136
+ pub fn find_by_source(source: &str) -> Option<&'static HelperEntry> {
137
+ REGISTRY.iter().find(|entry| entry.source == source)
138
+ }
139
+
140
+ pub fn helpers_for_tag(tag_name: &str) -> Vec<&'static HelperEntry> {
141
+ REGISTRY.iter().filter(|entry| entry.tag_name == Some(tag_name)).collect()
142
+ }
143
+
144
+ pub fn find_preferred_for_tag(tag_name: &str) -> Option<&'static HelperEntry> {
145
+ REGISTRY.iter().find(|entry| entry.tag_name == Some(tag_name) && entry.preferred_for_tag)
146
+ }
147
+
148
+ pub fn entries() -> &'static [HelperEntry] {
149
+ &REGISTRY
150
+ }
151
+
152
+ pub fn count() -> usize {
153
+ REGISTRY.len()
154
+ }
@@ -0,0 +1,230 @@
1
+ #include "../../include/analyze/action_view/tag_helper_handler.h"
2
+ #include "../../include/lib/hb_allocator.h"
3
+ #include "../../include/lib/hb_string.h"
4
+
5
+ #include <prism.h>
6
+ #include <stdbool.h>
7
+ #include <string.h>
8
+
9
+ <%- supported_helpers = helpers.select(&:supported?).reject(&:custom_transform?) -%>
10
+ // ==========================================================================
11
+ // Generated detect functions
12
+ // ==========================================================================
13
+ <%- supported_helpers.each do |helper| -%>
14
+ <%- if helper.call_name_detect? -%>
15
+
16
+ bool detect_<%= helper.safe_name %>(pm_call_node_t* call_node, pm_parser_t* parser) {
17
+ if (!call_node || !call_node->name) { return false; }
18
+
19
+ pm_constant_t* constant = pm_constant_pool_id_to_constant(&parser->constant_pool, call_node->name);
20
+
21
+ return constant && constant->length == <%= helper.name.length %> && strncmp((const char*) constant->start, "<%= helper.name %>", <%= helper.name.length %>) == 0;
22
+ }
23
+ <%- elsif helper.receiver_call_detect? -%>
24
+
25
+ bool detect_<%= helper.safe_name %>(pm_call_node_t* call_node, pm_parser_t* parser) {
26
+ if (!call_node || !call_node->receiver) { return false; }
27
+ if (call_node->receiver->type != PM_CALL_NODE) { return false; }
28
+
29
+ pm_call_node_t* receiver_node = (pm_call_node_t*) call_node->receiver;
30
+ if (!receiver_node->name) { return false; }
31
+
32
+ pm_constant_t* constant = pm_constant_pool_id_to_constant(&parser->constant_pool, receiver_node->name);
33
+
34
+ return constant && constant->length == 3 && strncmp((const char*) constant->start, "tag", 3) == 0;
35
+ }
36
+ <%- end -%>
37
+ <%- end -%>
38
+
39
+ // ==========================================================================
40
+ // Generated extract_tag_name functions
41
+ // ==========================================================================
42
+ <%- supported_helpers.each do |helper| -%>
43
+ <%- if helper.tag_name -%>
44
+
45
+ static char* extract_<%= helper.safe_name %>_tag_name(pm_call_node_t* _call_node, pm_parser_t* _parser, hb_allocator_T* allocator) {
46
+ (void) _call_node;
47
+ (void) _parser;
48
+
49
+ return hb_allocator_strdup(allocator, "<%= helper.tag_name %>");
50
+ }
51
+ <%- elsif helper.name == "content_tag" -%>
52
+
53
+ static char* extract_<%= helper.safe_name %>_tag_name(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
54
+ (void) parser;
55
+
56
+ if (!call_node || !call_node->arguments) { return NULL; }
57
+
58
+ pm_arguments_node_t* arguments = call_node->arguments;
59
+ if (!arguments->arguments.size) { return NULL; }
60
+
61
+ pm_node_t* first_argument = arguments->arguments.nodes[0];
62
+
63
+ if (first_argument->type == PM_STRING_NODE) {
64
+ pm_string_node_t* string_node = (pm_string_node_t*) first_argument;
65
+ size_t length = pm_string_length(&string_node->unescaped);
66
+ return hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
67
+ } else if (first_argument->type == PM_SYMBOL_NODE) {
68
+ pm_symbol_node_t* symbol_node = (pm_symbol_node_t*) first_argument;
69
+ size_t length = pm_string_length(&symbol_node->unescaped);
70
+ return hb_allocator_strndup(allocator, (const char*) pm_string_source(&symbol_node->unescaped), length);
71
+ }
72
+
73
+ return NULL;
74
+ }
75
+ <%- elsif helper.name == "tag" -%>
76
+
77
+ extern bool is_ruby_introspection_method(hb_string_T method_name);
78
+
79
+ static char* extract_<%= helper.safe_name %>_tag_name(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
80
+ if (!call_node || !call_node->name) { return NULL; }
81
+
82
+ pm_constant_t* constant = pm_constant_pool_id_to_constant(&parser->constant_pool, call_node->name);
83
+ if (!constant) { return NULL; }
84
+
85
+ if (is_ruby_introspection_method(hb_string_from_data((const char*) constant->start, constant->length))) { return NULL; }
86
+
87
+ char* name = hb_allocator_strndup(allocator, (const char*) constant->start, constant->length);
88
+
89
+ for (size_t i = 0; i < constant->length && name[i] != '\0'; i++) {
90
+ if (name[i] == '_') { name[i] = '-'; }
91
+ }
92
+
93
+ return name;
94
+ }
95
+ <%- end -%>
96
+ <%- end -%>
97
+
98
+ // ==========================================================================
99
+ // Generated extract_content functions
100
+ // ==========================================================================
101
+ <%- supported_helpers.each do |helper| -%>
102
+ <%- content = helper.content -%>
103
+ <%- if content.nil? -%>
104
+
105
+ static char* extract_<%= helper.safe_name %>_content(pm_call_node_t* _call_node, pm_parser_t* _parser, hb_allocator_T* _allocator) {
106
+ (void) _call_node;
107
+ (void) _parser;
108
+ (void) _allocator;
109
+
110
+ return NULL;
111
+ }
112
+ <%- elsif content.first_arg? -%>
113
+
114
+ static char* extract_<%= helper.safe_name %>_content(pm_call_node_t* call_node, pm_parser_t* _parser, hb_allocator_T* allocator) {
115
+ (void) _parser;
116
+
117
+ if (!call_node || !call_node->arguments) { return NULL; }
118
+
119
+ pm_arguments_node_t* arguments = call_node->arguments;
120
+ if (!arguments->arguments.size) { return NULL; }
121
+
122
+ pm_node_t* first_argument = arguments->arguments.nodes[0];
123
+
124
+ <%- if content.skip_if_hash -%>
125
+ if (first_argument->type == PM_KEYWORD_HASH_NODE) { return NULL; }
126
+ <%- end -%>
127
+
128
+ if (first_argument->type == PM_STRING_NODE) {
129
+ pm_string_node_t* string_node = (pm_string_node_t*) first_argument;
130
+ size_t length = pm_string_length(&string_node->unescaped);
131
+ return hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
132
+ }
133
+
134
+ size_t source_length = first_argument->location.end - first_argument->location.start;
135
+ return hb_allocator_strndup(allocator, (const char*) first_argument->location.start, source_length);
136
+ }
137
+ <%- elsif content.block_or_arg? -%>
138
+
139
+ static char* extract_<%= helper.safe_name %>_content(pm_call_node_t* call_node, pm_parser_t* <%= helper.receiver_call_detect? ? "parser" : "_parser" %>, hb_allocator_T* allocator) {
140
+ (void) <%= helper.receiver_call_detect? ? "parser" : "_parser" %>;
141
+
142
+ if (!call_node) { return NULL; }
143
+
144
+ <%- if helper.receiver_call_detect? -%>
145
+ if (call_node->name) {
146
+ pm_constant_t* constant = pm_constant_pool_id_to_constant(&parser->constant_pool, call_node->name);
147
+
148
+ if (constant && is_ruby_introspection_method(hb_string_from_data((const char*) constant->start, constant->length))) {
149
+ return NULL;
150
+ }
151
+ }
152
+ <%- end -%>
153
+
154
+ char* block_content = extract_inline_block_content(call_node, allocator);
155
+ if (block_content) { return block_content; }
156
+
157
+ <%- arg_index = (content.arg_position || 1) - 1 -%>
158
+
159
+ <%- if content.arg_position -%>
160
+ if (call_node->arguments) {
161
+ pm_arguments_node_t* arguments = call_node->arguments;
162
+
163
+ if (arguments->arguments.size >= <%= content.arg_position %>) {
164
+ pm_node_t* argument = arguments->arguments.nodes[<%= arg_index %>];
165
+
166
+ <%- if content.skip_if_hash -%>
167
+ if (argument->type != PM_KEYWORD_HASH_NODE) {
168
+ <%- end -%>
169
+
170
+ if (argument->type == PM_STRING_NODE) {
171
+ pm_string_node_t* string_node = (pm_string_node_t*) argument;
172
+ size_t length = pm_string_length(&string_node->unescaped);
173
+ return hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
174
+ }
175
+
176
+ size_t source_length = argument->location.end - argument->location.start;
177
+
178
+ <%- if content.to_s_suffix_when_single -%>
179
+ if (arguments->arguments.size == 1 && argument->type != PM_NIL_NODE) {
180
+ const char* suffix = ".to_s";
181
+ size_t suffix_length = strlen(suffix);
182
+ size_t total_length = source_length + suffix_length;
183
+ char* ruby_expression = hb_allocator_alloc(allocator, total_length + 1);
184
+
185
+ memcpy(ruby_expression, (const char*) argument->location.start, source_length);
186
+ memcpy(ruby_expression + source_length, suffix, suffix_length);
187
+ ruby_expression[total_length] = '\0';
188
+
189
+ return ruby_expression;
190
+ }
191
+ <%- end -%>
192
+
193
+ return hb_allocator_strndup(allocator, (const char*) argument->location.start, source_length);
194
+
195
+ <%- if content.skip_if_hash -%>
196
+ }
197
+ <%- end -%>
198
+ }
199
+ }
200
+ <%- end -%>
201
+
202
+ return NULL;
203
+ }
204
+ <%- end -%>
205
+ <%- end -%>
206
+
207
+ // ==========================================================================
208
+ // Generated supports_block functions
209
+ // ==========================================================================
210
+ <%- supported_helpers.each do |helper| -%>
211
+
212
+ static bool <%= helper.safe_name %>_supports_block(void) {
213
+ return <%= helper.supports_block %>;
214
+ }
215
+ <%- end -%>
216
+
217
+ // ==========================================================================
218
+ // Generated handler structs
219
+ // ==========================================================================
220
+ <%- supported_helpers.each do |helper| -%>
221
+
222
+ const tag_helper_handler_T <%= helper.safe_name %>_handler = {
223
+ .name = "<%= helper.name %>",
224
+ .source = HB_STRING_LITERAL("<%= helper.source %>"),
225
+ .detect = detect_<%= helper.safe_name %>,
226
+ .extract_tag_name = extract_<%= helper.safe_name %>_tag_name,
227
+ .extract_content = extract_<%= helper.safe_name %>_content,
228
+ .supports_block = <%= helper.safe_name %>_supports_block,
229
+ };
230
+ <%- end -%>
@@ -0,0 +1,12 @@
1
+ #ifndef GENERATED_HANDLERS_H
2
+ #define GENERATED_HANDLERS_H
3
+
4
+ #include "../../include/analyze/action_view/tag_helper_handler.h"
5
+
6
+ <%- supported_helpers = helpers.select(&:supported?) -%>
7
+ <%- supported_helpers.each do |helper| -%>
8
+ <%- next if helper.custom_transform? -%>
9
+ extern const tag_helper_handler_T <%= helper.safe_name %>_handler;
10
+ <%- end -%>
11
+
12
+ #endif