herb 0.9.0-arm-linux-gnu → 0.9.2-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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/config.yml +156 -0
  3. data/ext/herb/error_helpers.c +168 -0
  4. data/ext/herb/extension.c +4 -0
  5. data/ext/herb/extension_helpers.c +1 -0
  6. data/ext/herb/nodes.c +110 -0
  7. data/lib/herb/3.0/herb.so +0 -0
  8. data/lib/herb/3.1/herb.so +0 -0
  9. data/lib/herb/3.2/herb.so +0 -0
  10. data/lib/herb/3.3/herb.so +0 -0
  11. data/lib/herb/3.4/herb.so +0 -0
  12. data/lib/herb/4.0/herb.so +0 -0
  13. data/lib/herb/ast/nodes.rb +393 -17
  14. data/lib/herb/engine/compiler.rb +17 -41
  15. data/lib/herb/engine.rb +76 -26
  16. data/lib/herb/errors.rb +245 -0
  17. data/lib/herb/parser_options.rb +6 -1
  18. data/lib/herb/prism_inspect.rb +5 -1
  19. data/lib/herb/version.rb +1 -1
  20. data/lib/herb/visitor.rb +10 -0
  21. data/sig/herb/ast/nodes.rbs +132 -0
  22. data/sig/herb/engine/compiler.rbs +4 -2
  23. data/sig/herb/engine.rbs +8 -0
  24. data/sig/herb/errors.rbs +114 -0
  25. data/sig/herb/parser_options.rbs +4 -0
  26. data/sig/herb/visitor.rbs +6 -0
  27. data/sig/rubyvm.rbs +5 -0
  28. data/sig/serialized_ast_errors.rbs +28 -0
  29. data/sig/serialized_ast_nodes.rbs +31 -0
  30. data/src/analyze/action_view/attribute_extraction_helpers.c +23 -1
  31. data/src/analyze/action_view/content_tag.c +19 -11
  32. data/src/analyze/action_view/javascript_include_tag.c +92 -0
  33. data/src/analyze/action_view/javascript_tag.c +55 -0
  34. data/src/analyze/action_view/link_to.c +25 -1
  35. data/src/analyze/action_view/registry.c +29 -2
  36. data/src/analyze/action_view/tag.c +14 -8
  37. data/src/analyze/action_view/tag_helper_node_builders.c +16 -3
  38. data/src/analyze/action_view/tag_helpers.c +332 -12
  39. data/src/analyze/analyze.c +3 -0
  40. data/src/analyze/prism_annotate.c +4 -2
  41. data/src/analyze/render_nodes.c +761 -0
  42. data/src/analyze/transform.c +7 -0
  43. data/src/ast_nodes.c +97 -0
  44. data/src/ast_pretty_print.c +74 -0
  45. data/src/errors.c +379 -0
  46. data/src/html_util.c +50 -0
  47. data/src/include/analyze/action_view/tag_helper_handler.h +2 -0
  48. data/src/include/analyze/render_nodes.h +11 -0
  49. data/src/include/ast_nodes.h +37 -0
  50. data/src/include/errors.h +58 -0
  51. data/src/include/html_util.h +1 -0
  52. data/src/include/parser.h +1 -0
  53. data/src/include/version.h +1 -1
  54. data/src/parser.c +1 -0
  55. data/src/parser_match_tags.c +20 -0
  56. data/src/util/hb_arena.c +3 -7
  57. data/src/visitor.c +20 -0
  58. data/templates/lib/herb/ast/nodes.rb.erb +8 -2
  59. data/templates/rust/src/ast/nodes.rs.erb +1 -1
  60. data/templates/rust/src/nodes.rs.erb +1 -1
  61. metadata +6 -1
data/sig/herb/errors.rbs CHANGED
@@ -458,5 +458,119 @@ module Herb
458
458
  # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
459
459
  def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
460
460
  end
461
+
462
+ class RenderAmbiguousLocalsError < Error
463
+ include Colors
464
+
465
+ attr_reader partial: String?
466
+
467
+ # : (String, Location?, String, String) -> void
468
+ def initialize: (String, Location?, String, String) -> void
469
+
470
+ # : () -> String
471
+ def inspect: () -> String
472
+
473
+ # : () -> serialized_render_ambiguous_locals_error
474
+ def to_hash: () -> serialized_render_ambiguous_locals_error
475
+
476
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
477
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
478
+ end
479
+
480
+ class RenderMissingLocalsError < Error
481
+ include Colors
482
+
483
+ attr_reader partial: String?
484
+
485
+ attr_reader keywords: String?
486
+
487
+ # : (String, Location?, String, String, String) -> void
488
+ def initialize: (String, Location?, String, String, String) -> void
489
+
490
+ # : () -> String
491
+ def inspect: () -> String
492
+
493
+ # : () -> serialized_render_missing_locals_error
494
+ def to_hash: () -> serialized_render_missing_locals_error
495
+
496
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
497
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
498
+ end
499
+
500
+ class RenderNoArgumentsError < Error
501
+ include Colors
502
+
503
+ # : () -> String
504
+ def inspect: () -> String
505
+
506
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
507
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
508
+ end
509
+
510
+ class RenderConflictingPartialError < Error
511
+ include Colors
512
+
513
+ attr_reader positional_partial: String?
514
+
515
+ attr_reader keyword_partial: String?
516
+
517
+ # : (String, Location?, String, String, String) -> void
518
+ def initialize: (String, Location?, String, String, String) -> void
519
+
520
+ # : () -> String
521
+ def inspect: () -> String
522
+
523
+ # : () -> serialized_render_conflicting_partial_error
524
+ def to_hash: () -> serialized_render_conflicting_partial_error
525
+
526
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
527
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
528
+ end
529
+
530
+ class RenderInvalidAsOptionError < Error
531
+ include Colors
532
+
533
+ attr_reader as_value: String?
534
+
535
+ # : (String, Location?, String, String) -> void
536
+ def initialize: (String, Location?, String, String) -> void
537
+
538
+ # : () -> String
539
+ def inspect: () -> String
540
+
541
+ # : () -> serialized_render_invalid_as_option_error
542
+ def to_hash: () -> serialized_render_invalid_as_option_error
543
+
544
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
545
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
546
+ end
547
+
548
+ class RenderObjectAndCollectionError < Error
549
+ include Colors
550
+
551
+ # : () -> String
552
+ def inspect: () -> String
553
+
554
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
555
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
556
+ end
557
+
558
+ class RenderLayoutWithoutBlockError < Error
559
+ include Colors
560
+
561
+ attr_reader layout: String?
562
+
563
+ # : (String, Location?, String, String) -> void
564
+ def initialize: (String, Location?, String, String) -> void
565
+
566
+ # : () -> String
567
+ def inspect: () -> String
568
+
569
+ # : () -> serialized_render_layout_without_block_error
570
+ def to_hash: () -> serialized_render_layout_without_block_error
571
+
572
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
573
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
574
+ end
461
575
  end
462
576
  end
@@ -10,6 +10,8 @@ module Herb
10
10
 
11
11
  attr_reader action_view_helpers: bool
12
12
 
13
+ attr_reader render_nodes: bool
14
+
13
15
  attr_reader prism_program: bool
14
16
 
15
17
  attr_reader prism_nodes: bool
@@ -24,6 +26,8 @@ module Herb
24
26
 
25
27
  DEFAULT_ACTION_VIEW_HELPERS: bool
26
28
 
29
+ DEFAULT_RENDER_NODES: bool
30
+
27
31
  DEFAULT_PRISM_PROGRAM: bool
28
32
 
29
33
  DEFAULT_PRISM_NODES: bool
data/sig/herb/visitor.rbs CHANGED
@@ -121,6 +121,12 @@ module Herb
121
121
  # : (Herb::AST::ERBUnlessNode) -> void
122
122
  def visit_erb_unless_node: (Herb::AST::ERBUnlessNode) -> void
123
123
 
124
+ # : (Herb::AST::RubyRenderLocalNode) -> void
125
+ def visit_ruby_render_local_node: (Herb::AST::RubyRenderLocalNode) -> void
126
+
127
+ # : (Herb::AST::ERBRenderNode) -> void
128
+ def visit_erb_render_node: (Herb::AST::ERBRenderNode) -> void
129
+
124
130
  # : (Herb::AST::ERBYieldNode) -> void
125
131
  def visit_erb_yield_node: (Herb::AST::ERBYieldNode) -> void
126
132
 
data/sig/rubyvm.rbs ADDED
@@ -0,0 +1,5 @@
1
+ class RubyVM
2
+ class InstructionSequence
3
+ def self.compile: (String source, ?String? file, ?String? path, ?Integer? line, ?freeze: bool, **untyped options) -> RubyVM::InstructionSequence
4
+ end
5
+ end
@@ -112,4 +112,32 @@ module Herb
112
112
  nested_tag_column: Integer,
113
113
  }
114
114
 
115
+ type serialized_render_ambiguous_locals_error = serialized_error & {
116
+ partial: String,
117
+ }
118
+
119
+ type serialized_render_missing_locals_error = serialized_error & {
120
+ partial: String,
121
+ keywords: String,
122
+ }
123
+
124
+ type serialized_render_no_arguments_error = serialized_error & {
125
+ }
126
+
127
+ type serialized_render_conflicting_partial_error = serialized_error & {
128
+ positional_partial: String,
129
+ keyword_partial: String,
130
+ }
131
+
132
+ type serialized_render_invalid_as_option_error = serialized_error & {
133
+ as_value: String,
134
+ }
135
+
136
+ type serialized_render_object_and_collection_error = serialized_error & {
137
+ }
138
+
139
+ type serialized_render_layout_without_block_error = serialized_error & {
140
+ layout: String,
141
+ }
142
+
115
143
  end
@@ -268,6 +268,37 @@ module Herb
268
268
  end_node: Herb::AST::ERBEndNode,
269
269
  }
270
270
 
271
+ type serialized_ruby_render_local_node = serialized_node & {
272
+ name: Herb::Token,
273
+ value: Herb::AST::RubyLiteralNode,
274
+ }
275
+
276
+ type serialized_erb_render_node = serialized_node & {
277
+ tag_opening: Herb::Token,
278
+ content: Herb::Token,
279
+ tag_closing: Herb::Token,
280
+ analyzed_ruby: nil,
281
+ prism_node: String,
282
+ partial: Herb::Token,
283
+ template_path: Herb::Token,
284
+ layout: Herb::Token,
285
+ file: Herb::Token,
286
+ inline_template: Herb::Token,
287
+ body: Herb::Token,
288
+ plain: Herb::Token,
289
+ html: Herb::Token,
290
+ renderable: Herb::Token,
291
+ collection: Herb::Token,
292
+ object: Herb::Token,
293
+ as_name: Herb::Token,
294
+ spacer_template: Herb::Token,
295
+ formats: Herb::Token,
296
+ variants: Herb::Token,
297
+ handlers: Herb::Token,
298
+ content_type: Herb::Token,
299
+ locals: Array[Herb::AST::RubyRenderLocalNode],
300
+ }
301
+
271
302
  type serialized_erb_yield_node = serialized_node & {
272
303
  tag_opening: Herb::Token,
273
304
  content: Herb::Token,
@@ -1,5 +1,6 @@
1
1
  #include "../../include/analyze/action_view/attribute_extraction_helpers.h"
2
2
  #include "../../include/analyze/action_view/tag_helper_node_builders.h"
3
+ #include "../../include/html_util.h"
3
4
  #include "../../include/util.h"
4
5
  #include "../../include/util/hb_allocator.h"
5
6
  #include "../../include/util/hb_array.h"
@@ -58,6 +59,14 @@ static AST_HTML_ATTRIBUTE_NODE_T* create_attribute_from_value(
58
59
  hb_allocator_dealloc(allocator, value_string);
59
60
 
60
61
  return attribute;
62
+ } else if (value_node->type == PM_TRUE_NODE) {
63
+ if (is_boolean_attribute(hb_string((char*) name_string))) {
64
+ return create_html_attribute_node(name_string, NULL, start_position, end_position, allocator);
65
+ }
66
+ return create_html_attribute_node(name_string, "true", start_position, end_position, allocator);
67
+ } else if (value_node->type == PM_FALSE_NODE) {
68
+ if (is_boolean_attribute(hb_string((char*) name_string))) { return NULL; }
69
+ return create_html_attribute_node(name_string, "false", start_position, end_position, allocator);
61
70
  } else if (value_node->type == PM_INTERPOLATED_STRING_NODE) {
62
71
  return create_html_attribute_with_interpolated_value(
63
72
  name_string,
@@ -265,7 +274,7 @@ bool has_html_attributes_in_call(pm_call_node_t* call_node) {
265
274
 
266
275
  pm_node_t* last_argument = arguments->arguments.nodes[arguments->arguments.size - 1];
267
276
 
268
- return last_argument && last_argument->type == PM_KEYWORD_HASH_NODE;
277
+ return last_argument && (last_argument->type == PM_KEYWORD_HASH_NODE || last_argument->type == PM_HASH_NODE);
269
278
  }
270
279
 
271
280
  hb_array_T* extract_html_attributes_from_call_node(
@@ -280,6 +289,19 @@ hb_array_T* extract_html_attributes_from_call_node(
280
289
  pm_arguments_node_t* arguments = call_node->arguments;
281
290
  pm_node_t* last_argument = arguments->arguments.nodes[arguments->arguments.size - 1];
282
291
 
292
+ if (last_argument->type == PM_HASH_NODE) {
293
+ pm_hash_node_t* hash_node = (pm_hash_node_t*) last_argument;
294
+ pm_keyword_hash_node_t synthetic = { .base = hash_node->base, .elements = hash_node->elements };
295
+
296
+ return extract_html_attributes_from_keyword_hash(
297
+ &synthetic,
298
+ source,
299
+ original_source,
300
+ erb_content_offset,
301
+ allocator
302
+ );
303
+ }
304
+
283
305
  return extract_html_attributes_from_keyword_hash(
284
306
  (pm_keyword_hash_node_t*) last_argument,
285
307
  source,
@@ -38,23 +38,31 @@ char* extract_content_tag_name(pm_call_node_t* call_node, pm_parser_t* parser, h
38
38
  char* extract_content_tag_content(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
39
39
  (void) parser;
40
40
 
41
- if (!call_node || !call_node->arguments) { return NULL; }
41
+ if (!call_node) { return NULL; }
42
42
 
43
- pm_arguments_node_t* arguments = call_node->arguments;
44
- if (arguments->arguments.size < 2) { return NULL; }
43
+ char* block_content = extract_inline_block_content(call_node, allocator);
44
+ if (block_content) { return block_content; }
45
45
 
46
- pm_node_t* second_argument = arguments->arguments.nodes[1];
46
+ if (call_node->arguments) {
47
+ pm_arguments_node_t* arguments = call_node->arguments;
47
48
 
48
- if (second_argument->type == PM_KEYWORD_HASH_NODE) { return NULL; }
49
+ if (arguments->arguments.size >= 2) {
50
+ pm_node_t* second_argument = arguments->arguments.nodes[1];
49
51
 
50
- if (second_argument->type == PM_STRING_NODE) {
51
- pm_string_node_t* string_node = (pm_string_node_t*) second_argument;
52
- size_t length = pm_string_length(&string_node->unescaped);
53
- return hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
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
+ }
54
63
  }
55
64
 
56
- size_t source_length = second_argument->location.end - second_argument->location.start;
57
- return hb_allocator_strndup(allocator, (const char*) second_argument->location.start, source_length);
65
+ return NULL;
58
66
  }
59
67
 
60
68
  bool content_tag_supports_block(void) {
@@ -0,0 +1,92 @@
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_javascript_include_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 == 22
13
+ && strncmp((const char*) constant->start, "javascript_include_tag", 22) == 0;
14
+ }
15
+
16
+ char* extract_javascript_include_tag_name(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
17
+ (void) call_node;
18
+ (void) parser;
19
+
20
+ return hb_allocator_strdup(allocator, "script");
21
+ }
22
+
23
+ char* extract_javascript_include_tag_content(
24
+ pm_call_node_t* call_node,
25
+ pm_parser_t* parser,
26
+ hb_allocator_T* allocator
27
+ ) {
28
+ (void) call_node;
29
+ (void) parser;
30
+ (void) allocator;
31
+
32
+ return NULL;
33
+ }
34
+
35
+ char* extract_javascript_include_tag_src(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
36
+ (void) parser;
37
+
38
+ if (!call_node || !call_node->arguments) { return NULL; }
39
+
40
+ pm_arguments_node_t* arguments = call_node->arguments;
41
+ if (!arguments->arguments.size) { return NULL; }
42
+
43
+ pm_node_t* first_argument = arguments->arguments.nodes[0];
44
+
45
+ if (first_argument->type == PM_STRING_NODE) {
46
+ pm_string_node_t* string_node = (pm_string_node_t*) first_argument;
47
+ size_t length = pm_string_length(&string_node->unescaped);
48
+
49
+ return hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
50
+ }
51
+
52
+ size_t source_length = first_argument->location.end - first_argument->location.start;
53
+ return hb_allocator_strndup(allocator, (const char*) first_argument->location.start, source_length);
54
+ }
55
+
56
+ bool javascript_include_tag_source_is_url(const char* source, size_t length) {
57
+ if (!source || length == 0) { return false; }
58
+
59
+ if (length >= 2 && source[0] == '/' && source[1] == '/') { return true; }
60
+ if (strstr(source, "://") != NULL) { return true; }
61
+
62
+ return false;
63
+ }
64
+
65
+ char* wrap_in_javascript_path(const char* source, size_t source_length, hb_allocator_T* allocator) {
66
+ const char* prefix = "javascript_path(";
67
+ const char* suffix = ")";
68
+ size_t prefix_length = strlen(prefix);
69
+ size_t suffix_length = strlen(suffix);
70
+ size_t total_length = prefix_length + source_length + suffix_length;
71
+ char* result = hb_allocator_alloc(allocator, total_length + 1);
72
+
73
+ memcpy(result, prefix, prefix_length);
74
+ memcpy(result + prefix_length, source, source_length);
75
+ memcpy(result + prefix_length + source_length, suffix, suffix_length);
76
+ result[total_length] = '\0';
77
+
78
+ return result;
79
+ }
80
+
81
+ bool javascript_include_tag_supports_block(void) {
82
+ return false;
83
+ }
84
+
85
+ const tag_helper_handler_T javascript_include_tag_handler = {
86
+ .name = "javascript_include_tag",
87
+ .source = HB_STRING_LITERAL("ActionView::Helpers::AssetTagHelper#javascript_include_tag"),
88
+ .detect = detect_javascript_include_tag,
89
+ .extract_tag_name = extract_javascript_include_tag_name,
90
+ .extract_content = extract_javascript_include_tag_content,
91
+ .supports_block = javascript_include_tag_supports_block
92
+ };
@@ -0,0 +1,55 @@
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_javascript_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 == 14 && strncmp((const char*) constant->start, "javascript_tag", 14) == 0;
13
+ }
14
+
15
+ char* extract_javascript_tag_name(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
16
+ (void) call_node;
17
+ (void) parser;
18
+
19
+ return hb_allocator_strdup(allocator, "script");
20
+ }
21
+
22
+ char* extract_javascript_tag_content(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
23
+ (void) parser;
24
+
25
+ if (!call_node || !call_node->arguments) { return NULL; }
26
+
27
+ pm_arguments_node_t* arguments = call_node->arguments;
28
+ if (!arguments->arguments.size) { return NULL; }
29
+
30
+ pm_node_t* first_argument = arguments->arguments.nodes[0];
31
+
32
+ if (first_argument->type == PM_KEYWORD_HASH_NODE) { return NULL; }
33
+
34
+ if (first_argument->type == PM_STRING_NODE) {
35
+ pm_string_node_t* string_node = (pm_string_node_t*) first_argument;
36
+ size_t length = pm_string_length(&string_node->unescaped);
37
+ return hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
38
+ }
39
+
40
+ size_t source_length = first_argument->location.end - first_argument->location.start;
41
+ return hb_allocator_strndup(allocator, (const char*) first_argument->location.start, source_length);
42
+ }
43
+
44
+ bool javascript_tag_supports_block(void) {
45
+ return true;
46
+ }
47
+
48
+ const tag_helper_handler_T javascript_tag_handler = {
49
+ .name = "javascript_tag",
50
+ .source = HB_STRING_LITERAL("ActionView::Helpers::JavaScriptHelper#javascript_tag"),
51
+ .detect = detect_javascript_tag,
52
+ .extract_tag_name = extract_javascript_tag_name,
53
+ .extract_content = extract_javascript_tag_content,
54
+ .supports_block = javascript_tag_supports_block
55
+ };
@@ -56,7 +56,12 @@ char* extract_link_to_tag_name(pm_call_node_t* call_node, pm_parser_t* parser, h
56
56
  char* extract_link_to_content(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
57
57
  (void) parser;
58
58
 
59
- if (!call_node || !call_node->arguments) { return NULL; }
59
+ if (!call_node) { return NULL; }
60
+
61
+ char* block_content = extract_inline_block_content(call_node, allocator);
62
+ if (block_content) { return block_content; }
63
+
64
+ if (!call_node->arguments) { return NULL; }
60
65
 
61
66
  pm_arguments_node_t* arguments = call_node->arguments;
62
67
  if (!arguments->arguments.size) { return NULL; }
@@ -96,6 +101,25 @@ char* extract_link_to_href(pm_call_node_t* call_node, pm_parser_t* parser, hb_al
96
101
  if (!call_node || !call_node->arguments) { return NULL; }
97
102
 
98
103
  pm_arguments_node_t* arguments = call_node->arguments;
104
+ bool has_inline_block = call_node->block && call_node->block->type == PM_BLOCK_NODE;
105
+
106
+ if (has_inline_block && arguments->arguments.size >= 1) {
107
+ pm_node_t* first_argument = arguments->arguments.nodes[0];
108
+
109
+ if (first_argument->type == PM_STRING_NODE) {
110
+ pm_string_node_t* string_node = (pm_string_node_t*) first_argument;
111
+ size_t length = pm_string_length(&string_node->unescaped);
112
+ return hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
113
+ }
114
+
115
+ size_t source_length = first_argument->location.end - first_argument->location.start;
116
+
117
+ if (is_route_helper_node(first_argument, parser)) {
118
+ return hb_allocator_strndup(allocator, (const char*) first_argument->location.start, source_length);
119
+ }
120
+
121
+ return wrap_in_url_for((const char*) first_argument->location.start, source_length, allocator);
122
+ }
99
123
 
100
124
  // Format: "url_for(<expression>)"
101
125
  if (arguments->arguments.size == 1) {
@@ -8,8 +8,10 @@ extern const tag_helper_handler_T content_tag_handler;
8
8
  extern const tag_helper_handler_T tag_dot_handler;
9
9
  extern const tag_helper_handler_T link_to_handler;
10
10
  extern const tag_helper_handler_T turbo_frame_tag_handler;
11
+ extern const tag_helper_handler_T javascript_tag_handler;
12
+ extern const tag_helper_handler_T javascript_include_tag_handler;
11
13
 
12
- static size_t handlers_count = 4;
14
+ static size_t handlers_count = 6;
13
15
 
14
16
  tag_helper_info_T* tag_helper_info_init(hb_allocator_T* allocator) {
15
17
  tag_helper_info_T* info = hb_allocator_alloc(allocator, sizeof(tag_helper_info_T));
@@ -41,7 +43,7 @@ void tag_helper_info_free(tag_helper_info_T** info) {
41
43
  }
42
44
 
43
45
  tag_helper_handler_T* get_tag_helper_handlers(void) {
44
- static tag_helper_handler_T static_handlers[4];
46
+ static tag_helper_handler_T static_handlers[6];
45
47
  static bool initialized = false;
46
48
 
47
49
  if (!initialized) {
@@ -49,6 +51,8 @@ tag_helper_handler_T* get_tag_helper_handlers(void) {
49
51
  static_handlers[1] = tag_dot_handler;
50
52
  static_handlers[2] = link_to_handler;
51
53
  static_handlers[3] = turbo_frame_tag_handler;
54
+ static_handlers[4] = javascript_tag_handler;
55
+ static_handlers[5] = javascript_include_tag_handler;
52
56
  initialized = true;
53
57
  }
54
58
 
@@ -58,3 +62,26 @@ tag_helper_handler_T* get_tag_helper_handlers(void) {
58
62
  size_t get_tag_helper_handlers_count(void) {
59
63
  return handlers_count;
60
64
  }
65
+
66
+ char* extract_inline_block_content(pm_call_node_t* call_node, hb_allocator_T* allocator) {
67
+ if (!call_node || !call_node->block || call_node->block->type != PM_BLOCK_NODE) { return NULL; }
68
+
69
+ pm_block_node_t* block_node = (pm_block_node_t*) call_node->block;
70
+
71
+ if (!block_node->body || block_node->body->type != PM_STATEMENTS_NODE) { return NULL; }
72
+
73
+ pm_statements_node_t* statements = (pm_statements_node_t*) block_node->body;
74
+
75
+ if (statements->body.size != 1) { return NULL; }
76
+
77
+ pm_node_t* statement = statements->body.nodes[0];
78
+
79
+ if (statement->type == PM_STRING_NODE) {
80
+ pm_string_node_t* string_node = (pm_string_node_t*) statement;
81
+ size_t length = pm_string_length(&string_node->unescaped);
82
+ return hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
83
+ }
84
+
85
+ size_t source_length = statement->location.end - statement->location.start;
86
+ return hb_allocator_strndup(allocator, (const char*) statement->location.start, source_length);
87
+ }
@@ -36,17 +36,23 @@ char* extract_tag_dot_name(pm_call_node_t* call_node, pm_parser_t* parser, hb_al
36
36
  char* extract_tag_dot_content(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
37
37
  (void) parser;
38
38
 
39
- if (!call_node || !call_node->arguments) { return NULL; }
39
+ if (!call_node) { return NULL; }
40
40
 
41
- pm_arguments_node_t* arguments = call_node->arguments;
42
- if (!arguments->arguments.size) { return NULL; }
41
+ char* block_content = extract_inline_block_content(call_node, allocator);
42
+ if (block_content) { return block_content; }
43
43
 
44
- pm_node_t* first_argument = arguments->arguments.nodes[0];
44
+ if (call_node->arguments) {
45
+ pm_arguments_node_t* arguments = call_node->arguments;
45
46
 
46
- if (first_argument->type == PM_STRING_NODE) {
47
- pm_string_node_t* string_node = (pm_string_node_t*) first_argument;
48
- size_t length = pm_string_length(&string_node->unescaped);
49
- return hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
47
+ if (arguments->arguments.size) {
48
+ pm_node_t* first_argument = arguments->arguments.nodes[0];
49
+
50
+ if (first_argument->type == PM_STRING_NODE) {
51
+ pm_string_node_t* string_node = (pm_string_node_t*) first_argument;
52
+ size_t length = pm_string_length(&string_node->unescaped);
53
+ return hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
54
+ }
55
+ }
50
56
  }
51
57
 
52
58
  return NULL;
@@ -88,7 +88,8 @@ AST_HTML_ATTRIBUTE_NODE_T* create_html_attribute_node(
88
88
  AST_HTML_ATTRIBUTE_NAME_NODE_T* name_node =
89
89
  create_attribute_name_node(name_string, start_position, end_position, allocator);
90
90
 
91
- token_T* equals_token = create_synthetic_token(allocator, "=", TOKEN_EQUALS, start_position, end_position);
91
+ token_T* equals_token =
92
+ value_string ? create_synthetic_token(allocator, "=", TOKEN_EQUALS, start_position, end_position) : NULL;
92
93
  AST_HTML_ATTRIBUTE_VALUE_NODE_T* value_node = NULL;
93
94
 
94
95
  if (value_string) {
@@ -210,8 +211,20 @@ static AST_HTML_ATTRIBUTE_VALUE_NODE_T* create_interpolated_attribute_value(
210
211
  }
211
212
  }
212
213
  } else if (part->type == PM_EMBEDDED_STATEMENTS_NODE) {
213
- size_t ruby_length = part->location.end - part->location.start;
214
- char* ruby_content = hb_allocator_strndup(allocator, (const char*) part->location.start, ruby_length);
214
+ pm_embedded_statements_node_t* embedded = (pm_embedded_statements_node_t*) part;
215
+ const uint8_t* content_start;
216
+ const uint8_t* content_end;
217
+
218
+ if (embedded->statements) {
219
+ content_start = embedded->statements->base.location.start;
220
+ content_end = embedded->statements->base.location.end;
221
+ } else {
222
+ content_start = part->location.start;
223
+ content_end = part->location.end;
224
+ }
225
+
226
+ size_t ruby_length = content_end - content_start;
227
+ char* ruby_content = hb_allocator_strndup(allocator, (const char*) content_start, ruby_length);
215
228
 
216
229
  if (ruby_content) {
217
230
  AST_RUBY_LITERAL_NODE_T* ruby_node = ast_ruby_literal_node_init(