herb 0.10.3-aarch64-linux-gnu → 0.11.0-aarch64-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 (788) hide show
  1. checksums.yaml +4 -4
  2. data/Makefile +16 -6
  3. data/README.md +36 -119
  4. data/Rakefile +2 -11
  5. data/config/action_view_helpers/actioncable/action_cable_helper/action_cable_meta_tag.yml +12 -8
  6. data/config/action_view_helpers/actionpack/content_security_policy/content_security_policy?.yml +9 -6
  7. data/config/action_view_helpers/actionpack/content_security_policy/content_security_policy_nonce.yml +9 -6
  8. data/config/action_view_helpers/actionpack/polymorphic_routes/polymorphic_path.yml +20 -12
  9. data/config/action_view_helpers/actionpack/polymorphic_routes/polymorphic_url.yml +30 -18
  10. data/config/action_view_helpers/actionpack/request_forgery_protection/form_authenticity_token.yml +15 -9
  11. data/config/action_view_helpers/actionpack/request_forgery_protection/protect_against_forgery?.yml +9 -6
  12. data/config/action_view_helpers/actiontext/content_helper/render_action_text_content.yml +15 -9
  13. data/config/action_view_helpers/actiontext/content_helper/sanitize_action_text_content.yml +15 -9
  14. data/config/action_view_helpers/actiontext/tag_helper/rich_textarea_tag.yml +34 -22
  15. data/config/action_view_helpers/actionview/active_model_helper/error_message.yml +9 -6
  16. data/config/action_view_helpers/actionview/active_model_helper/error_wrapping.yml +9 -6
  17. data/config/action_view_helpers/actionview/active_model_helper/object.yml +9 -6
  18. data/config/action_view_helpers/actionview/asset_tag_helper/apply_stylesheet_media_default.yml +9 -6
  19. data/config/action_view_helpers/actionview/asset_tag_helper/audio_tag.yml +40 -27
  20. data/config/action_view_helpers/actionview/asset_tag_helper/auto_discovery_link_tag.yml +37 -23
  21. data/config/action_view_helpers/actionview/asset_tag_helper/auto_include_nonce.yml +9 -6
  22. data/config/action_view_helpers/actionview/asset_tag_helper/auto_include_nonce_for_scripts.yml +9 -6
  23. data/config/action_view_helpers/actionview/asset_tag_helper/auto_include_nonce_for_styles.yml +9 -6
  24. data/config/action_view_helpers/actionview/asset_tag_helper/favicon_link_tag.yml +32 -22
  25. data/config/action_view_helpers/actionview/asset_tag_helper/image_decoding.yml +9 -6
  26. data/config/action_view_helpers/actionview/asset_tag_helper/image_loading.yml +9 -6
  27. data/config/action_view_helpers/actionview/asset_tag_helper/image_tag.yml +67 -46
  28. data/config/action_view_helpers/actionview/asset_tag_helper/javascript_include_tag.yml +58 -39
  29. data/config/action_view_helpers/actionview/asset_tag_helper/picture_tag.yml +22 -14
  30. data/config/action_view_helpers/actionview/asset_tag_helper/preload_link_tag.yml +45 -30
  31. data/config/action_view_helpers/actionview/asset_tag_helper/preload_links_header.yml +9 -6
  32. data/config/action_view_helpers/actionview/asset_tag_helper/stylesheet_link_tag.yml +49 -30
  33. data/config/action_view_helpers/actionview/asset_tag_helper/video_tag.yml +53 -35
  34. data/config/action_view_helpers/actionview/asset_url_helper/asset_path.yml +16 -10
  35. data/config/action_view_helpers/actionview/asset_url_helper/asset_url.yml +16 -10
  36. data/config/action_view_helpers/actionview/asset_url_helper/audio_path.yml +16 -10
  37. data/config/action_view_helpers/actionview/asset_url_helper/audio_url.yml +16 -10
  38. data/config/action_view_helpers/actionview/asset_url_helper/compute_asset_extname.yml +9 -6
  39. data/config/action_view_helpers/actionview/asset_url_helper/compute_asset_host.yml +9 -6
  40. data/config/action_view_helpers/actionview/asset_url_helper/compute_asset_path.yml +9 -6
  41. data/config/action_view_helpers/actionview/asset_url_helper/font_path.yml +16 -10
  42. data/config/action_view_helpers/actionview/asset_url_helper/font_url.yml +16 -10
  43. data/config/action_view_helpers/actionview/asset_url_helper/image_path.yml +16 -10
  44. data/config/action_view_helpers/actionview/asset_url_helper/image_url.yml +16 -10
  45. data/config/action_view_helpers/actionview/asset_url_helper/javascript_path.yml +16 -10
  46. data/config/action_view_helpers/actionview/asset_url_helper/javascript_url.yml +16 -10
  47. data/config/action_view_helpers/actionview/asset_url_helper/public_compute_asset_path.yml +9 -6
  48. data/config/action_view_helpers/actionview/asset_url_helper/stylesheet_path.yml +16 -10
  49. data/config/action_view_helpers/actionview/asset_url_helper/stylesheet_url.yml +16 -10
  50. data/config/action_view_helpers/actionview/asset_url_helper/video_path.yml +16 -10
  51. data/config/action_view_helpers/actionview/asset_url_helper/video_url.yml +16 -10
  52. data/config/action_view_helpers/actionview/atom_feed_helper/atom_feed.yml +52 -27
  53. data/config/action_view_helpers/actionview/cache_helper/cache.yml +25 -15
  54. data/config/action_view_helpers/actionview/cache_helper/cache_fragment_name.yml +9 -6
  55. data/config/action_view_helpers/actionview/cache_helper/cache_if.yml +20 -12
  56. data/config/action_view_helpers/actionview/cache_helper/cache_unless.yml +20 -12
  57. data/config/action_view_helpers/actionview/cache_helper/caching_predicate.yml +9 -6
  58. data/config/action_view_helpers/actionview/cache_helper/digest_path_from_template.yml +9 -6
  59. data/config/action_view_helpers/actionview/cache_helper/fragment_for.yml +9 -6
  60. data/config/action_view_helpers/actionview/cache_helper/fragment_name_with_digest.yml +9 -6
  61. data/config/action_view_helpers/actionview/cache_helper/read_fragment_for.yml +9 -6
  62. data/config/action_view_helpers/actionview/cache_helper/uncacheable.yml +9 -6
  63. data/config/action_view_helpers/actionview/cache_helper/write_fragment_for.yml +9 -6
  64. data/config/action_view_helpers/actionview/capture_helper/capture.yml +9 -6
  65. data/config/action_view_helpers/actionview/capture_helper/content_for.yml +20 -12
  66. data/config/action_view_helpers/actionview/capture_helper/content_for_predicate.yml +15 -9
  67. data/config/action_view_helpers/actionview/capture_helper/provide.yml +15 -9
  68. data/config/action_view_helpers/actionview/capture_helper/with_output_buffer.yml +9 -6
  69. data/config/action_view_helpers/actionview/content_exfiltration_prevention_helper/prepend_content_exfiltration_prevention.yml +9 -6
  70. data/config/action_view_helpers/actionview/content_exfiltration_prevention_helper/prevent_content_exfiltration.yml +9 -6
  71. data/config/action_view_helpers/actionview/controller_helper/action_name.yml +9 -6
  72. data/config/action_view_helpers/actionview/controller_helper/assign_controller.yml +9 -6
  73. data/config/action_view_helpers/actionview/controller_helper/controller.yml +9 -6
  74. data/config/action_view_helpers/actionview/controller_helper/controller_name.yml +9 -6
  75. data/config/action_view_helpers/actionview/controller_helper/controller_path.yml +9 -6
  76. data/config/action_view_helpers/actionview/controller_helper/cookies.yml +9 -6
  77. data/config/action_view_helpers/actionview/controller_helper/flash.yml +9 -6
  78. data/config/action_view_helpers/actionview/controller_helper/headers.yml +9 -6
  79. data/config/action_view_helpers/actionview/controller_helper/logger.yml +9 -6
  80. data/config/action_view_helpers/actionview/controller_helper/params.yml +9 -6
  81. data/config/action_view_helpers/actionview/controller_helper/request.yml +9 -6
  82. data/config/action_view_helpers/actionview/controller_helper/request_forgery_protection_token.yml +9 -6
  83. data/config/action_view_helpers/actionview/controller_helper/response.yml +9 -6
  84. data/config/action_view_helpers/actionview/controller_helper/session.yml +9 -6
  85. data/config/action_view_helpers/actionview/csp_helper/csp_meta_tag.yml +18 -11
  86. data/config/action_view_helpers/actionview/csrf_helper/csrf_meta_tags.yml +16 -10
  87. data/config/action_view_helpers/actionview/date_helper/date_select.yml +85 -51
  88. data/config/action_view_helpers/actionview/date_helper/datetime_select.yml +65 -39
  89. data/config/action_view_helpers/actionview/date_helper/distance_of_time_in_words.yml +30 -18
  90. data/config/action_view_helpers/actionview/date_helper/relative_time_in_words.yml +35 -21
  91. data/config/action_view_helpers/actionview/date_helper/select_date.yml +50 -30
  92. data/config/action_view_helpers/actionview/date_helper/select_datetime.yml +65 -39
  93. data/config/action_view_helpers/actionview/date_helper/select_day.yml +40 -24
  94. data/config/action_view_helpers/actionview/date_helper/select_hour.yml +50 -30
  95. data/config/action_view_helpers/actionview/date_helper/select_minute.yml +40 -24
  96. data/config/action_view_helpers/actionview/date_helper/select_month.yml +60 -36
  97. data/config/action_view_helpers/actionview/date_helper/select_second.yml +35 -21
  98. data/config/action_view_helpers/actionview/date_helper/select_time.yml +55 -33
  99. data/config/action_view_helpers/actionview/date_helper/select_year.yml +45 -27
  100. data/config/action_view_helpers/actionview/date_helper/time_ago_in_words.yml +21 -13
  101. data/config/action_view_helpers/actionview/date_helper/time_select.yml +60 -36
  102. data/config/action_view_helpers/actionview/date_helper/time_tag.yml +32 -20
  103. data/config/action_view_helpers/actionview/debug_helper/debug.yml +15 -9
  104. data/config/action_view_helpers/actionview/form_builder/button.yml +60 -0
  105. data/config/action_view_helpers/actionview/form_builder/submit.yml +48 -0
  106. data/config/action_view_helpers/actionview/form_helper/apply_form_for_options.yml +9 -6
  107. data/config/action_view_helpers/actionview/form_helper/checkbox.yml +41 -25
  108. data/config/action_view_helpers/actionview/form_helper/color_field.yml +25 -15
  109. data/config/action_view_helpers/actionview/form_helper/date_field.yml +35 -21
  110. data/config/action_view_helpers/actionview/form_helper/datetime_field.yml +41 -25
  111. data/config/action_view_helpers/actionview/form_helper/email_field.yml +25 -15
  112. data/config/action_view_helpers/actionview/form_helper/emitted_hidden_id.yml +9 -6
  113. data/config/action_view_helpers/actionview/form_helper/fields.yml +37 -18
  114. data/config/action_view_helpers/actionview/form_helper/fields_for.yml +33 -15
  115. data/config/action_view_helpers/actionview/form_helper/file_field.yml +45 -27
  116. data/config/action_view_helpers/actionview/form_helper/form_for.yml +45 -24
  117. data/config/action_view_helpers/actionview/form_helper/form_with.yml +56 -30
  118. data/config/action_view_helpers/actionview/form_helper/form_with_generates_ids.yml +9 -6
  119. data/config/action_view_helpers/actionview/form_helper/form_with_generates_remote_forms.yml +9 -6
  120. data/config/action_view_helpers/actionview/form_helper/hidden_field.yml +25 -15
  121. data/config/action_view_helpers/actionview/form_helper/label.yml +52 -27
  122. data/config/action_view_helpers/actionview/form_helper/month_field.yml +25 -15
  123. data/config/action_view_helpers/actionview/form_helper/multipart.yml +9 -6
  124. data/config/action_view_helpers/actionview/form_helper/multiple_file_field_include_hidden.yml +9 -6
  125. data/config/action_view_helpers/actionview/form_helper/number_field.yml +45 -27
  126. data/config/action_view_helpers/actionview/form_helper/password_field.yml +25 -15
  127. data/config/action_view_helpers/actionview/form_helper/radio_button.yml +30 -18
  128. data/config/action_view_helpers/actionview/form_helper/range_field.yml +45 -27
  129. data/config/action_view_helpers/actionview/form_helper/search_field.yml +25 -15
  130. data/config/action_view_helpers/actionview/form_helper/telephone_field.yml +26 -16
  131. data/config/action_view_helpers/actionview/form_helper/text_field.yml +25 -15
  132. data/config/action_view_helpers/actionview/form_helper/textarea.yml +31 -19
  133. data/config/action_view_helpers/actionview/form_helper/time_field.yml +40 -24
  134. data/config/action_view_helpers/actionview/form_helper/url_field.yml +25 -15
  135. data/config/action_view_helpers/actionview/form_helper/week_field.yml +25 -15
  136. data/config/action_view_helpers/actionview/form_options_helper/collection_checkboxes.yml +58 -31
  137. data/config/action_view_helpers/actionview/form_options_helper/collection_radio_buttons.yml +57 -30
  138. data/config/action_view_helpers/actionview/form_options_helper/collection_select.yml +65 -39
  139. data/config/action_view_helpers/actionview/form_options_helper/grouped_collection_select.yml +65 -39
  140. data/config/action_view_helpers/actionview/form_options_helper/grouped_options_for_select.yml +35 -21
  141. data/config/action_view_helpers/actionview/form_options_helper/option_groups_from_collection_for_select.yml +40 -24
  142. data/config/action_view_helpers/actionview/form_options_helper/options_for_select.yml +20 -12
  143. data/config/action_view_helpers/actionview/form_options_helper/options_from_collection_for_select.yml +30 -18
  144. data/config/action_view_helpers/actionview/form_options_helper/select.yml +60 -36
  145. data/config/action_view_helpers/actionview/form_options_helper/time_zone_options_for_select.yml +25 -15
  146. data/config/action_view_helpers/actionview/form_options_helper/time_zone_select.yml +50 -30
  147. data/config/action_view_helpers/actionview/form_options_helper/weekday_options_for_select.yml +30 -18
  148. data/config/action_view_helpers/actionview/form_options_helper/weekday_select.yml +30 -18
  149. data/config/action_view_helpers/actionview/form_tag_helper/button_tag.yml +39 -21
  150. data/config/action_view_helpers/actionview/form_tag_helper/checkbox_tag.yml +33 -21
  151. data/config/action_view_helpers/actionview/form_tag_helper/color_field_tag.yml +26 -17
  152. data/config/action_view_helpers/actionview/form_tag_helper/date_field_tag.yml +27 -17
  153. data/config/action_view_helpers/actionview/form_tag_helper/datetime_field_tag.yml +28 -18
  154. data/config/action_view_helpers/actionview/form_tag_helper/default_enforce_utf8.yml +9 -6
  155. data/config/action_view_helpers/actionview/form_tag_helper/email_field_tag.yml +27 -17
  156. data/config/action_view_helpers/actionview/form_tag_helper/embed_authenticity_token_in_remote_forms.yml +9 -6
  157. data/config/action_view_helpers/actionview/form_tag_helper/field_id.yml +30 -18
  158. data/config/action_view_helpers/actionview/form_tag_helper/field_name.yml +30 -18
  159. data/config/action_view_helpers/actionview/form_tag_helper/field_set_tag.yml +23 -15
  160. data/config/action_view_helpers/actionview/form_tag_helper/file_field_tag.yml +35 -22
  161. data/config/action_view_helpers/actionview/form_tag_helper/form_tag.yml +38 -24
  162. data/config/action_view_helpers/actionview/form_tag_helper/hidden_field_tag.yml +27 -17
  163. data/config/action_view_helpers/actionview/form_tag_helper/image_submit_tag.yml +31 -21
  164. data/config/action_view_helpers/actionview/form_tag_helper/label_tag.yml +33 -17
  165. data/config/action_view_helpers/actionview/form_tag_helper/month_field_tag.yml +27 -17
  166. data/config/action_view_helpers/actionview/form_tag_helper/number_field_tag.yml +47 -29
  167. data/config/action_view_helpers/actionview/form_tag_helper/password_field_tag.yml +27 -17
  168. data/config/action_view_helpers/actionview/form_tag_helper/radio_button_tag.yml +32 -20
  169. data/config/action_view_helpers/actionview/form_tag_helper/range_field_tag.yml +47 -29
  170. data/config/action_view_helpers/actionview/form_tag_helper/search_field_tag.yml +27 -17
  171. data/config/action_view_helpers/actionview/form_tag_helper/select_tag.yml +47 -29
  172. data/config/action_view_helpers/actionview/form_tag_helper/submit_tag.yml +33 -21
  173. data/config/action_view_helpers/actionview/form_tag_helper/telephone_field_tag.yml +28 -18
  174. data/config/action_view_helpers/actionview/form_tag_helper/text_field_tag.yml +27 -17
  175. data/config/action_view_helpers/actionview/form_tag_helper/textarea_tag.yml +31 -20
  176. data/config/action_view_helpers/actionview/form_tag_helper/time_field_tag.yml +27 -17
  177. data/config/action_view_helpers/actionview/form_tag_helper/url_field_tag.yml +27 -17
  178. data/config/action_view_helpers/actionview/form_tag_helper/utf8_enforcer_tag.yml +9 -6
  179. data/config/action_view_helpers/actionview/form_tag_helper/week_field_tag.yml +28 -18
  180. data/config/action_view_helpers/actionview/javascript_helper/escape_javascript.yml +16 -10
  181. data/config/action_view_helpers/actionview/javascript_helper/javascript_cdata_section.yml +9 -6
  182. data/config/action_view_helpers/actionview/javascript_helper/javascript_tag.yml +41 -27
  183. data/config/action_view_helpers/actionview/number_helper/number_to_currency.yml +29 -18
  184. data/config/action_view_helpers/actionview/number_helper/number_to_human.yml +25 -15
  185. data/config/action_view_helpers/actionview/number_helper/number_to_human_size.yml +20 -12
  186. data/config/action_view_helpers/actionview/number_helper/number_to_percentage.yml +20 -12
  187. data/config/action_view_helpers/actionview/number_helper/number_to_phone.yml +33 -20
  188. data/config/action_view_helpers/actionview/number_helper/number_with_delimiter.yml +21 -13
  189. data/config/action_view_helpers/actionview/number_helper/number_with_precision.yml +25 -15
  190. data/config/action_view_helpers/actionview/output_safety_helper/raw.yml +15 -9
  191. data/config/action_view_helpers/actionview/output_safety_helper/safe_join.yml +20 -12
  192. data/config/action_view_helpers/actionview/output_safety_helper/to_sentence.yml +24 -15
  193. data/config/action_view_helpers/actionview/record_identifier/dom_class.yml +20 -12
  194. data/config/action_view_helpers/actionview/record_identifier/dom_id.yml +20 -12
  195. data/config/action_view_helpers/actionview/record_identifier/dom_target.yml +15 -9
  196. data/config/action_view_helpers/actionview/record_identifier/model_name_from_record_or_class.yml +9 -6
  197. data/config/action_view_helpers/actionview/rendering_helper/render.yml +108 -22
  198. data/config/action_view_helpers/actionview/sanitize_helper/sanitize.yml +25 -15
  199. data/config/action_view_helpers/actionview/sanitize_helper/sanitize_css.yml +15 -9
  200. data/config/action_view_helpers/actionview/sanitize_helper/sanitizer_vendor.yml +9 -6
  201. data/config/action_view_helpers/actionview/sanitize_helper/strip_links.yml +15 -9
  202. data/config/action_view_helpers/actionview/sanitize_helper/strip_tags.yml +15 -9
  203. data/config/action_view_helpers/actionview/tag_helper/cdata_section.yml +15 -9
  204. data/config/action_view_helpers/actionview/tag_helper/content_tag.yml +59 -38
  205. data/config/action_view_helpers/actionview/tag_helper/escape_once.yml +15 -9
  206. data/config/action_view_helpers/actionview/tag_helper/tag.yml +54 -31
  207. data/config/action_view_helpers/actionview/tag_helper/token_list.yml +16 -10
  208. data/config/action_view_helpers/actionview/text_helper/concat.yml +15 -9
  209. data/config/action_view_helpers/actionview/text_helper/current_cycle.yml +15 -9
  210. data/config/action_view_helpers/actionview/text_helper/cycle.yml +23 -14
  211. data/config/action_view_helpers/actionview/text_helper/excerpt.yml +33 -20
  212. data/config/action_view_helpers/actionview/text_helper/highlight.yml +37 -18
  213. data/config/action_view_helpers/actionview/text_helper/pluralize.yml +30 -18
  214. data/config/action_view_helpers/actionview/text_helper/reset_cycle.yml +15 -9
  215. data/config/action_view_helpers/actionview/text_helper/safe_concat.yml +9 -6
  216. data/config/action_view_helpers/actionview/text_helper/simple_format.yml +33 -20
  217. data/config/action_view_helpers/actionview/text_helper/truncate.yml +38 -23
  218. data/config/action_view_helpers/actionview/text_helper/word_wrap.yml +25 -15
  219. data/config/action_view_helpers/actionview/translation_helper/debug_missing_translation.yml +9 -6
  220. data/config/action_view_helpers/actionview/translation_helper/localize.yml +26 -16
  221. data/config/action_view_helpers/actionview/translation_helper/translate.yml +31 -19
  222. data/config/action_view_helpers/actionview/url_helper/button_to.yml +57 -33
  223. data/config/action_view_helpers/actionview/url_helper/button_to_generates_button_tag.yml +9 -6
  224. data/config/action_view_helpers/actionview/url_helper/current_page?.yml +25 -15
  225. data/config/action_view_helpers/actionview/url_helper/link_to.yml +76 -53
  226. data/config/action_view_helpers/actionview/url_helper/link_to_if.yml +54 -20
  227. data/config/action_view_helpers/actionview/url_helper/link_to_unless.yml +54 -20
  228. data/config/action_view_helpers/actionview/url_helper/link_to_unless_current.yml +49 -17
  229. data/config/action_view_helpers/actionview/url_helper/mail_to.yml +60 -34
  230. data/config/action_view_helpers/actionview/url_helper/phone_to.yml +40 -22
  231. data/config/action_view_helpers/actionview/url_helper/sms_to.yml +45 -25
  232. data/config/action_view_helpers/actionview/url_helper/url_for.yml +15 -9
  233. data/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_import_module_tag.yml +32 -0
  234. data/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_importmap_module_preload_tags.yml +39 -0
  235. data/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_importmap_tags.yml +35 -0
  236. data/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_inline_importmap_tag.yml +32 -0
  237. data/config/action_view_helpers/importmap-rails/importmap_tags_helper/javascript_module_preload_tag.yml +35 -0
  238. data/config/action_view_helpers/turbo-rails/drive_helper/turbo_exempts_page_from_cache.yml +9 -6
  239. data/config/action_view_helpers/turbo-rails/drive_helper/turbo_exempts_page_from_cache_tag.yml +12 -8
  240. data/config/action_view_helpers/turbo-rails/drive_helper/turbo_exempts_page_from_preview.yml +9 -6
  241. data/config/action_view_helpers/turbo-rails/drive_helper/turbo_exempts_page_from_preview_tag.yml +12 -8
  242. data/config/action_view_helpers/turbo-rails/drive_helper/turbo_page_requires_reload.yml +9 -6
  243. data/config/action_view_helpers/turbo-rails/drive_helper/turbo_page_requires_reload_tag.yml +12 -8
  244. data/config/action_view_helpers/turbo-rails/drive_helper/turbo_refresh_method_tag.yml +17 -11
  245. data/config/action_view_helpers/turbo-rails/drive_helper/turbo_refresh_scroll_tag.yml +17 -11
  246. data/config/action_view_helpers/turbo-rails/drive_helper/turbo_refreshes_with.yml +20 -12
  247. data/config/action_view_helpers/turbo-rails/frames_helper/turbo_frame_tag.yml +54 -33
  248. data/config/action_view_helpers/turbo-rails/includes_helper/turbo_include_tags.yml +9 -6
  249. data/config/action_view_helpers/turbo-rails/streams_action_helper/turbo_stream_action_tag.yml +32 -20
  250. data/config/action_view_helpers/turbo-rails/streams_action_helper/turbo_stream_refresh_tag.yml +18 -11
  251. data/config/action_view_helpers/turbo-rails/streams_helper/turbo_stream.yml +9 -6
  252. data/config/action_view_helpers/turbo-rails/streams_helper/turbo_stream_from.yml +28 -18
  253. data/config/html_elements.yml +597 -0
  254. data/config/options.yml +1 -0
  255. data/config/slots/components.yml +52 -0
  256. data/config/state/kind.yml +61 -0
  257. data/config/state/operators.yml +27 -0
  258. data/config/state/predicates.yml +39 -0
  259. data/config/state/transforms.yml +18 -0
  260. data/config/view_context_methods/actionview/base/annotate_rendered_view_with_filenames.yml +4 -4
  261. data/config/view_context_methods/actionview/base/assign.yml +4 -4
  262. data/config/view_context_methods/actionview/base/assigns.yml +4 -4
  263. data/config/view_context_methods/actionview/base/automatically_disable_submit_tag.yml +4 -4
  264. data/config/view_context_methods/actionview/base/compiled_method_container.yml +4 -4
  265. data/config/view_context_methods/actionview/base/config.yml +4 -4
  266. data/config/view_context_methods/actionview/base/default_form_builder.yml +4 -4
  267. data/config/view_context_methods/actionview/base/default_formats.yml +4 -4
  268. data/config/view_context_methods/actionview/base/field_error_proc.yml +4 -4
  269. data/config/view_context_methods/actionview/base/formats.yml +4 -4
  270. data/config/view_context_methods/actionview/base/in_rendering_context.yml +4 -4
  271. data/config/view_context_methods/actionview/base/locale.yml +4 -4
  272. data/config/view_context_methods/actionview/base/lookup_context.yml +4 -4
  273. data/config/view_context_methods/actionview/base/output_buffer.yml +4 -4
  274. data/config/view_context_methods/actionview/base/prefix_partial_path_with_controller_namespace.yml +4 -4
  275. data/config/view_context_methods/actionview/base/remove_hidden_field_autocomplete.yml +4 -4
  276. data/config/view_context_methods/actionview/base/streaming_completion_on_exception.yml +4 -4
  277. data/config/view_context_methods/actionview/base/view_flow.yml +4 -4
  278. data/config/view_context_methods/actionview/base/view_paths.yml +4 -4
  279. data/config/view_context_methods/actionview/base/view_renderer.yml +4 -4
  280. data/config/view_context_methods/actionview/template/local_assigns.yml +4 -4
  281. data/config/view_context_methods/activemodel/conversion/convert_to_model.yml +4 -4
  282. data/config/view_context_methods/activesupport/benchmarkable/benchmark.yml +4 -4
  283. data/config.yml +1240 -790
  284. data/exe/herb +2 -0
  285. data/ext/herb/error_helpers.c +355 -17
  286. data/ext/herb/error_helpers.h +1 -1
  287. data/ext/herb/extconf.rb +4 -2
  288. data/ext/herb/extension.c +137 -89
  289. data/ext/herb/extension_helpers.c +81 -42
  290. data/ext/herb/extension_helpers.h +4 -1
  291. data/ext/herb/nodes.c +826 -358
  292. data/ext/herb/nodes.h +3 -2
  293. data/ext/herb/parser_options_helpers.c +151 -0
  294. data/ext/herb/parser_options_helpers.h +14 -0
  295. data/herb.gemspec +2 -2
  296. data/lib/herb/3.2/herb.so +0 -0
  297. data/lib/herb/3.3/herb.so +0 -0
  298. data/lib/herb/3.4/herb.so +0 -0
  299. data/lib/herb/4.0/herb.so +0 -0
  300. data/lib/herb/action_view/helper_registry.rb +1358 -62
  301. data/lib/herb/analysis/partial_declaration.rb +140 -0
  302. data/lib/herb/analysis/partial_index.rb +189 -0
  303. data/lib/herb/analysis/partial_resolution.rb +161 -0
  304. data/lib/herb/analysis/partial_resolver.rb +144 -0
  305. data/lib/herb/analysis/project_index.rb +152 -0
  306. data/lib/herb/{action_view → analysis}/render_analyzer.rb +287 -77
  307. data/lib/herb/analysis/render_graph/ancestor_chain.rb +27 -0
  308. data/lib/herb/analysis/render_graph/builder.rb +376 -0
  309. data/lib/herb/analysis/render_graph/call_frame.rb +9 -0
  310. data/lib/herb/analysis/render_graph/call_site_location.rb +23 -0
  311. data/lib/herb/analysis/render_graph/inferred_signature.rb +21 -0
  312. data/lib/herb/analysis/render_graph/partial_call_site.rb +40 -0
  313. data/lib/herb/analysis/render_graph/partial_context.rb +46 -0
  314. data/lib/herb/analysis/render_graph/template_roots.rb +31 -0
  315. data/lib/herb/analysis/render_graph.rb +285 -0
  316. data/lib/herb/analysis/ruby_locals_index/local.rb +25 -0
  317. data/lib/herb/analysis/ruby_locals_index/named_reference.rb +22 -0
  318. data/lib/herb/analysis/ruby_locals_index/offset_table.rb +44 -0
  319. data/lib/herb/analysis/ruby_locals_index/reference_collector.rb +80 -0
  320. data/lib/herb/analysis/ruby_locals_index/state_locals.rb +49 -0
  321. data/lib/herb/analysis/ruby_locals_index.rb +165 -0
  322. data/lib/herb/analysis/ruby_reference.rb +67 -0
  323. data/lib/herb/analysis/template_dependencies/dependency_collector.rb +179 -0
  324. data/lib/herb/analysis/template_dependencies/node_dependency_collector.rb +309 -0
  325. data/lib/herb/analysis/template_dependencies.rb +562 -0
  326. data/lib/herb/ast/erb_render_node.rb +26 -94
  327. data/lib/herb/ast/helpers.rb +90 -10
  328. data/lib/herb/ast/herb_directive_node.rb +18 -0
  329. data/lib/herb/ast/node.rb +32 -2
  330. data/lib/herb/ast/nodes.rb +1342 -105
  331. data/lib/herb/backend.rb +73 -0
  332. data/lib/herb/bootstrap.rb +3 -7
  333. data/lib/herb/cli.rb +550 -32
  334. data/lib/herb/colors.rb +14 -0
  335. data/lib/herb/configuration.rb +71 -35
  336. data/lib/herb/defaults.yml +2 -1
  337. data/lib/herb/dev/assets.rb +57 -0
  338. data/lib/herb/dev/classifier.rb +84 -0
  339. data/lib/herb/dev/embedded.rb +27 -0
  340. data/lib/herb/dev/pipeline.rb +305 -0
  341. data/lib/herb/dev/protocol.rb +221 -0
  342. data/lib/herb/dev/runner.rb +175 -175
  343. data/lib/herb/dev/server.rb +89 -26
  344. data/lib/herb/dev/server_entry.rb +41 -6
  345. data/lib/herb/dev/watcher.rb +181 -0
  346. data/lib/herb/dev.rb +163 -0
  347. data/lib/herb/diagnostic/formatter.rb +88 -0
  348. data/lib/herb/diagnostic.rb +172 -0
  349. data/lib/herb/diff/operation.rb +36 -0
  350. data/lib/herb/diff/result.rb +62 -0
  351. data/lib/herb/diff.rb +10 -0
  352. data/lib/herb/engine/collector.rb +62 -0
  353. data/lib/herb/engine/compiler.rb +346 -108
  354. data/lib/herb/engine/component_tags/constant_resolver.rb +28 -0
  355. data/lib/herb/engine/component_tags/partial_resolver.rb +54 -0
  356. data/lib/herb/engine/component_tags/resolver.rb +32 -0
  357. data/lib/herb/engine/component_tags/visitor.rb +231 -0
  358. data/lib/herb/engine/css_inliner/inliner.rb +101 -0
  359. data/lib/herb/engine/css_inliner/visitor.rb +174 -0
  360. data/lib/herb/engine/errors.rb +122 -0
  361. data/lib/herb/engine/helpers.rb +82 -0
  362. data/lib/herb/engine/inline_render/inliner.rb +122 -0
  363. data/lib/herb/engine/inline_render/visitor.rb +267 -0
  364. data/lib/herb/engine/runtime/entry.rb +62 -0
  365. data/lib/herb/engine/runtime/error_page.rb +362 -0
  366. data/lib/herb/engine/runtime/html_safe_assertions.rb +217 -0
  367. data/lib/herb/engine/runtime/journal/summary.rb +227 -0
  368. data/lib/herb/engine/runtime/journal.rb +379 -0
  369. data/lib/herb/engine/runtime/middleware.rb +212 -0
  370. data/lib/herb/engine/runtime/observations.rb +73 -0
  371. data/lib/herb/engine/runtime/report.rb +204 -0
  372. data/lib/herb/engine/runtime/session.rb +437 -0
  373. data/lib/herb/engine/runtime.rb +21 -0
  374. data/lib/herb/engine/scoped_style/channel.rb +66 -0
  375. data/lib/herb/engine/scoped_style/collector.rb +74 -0
  376. data/lib/herb/engine/scoped_style/visitor.rb +533 -0
  377. data/lib/herb/engine/slots/annotation.rb +98 -0
  378. data/lib/herb/engine/slots/bindings.rb +61 -0
  379. data/lib/herb/engine/slots/callee.rb +118 -0
  380. data/lib/herb/engine/slots/component_definitions.rb +67 -0
  381. data/lib/herb/engine/slots/components/async.rb +24 -0
  382. data/lib/herb/engine/slots/components/base.rb +132 -0
  383. data/lib/herb/engine/slots/components/deferred.rb +81 -0
  384. data/lib/herb/engine/slots/components/fallback.rb +27 -0
  385. data/lib/herb/engine/slots/components/fragment.rb +99 -0
  386. data/lib/herb/engine/slots/components/lazy.rb +24 -0
  387. data/lib/herb/engine/slots/components.rb +52 -0
  388. data/lib/herb/engine/slots/dependencies/channel.rb +72 -0
  389. data/lib/herb/engine/slots/dependencies.rb +269 -0
  390. data/lib/herb/engine/slots/dynamics_compiler.rb +854 -0
  391. data/lib/herb/engine/slots/identifier.rb +35 -0
  392. data/lib/herb/engine/slots/manifest/channel.rb +74 -0
  393. data/lib/herb/engine/slots/manifest/collector.rb +75 -0
  394. data/lib/herb/engine/slots/markers.rb +128 -0
  395. data/lib/herb/engine/slots/render_bindings.rb +141 -0
  396. data/lib/herb/engine/slots/schema_compiler.rb +94 -0
  397. data/lib/herb/engine/slots/state_anchor.rb +94 -0
  398. data/lib/herb/engine/slots/state_compiler.rb +1326 -0
  399. data/lib/herb/engine/slots/state_directives.rb +854 -0
  400. data/lib/herb/engine/slots/state_kinds.rb +54 -0
  401. data/lib/herb/engine/slots/state_operators.rb +36 -0
  402. data/lib/herb/engine/slots/state_overrides.rb +79 -0
  403. data/lib/herb/engine/slots/state_predicates.rb +65 -0
  404. data/lib/herb/engine/slots/state_transforms.rb +42 -0
  405. data/lib/herb/engine/slots/statics.rb +185 -0
  406. data/lib/herb/engine/slots/subtree.rb +82 -0
  407. data/lib/herb/engine/slots/subtree_compiler.rb +255 -0
  408. data/lib/herb/engine/slots/types.rb +42 -0
  409. data/lib/herb/engine/slots/visitor.rb +2256 -0
  410. data/lib/herb/engine/validators/accessibility_validator.rb +3 -3
  411. data/lib/herb/engine/validators/base.rb +36 -0
  412. data/lib/herb/engine/validators/generator_template_validator.rb +39 -0
  413. data/lib/herb/engine/validators/nesting_validator.rb +3 -3
  414. data/lib/herb/engine/validators/render_validator.rb +13 -33
  415. data/lib/herb/engine/validators/security_validator.rb +3 -17
  416. data/lib/herb/engine/validators.rb +43 -0
  417. data/lib/herb/engine/visitors/auto_close_omitted_tags_visitor.rb +48 -0
  418. data/lib/herb/engine/visitors/content_for_visitor.rb +127 -0
  419. data/lib/herb/engine/{debug_visitor.rb → visitors/debug_visitor.rb} +121 -93
  420. data/lib/herb/engine/visitors/html_safe_assertions_visitor.rb +281 -0
  421. data/lib/herb/engine/visitors/instrumentation_visitor.rb +290 -0
  422. data/lib/herb/engine/visitors/optimize_visitor.rb +728 -0
  423. data/lib/herb/engine/visitors/remove_comments_visitor.rb +106 -0
  424. data/lib/herb/engine/visitors/source_attribution_visitor.rb +177 -0
  425. data/lib/herb/engine.rb +218 -257
  426. data/lib/herb/ensure_installed.rb +43 -0
  427. data/lib/herb/errors.rb +646 -157
  428. data/lib/herb/fingerprint.rb +82 -0
  429. data/lib/herb/html/elements.rb +18 -0
  430. data/lib/herb/html/util.rb +21 -2
  431. data/lib/herb/lex_result.rb +0 -10
  432. data/lib/herb/locate.rb +163 -0
  433. data/lib/herb/location.rb +20 -0
  434. data/lib/herb/parse_result.rb +17 -11
  435. data/lib/herb/parser_options.rb +65 -46
  436. data/lib/herb/position.rb +19 -0
  437. data/lib/herb/prism_inspect.rb +1 -4
  438. data/lib/herb/project.rb +31 -18
  439. data/lib/herb/range.rb +5 -0
  440. data/lib/herb/result.rb +2 -2
  441. data/lib/herb/ruby_program.rb +110 -0
  442. data/lib/herb/source_path.rb +232 -0
  443. data/lib/herb/token.rb +14 -3
  444. data/lib/herb/version.rb +1 -1
  445. data/lib/herb/visitor/context/origin.rb +52 -0
  446. data/lib/herb/visitor/context/replacements.rb +59 -0
  447. data/lib/herb/visitor/context.rb +169 -0
  448. data/lib/herb/visitor/context_aware.rb +45 -0
  449. data/lib/herb/visitor/diagnostics.rb +148 -0
  450. data/lib/herb/visitor/experimental.rb +33 -0
  451. data/lib/herb/visitor/parser_option_requirements.rb +151 -0
  452. data/lib/herb/visitor/stack.rb +192 -0
  453. data/lib/herb/visitor.rb +47 -1
  454. data/lib/herb/warnings.rb +25 -0
  455. data/lib/herb.rb +74 -63
  456. data/sig/herb/action_view/helper_registry.rbs +82 -10
  457. data/sig/herb/analysis/partial_declaration.rbs +80 -0
  458. data/sig/herb/analysis/partial_index.rbs +66 -0
  459. data/sig/herb/analysis/partial_resolution.rbs +57 -0
  460. data/sig/herb/analysis/partial_resolver.rbs +76 -0
  461. data/sig/herb/analysis/project_index.rbs +57 -0
  462. data/sig/herb/{action_view → analysis}/render_analyzer.rbs +25 -4
  463. data/sig/herb/analysis/render_graph/ancestor_chain.rbs +22 -0
  464. data/sig/herb/analysis/render_graph/builder.rbs +180 -0
  465. data/sig/herb/analysis/render_graph/call_frame.rbs +26 -0
  466. data/sig/herb/analysis/render_graph/call_site_location.rbs +28 -0
  467. data/sig/herb/analysis/render_graph/inferred_signature.rbs +27 -0
  468. data/sig/herb/analysis/render_graph/partial_call_site.rbs +36 -0
  469. data/sig/herb/analysis/render_graph/partial_context.rbs +31 -0
  470. data/sig/herb/analysis/render_graph/template_roots.rbs +32 -0
  471. data/sig/herb/analysis/render_graph.rbs +83 -0
  472. data/sig/herb/analysis/ruby_locals_index/local.rbs +21 -0
  473. data/sig/herb/analysis/ruby_locals_index/named_reference.rbs +20 -0
  474. data/sig/herb/analysis/ruby_locals_index/offset_table.rbs +26 -0
  475. data/sig/herb/analysis/ruby_locals_index/reference_collector.rbs +40 -0
  476. data/sig/herb/analysis/ruby_locals_index/state_locals.rbs +19 -0
  477. data/sig/herb/analysis/ruby_locals_index.rbs +62 -0
  478. data/sig/herb/analysis/ruby_reference.rbs +28 -0
  479. data/sig/herb/analysis/template_dependencies/dependency_collector.rbs +52 -0
  480. data/sig/herb/analysis/template_dependencies/node_dependency_collector.rbs +81 -0
  481. data/sig/herb/analysis/template_dependencies.rbs +125 -0
  482. data/sig/herb/ast/erb_render_node.rbs +10 -2
  483. data/sig/herb/ast/helpers.rbs +31 -1
  484. data/sig/herb/ast/herb_directive_node.rbs +13 -0
  485. data/sig/herb/ast/node.rbs +9 -0
  486. data/sig/herb/ast/nodes.rbs +662 -89
  487. data/sig/herb/backend.rbs +52 -0
  488. data/sig/herb/colors.rbs +3 -0
  489. data/sig/herb/configuration.rbs +27 -3
  490. data/sig/herb/dev/assets.rbs +27 -0
  491. data/sig/herb/dev/classifier.rbs +57 -0
  492. data/sig/herb/dev/embedded.rbs +21 -0
  493. data/sig/herb/dev/pipeline.rbs +118 -0
  494. data/sig/herb/dev/protocol.rbs +78 -0
  495. data/sig/herb/dev/runner.rbs +61 -23
  496. data/sig/herb/dev/server.rbs +49 -22
  497. data/sig/herb/dev/server_entry.rbs +46 -21
  498. data/sig/herb/dev/watcher.rbs +84 -0
  499. data/sig/herb/dev.rbs +58 -0
  500. data/sig/herb/diagnostic/formatter.rbs +28 -0
  501. data/sig/herb/diagnostic.rbs +88 -0
  502. data/sig/herb/diff/operation.rbs +36 -0
  503. data/sig/herb/diff/result.rbs +36 -0
  504. data/sig/herb/diff.rbs +6 -0
  505. data/sig/herb/engine/collector.rbs +48 -0
  506. data/sig/herb/engine/compiler.rbs +62 -23
  507. data/sig/herb/engine/component_tags/constant_resolver.rbs +19 -0
  508. data/sig/herb/engine/component_tags/partial_resolver.rbs +30 -0
  509. data/sig/herb/engine/component_tags/resolver.rbs +25 -0
  510. data/sig/herb/engine/component_tags/visitor.rbs +69 -0
  511. data/sig/herb/engine/css_inliner/inliner.rbs +65 -0
  512. data/sig/herb/engine/css_inliner/visitor.rbs +87 -0
  513. data/sig/herb/engine/errors.rbs +68 -0
  514. data/sig/herb/engine/helpers.rbs +28 -0
  515. data/sig/herb/engine/inline_render/inliner.rbs +39 -0
  516. data/sig/herb/engine/inline_render/visitor.rbs +104 -0
  517. data/sig/herb/engine/runtime/entry.rbs +43 -0
  518. data/sig/herb/engine/runtime/error_page.rbs +115 -0
  519. data/sig/herb/engine/runtime/html_safe_assertions.rbs +99 -0
  520. data/sig/herb/engine/runtime/journal/summary.rbs +172 -0
  521. data/sig/herb/engine/runtime/journal.rbs +126 -0
  522. data/sig/herb/engine/runtime/middleware.rbs +96 -0
  523. data/sig/herb/engine/runtime/observations.rbs +38 -0
  524. data/sig/herb/engine/runtime/report.rbs +97 -0
  525. data/sig/herb/engine/runtime/session.rbs +233 -0
  526. data/sig/herb/engine/runtime.rbs +14 -0
  527. data/sig/herb/engine/scoped_style/channel.rbs +45 -0
  528. data/sig/herb/engine/scoped_style/collector.rbs +45 -0
  529. data/sig/herb/engine/scoped_style/visitor.rbs +220 -0
  530. data/sig/herb/engine/slots/annotation.rbs +72 -0
  531. data/sig/herb/engine/slots/bindings.rbs +34 -0
  532. data/sig/herb/engine/slots/callee.rbs +62 -0
  533. data/sig/herb/engine/slots/component_definitions.rbs +19 -0
  534. data/sig/herb/engine/slots/components/async.rbs +20 -0
  535. data/sig/herb/engine/slots/components/base.rbs +60 -0
  536. data/sig/herb/engine/slots/components/deferred.rbs +29 -0
  537. data/sig/herb/engine/slots/components/fallback.rbs +21 -0
  538. data/sig/herb/engine/slots/components/fragment.rbs +34 -0
  539. data/sig/herb/engine/slots/components/lazy.rbs +20 -0
  540. data/sig/herb/engine/slots/components.rbs +30 -0
  541. data/sig/herb/engine/slots/dependencies/channel.rbs +51 -0
  542. data/sig/herb/engine/slots/dependencies.rbs +94 -0
  543. data/sig/herb/engine/slots/dynamics_compiler.rbs +349 -0
  544. data/sig/herb/engine/slots/identifier.rbs +19 -0
  545. data/sig/herb/engine/slots/manifest/channel.rbs +49 -0
  546. data/sig/herb/engine/slots/manifest/collector.rbs +45 -0
  547. data/sig/herb/engine/slots/markers.rbs +85 -0
  548. data/sig/herb/engine/slots/render_bindings.rbs +69 -0
  549. data/sig/herb/engine/slots/schema_compiler.rbs +62 -0
  550. data/sig/herb/engine/slots/state_anchor.rbs +41 -0
  551. data/sig/herb/engine/slots/state_compiler.rbs +258 -0
  552. data/sig/herb/engine/slots/state_directives.rbs +278 -0
  553. data/sig/herb/engine/slots/state_kinds.rbs +24 -0
  554. data/sig/herb/engine/slots/state_operators.rbs +19 -0
  555. data/sig/herb/engine/slots/state_overrides.rbs +33 -0
  556. data/sig/herb/engine/slots/state_predicates.rbs +16 -0
  557. data/sig/herb/engine/slots/state_transforms.rbs +15 -0
  558. data/sig/herb/engine/slots/statics.rbs +67 -0
  559. data/sig/herb/engine/slots/subtree.rbs +49 -0
  560. data/sig/herb/engine/slots/subtree_compiler.rbs +130 -0
  561. data/sig/herb/engine/slots/types.rbs +46 -0
  562. data/sig/herb/engine/slots/visitor.rbs +599 -0
  563. data/sig/herb/engine/validators/accessibility_validator.rbs +1 -1
  564. data/sig/herb/engine/validators/base.rbs +28 -0
  565. data/sig/herb/engine/validators/generator_template_validator.rbs +19 -0
  566. data/sig/herb/engine/validators/nesting_validator.rbs +1 -1
  567. data/sig/herb/engine/validators/render_validator.rbs +2 -6
  568. data/sig/herb/engine/validators/security_validator.rbs +1 -3
  569. data/sig/herb/engine/validators.rbs +21 -0
  570. data/sig/herb/engine/visitors/auto_close_omitted_tags_visitor.rbs +26 -0
  571. data/sig/herb/engine/visitors/content_for_visitor.rbs +52 -0
  572. data/sig/herb/engine/{debug_visitor.rbs → visitors/debug_visitor.rbs} +26 -15
  573. data/sig/herb/engine/visitors/html_safe_assertions_visitor.rbs +100 -0
  574. data/sig/herb/engine/visitors/instrumentation_visitor.rbs +111 -0
  575. data/sig/herb/engine/visitors/optimize_visitor.rbs +246 -0
  576. data/sig/herb/engine/visitors/remove_comments_visitor.rbs +66 -0
  577. data/sig/herb/engine/visitors/source_attribution_visitor.rbs +90 -0
  578. data/sig/herb/engine.rbs +47 -41
  579. data/sig/herb/ensure_installed.rbs +9 -0
  580. data/sig/herb/errors.rbs +293 -0
  581. data/sig/herb/fingerprint.rbs +37 -0
  582. data/sig/herb/html/elements.rbs +19 -0
  583. data/sig/herb/html/util.rbs +15 -1
  584. data/sig/herb/lex_result.rbs +0 -6
  585. data/sig/herb/locate.rbs +89 -0
  586. data/sig/herb/location.rbs +12 -0
  587. data/sig/herb/parse_result.rbs +10 -8
  588. data/sig/herb/parser_options.rbs +47 -21
  589. data/sig/herb/position.rbs +11 -0
  590. data/sig/herb/range.rbs +3 -0
  591. data/sig/herb/ruby_program.rbs +51 -0
  592. data/sig/herb/source_path.rbs +132 -0
  593. data/sig/herb/token.rbs +5 -2
  594. data/sig/herb/visitor/context/origin.rbs +51 -0
  595. data/sig/herb/visitor/context/replacements.rbs +39 -0
  596. data/sig/herb/visitor/context.rbs +83 -0
  597. data/sig/herb/visitor/context_aware.rbs +32 -0
  598. data/sig/herb/visitor/diagnostics.rbs +82 -0
  599. data/sig/herb/visitor/experimental.rbs +23 -0
  600. data/sig/herb/visitor/parser_option_requirements.rbs +76 -0
  601. data/sig/herb/visitor/stack.rbs +91 -0
  602. data/sig/herb/visitor.rbs +22 -0
  603. data/sig/herb/warnings.rbs +10 -0
  604. data/sig/herb.rbs +27 -10
  605. data/sig/serialized_ast_errors.rbs +58 -1
  606. data/sig/serialized_ast_nodes.rbs +58 -2
  607. data/src/analyze/action_view/attribute_extraction_helpers.c +73 -36
  608. data/src/analyze/action_view/generated_handlers.c +37 -1
  609. data/src/analyze/action_view/generated_handlers.h +2 -1
  610. data/src/analyze/action_view/helper_registry.c +1353 -58
  611. data/src/analyze/action_view/registry.c +10 -17
  612. data/src/analyze/action_view/stylesheet_link_tag.c +40 -0
  613. data/src/analyze/action_view/tag_helper_node_builders.c +57 -0
  614. data/src/analyze/action_view/tag_helpers.c +423 -93
  615. data/src/analyze/analyze.c +301 -64
  616. data/src/analyze/analyze_helpers.c +420 -23
  617. data/src/analyze/builders.c +79 -51
  618. data/src/analyze/conditional_elements.c +13 -8
  619. data/src/analyze/conditional_open_tags.c +11 -5
  620. data/src/analyze/control_type.c +16 -1
  621. data/src/analyze/herb_directives.c +701 -0
  622. data/src/analyze/invalid_structures.c +11 -7
  623. data/src/analyze/iteration_nodes.c +306 -0
  624. data/src/analyze/missing_end.c +116 -32
  625. data/src/analyze/parse_errors.c +8 -3
  626. data/src/analyze/postfix_conditionals.c +98 -63
  627. data/src/analyze/prism_annotate.c +13 -4
  628. data/src/analyze/render_nodes.c +345 -123
  629. data/src/analyze/strict_locals.c +30 -26
  630. data/src/analyze/ternary_conditionals.c +95 -70
  631. data/src/analyze/transform.c +43 -1
  632. data/src/ast/ast_node.c +3 -13
  633. data/src/ast/ast_nodes.c +911 -112
  634. data/src/ast/ast_pretty_print.c +161 -2
  635. data/src/diff/herb_diff.c +8 -0
  636. data/src/diff/herb_diff_attributes.c +1 -0
  637. data/src/diff/herb_diff_children.c +4 -3
  638. data/src/diff/herb_diff_helpers.c +7 -1
  639. data/src/diff/herb_diff_nodes.c +186 -71
  640. data/src/diff/herb_hash_tree.c +99 -1
  641. data/src/errors.c +1096 -128
  642. data/src/extract.c +30 -8
  643. data/src/herb.c +30 -11
  644. data/src/include/analyze/action_view/attribute_extraction_helpers.h +1 -1
  645. data/src/include/analyze/action_view/helper_registry.h +28 -4
  646. data/src/include/analyze/action_view/tag_helper_handler.h +1 -1
  647. data/src/include/analyze/action_view/tag_helper_node_builders.h +7 -0
  648. data/src/include/analyze/analyze.h +11 -0
  649. data/src/include/analyze/builders.h +16 -0
  650. data/src/include/analyze/helpers.h +23 -2
  651. data/src/include/analyze/herb_directive_kinds.h +41 -0
  652. data/src/include/analyze/herb_directives.h +14 -0
  653. data/src/include/analyze/iteration_nodes.h +11 -0
  654. data/src/include/analyze/prism_annotate.h +3 -3
  655. data/src/include/analyze/ternary_conditionals.h +2 -0
  656. data/src/include/ast/ast_node.h +1 -5
  657. data/src/include/ast/ast_nodes.h +76 -2
  658. data/src/include/ast/ast_pretty_print.h +1 -1
  659. data/src/include/diff/herb_diff.h +11 -0
  660. data/src/include/errors.h +156 -39
  661. data/src/include/extract.h +11 -0
  662. data/src/include/herb.h +6 -0
  663. data/src/include/lexer/erb_openers.h +29 -0
  664. data/src/include/lexer/lexer_peek_helpers.h +8 -1
  665. data/src/include/lexer/lexer_struct.h +4 -0
  666. data/src/include/lexer/token.h +3 -0
  667. data/src/include/lexer/token_struct.h +8 -6
  668. data/src/include/lib/hb_allocator.h +2 -1
  669. data/src/include/lib/hb_array.h +2 -0
  670. data/src/include/lib/hb_foreach.h +1 -1
  671. data/src/include/lib/hb_string.h +7 -0
  672. data/src/include/parser/dot_notation.h +1 -1
  673. data/src/include/parser/foreign_content_elements.h +32 -0
  674. data/src/include/parser/parser.h +16 -30
  675. data/src/include/parser/parser_helpers.h +13 -11
  676. data/src/include/parser/parser_options.h +41 -0
  677. data/src/include/prism/ruby_parser.h +6 -0
  678. data/src/include/util/html_elements.h +85 -0
  679. data/src/include/util/html_util.h +1 -0
  680. data/src/include/version.h +1 -1
  681. data/src/lexer/lexer_peek_helpers.c +15 -1
  682. data/src/lexer/token.c +83 -1
  683. data/src/lexer.c +196 -12
  684. data/src/lib/hb_allocator.c +2 -0
  685. data/src/lib/hb_array.c +26 -0
  686. data/src/lib/hb_string.c +26 -0
  687. data/src/main.c +1 -1
  688. data/src/parser/dot_notation.c +4 -3
  689. data/src/parser/match_tags.c +66 -1
  690. data/src/parser/parser_helpers.c +69 -37
  691. data/src/parser.c +606 -383
  692. data/src/parser_options_defaults.c +30 -0
  693. data/src/prism/prism_helpers.c +3 -0
  694. data/src/prism/ruby_parser.c +49 -0
  695. data/src/util/html_util.c +11 -58
  696. data/src/visitor.c +77 -1
  697. data/templates/ext/herb/error_helpers.c.erb +1 -1
  698. data/templates/ext/herb/nodes.c.erb +18 -13
  699. data/templates/ext/herb/nodes.h.erb +2 -1
  700. data/templates/ext/herb/parser_options_helpers.c.erb +69 -0
  701. data/templates/ext/herb/parser_options_helpers.h.erb +11 -0
  702. data/templates/java/nodes.c.erb +20 -16
  703. data/templates/java/nodes.h.erb +3 -2
  704. data/templates/java/org/herb/ParserOptions.java.erb +24 -0
  705. data/templates/java/org/herb/ast/HelperRegistry.java.erb +67 -31
  706. data/templates/java/org/herb/ast/HelperType.java.erb +29 -0
  707. data/templates/java/org/herb/ast/Nodes.java.erb +11 -7
  708. data/templates/java/parser_options_helpers.c.erb +45 -0
  709. data/templates/java/parser_options_helpers.h.erb +10 -0
  710. data/templates/javascript/packages/client/src/state-kinds.ts.erb +49 -0
  711. data/templates/javascript/packages/client/src/state-operators.ts.erb +15 -0
  712. data/templates/javascript/packages/client/src/state-predicates.ts.erb +27 -0
  713. data/templates/javascript/packages/client/src/state-transforms.ts.erb +19 -0
  714. data/templates/javascript/packages/core/src/action-view-helpers.ts.erb +47 -0
  715. data/templates/javascript/packages/core/src/built-in-components.ts.erb +67 -0
  716. data/templates/javascript/packages/core/src/errors.ts.erb +9 -3
  717. data/templates/javascript/packages/core/src/foreign-content-elements.ts.erb +18 -0
  718. data/templates/javascript/packages/core/src/html-constants.ts.erb +32 -0
  719. data/templates/javascript/packages/core/src/html-elements.ts.erb +41 -0
  720. data/templates/javascript/packages/core/src/node-type-guards.ts.erb +11 -7
  721. data/templates/javascript/packages/core/src/nodes.ts.erb +82 -19
  722. data/templates/javascript/packages/core/src/parser-options.ts.erb +41 -0
  723. data/templates/javascript/packages/core/src/visitor.ts.erb +1 -1
  724. data/templates/javascript/packages/node/extension/error_helpers.cpp.erb +3 -3
  725. data/templates/javascript/packages/node/extension/nodes.cpp.erb +17 -12
  726. data/templates/javascript/packages/node/extension/nodes.h.erb +3 -3
  727. data/templates/javascript/packages/node/extension/parser_options_helpers.cpp.erb +41 -0
  728. data/templates/javascript/packages/node/extension/parser_options_helpers.h.erb +12 -0
  729. data/templates/lib/herb/action_view/helper_registry.rb.erb +77 -3
  730. data/templates/lib/herb/ast/nodes.rb.erb +16 -6
  731. data/templates/lib/herb/engine/slots/component_definitions.rb.erb +38 -0
  732. data/templates/lib/herb/engine/slots/state_kinds.rb.erb +37 -0
  733. data/templates/lib/herb/engine/slots/state_operators.rb.erb +26 -0
  734. data/templates/lib/herb/engine/slots/state_predicates.rb.erb +39 -0
  735. data/templates/lib/herb/engine/slots/state_transforms.rb.erb +27 -0
  736. data/templates/lib/herb/errors.rb.erb +34 -6
  737. data/templates/lib/herb/html/elements.rb.erb +12 -0
  738. data/templates/lib/herb/parser_options.rb.erb +32 -0
  739. data/templates/lib/herb/visitor.rb.erb +7 -1
  740. data/templates/rust/herb-printer/src/printer_visitor.rs.erb +14 -0
  741. data/templates/rust/src/action_view_helpers.rs.erb +38 -0
  742. data/templates/rust/src/ast/nodes.rs.erb +24 -31
  743. data/templates/rust/src/errors.rs.erb +1 -1
  744. data/templates/rust/src/nodes.rs.erb +58 -26
  745. data/templates/rust/src/parser_options.rs.erb +16 -0
  746. data/templates/rust/src/prism/generated_deserialize.rs.erb +109 -0
  747. data/templates/rust/src/prism/generated_prettyprint.rs.erb +153 -0
  748. data/templates/rust/src/union_types.rs.erb +7 -1
  749. data/templates/rust/src/visitor.rs.erb +1 -1
  750. data/templates/src/analyze/action_view/helper_registry.c.erb +22 -0
  751. data/templates/src/analyze/missing_end.c.erb +13 -5
  752. data/templates/src/ast/ast_nodes.c.erb +41 -2
  753. data/templates/src/diff/herb_diff_nodes.c.erb +44 -8
  754. data/templates/src/errors.c.erb +8 -3
  755. data/templates/src/include/analyze/action_view/helper_registry.h.erb +19 -0
  756. data/templates/src/include/analyze/herb_directive_kinds.h.erb +25 -0
  757. data/templates/src/include/errors.h.erb +3 -1
  758. data/templates/src/include/parser/foreign_content_elements.h.erb +23 -0
  759. data/templates/src/include/parser/parser_options.h.erb +21 -0
  760. data/templates/src/include/util/html_elements.h.erb +28 -0
  761. data/templates/src/parser_options_defaults.c.erb +9 -0
  762. data/templates/template.rb +564 -9
  763. data/templates/wasm/error_helpers.cpp.erb +4 -4
  764. data/templates/wasm/nodes.cpp.erb +12 -12
  765. data/templates/wasm/nodes.h.erb +3 -3
  766. data/templates/wasm/parser_options_helpers.cpp.erb +25 -0
  767. data/templates/wasm/parser_options_helpers.h.erb +12 -0
  768. metadata +306 -27
  769. data/config/action_view_helpers/actionview/form_helper/button.yml +0 -38
  770. data/config/action_view_helpers/actionview/form_helper/submit.yml +0 -35
  771. data/lib/herb/ast.rb +0 -7
  772. data/lib/herb/diff_operation.rb +0 -34
  773. data/lib/herb/diff_result.rb +0 -59
  774. data/lib/herb/engine/error_formatter.rb +0 -432
  775. data/lib/herb/engine/parser_error_overlay.rb +0 -773
  776. data/lib/herb/engine/validation_error_overlay.rb +0 -182
  777. data/lib/herb/engine/validation_errors.rb +0 -65
  778. data/lib/herb/engine/validator.rb +0 -80
  779. data/sig/herb/ast.rbs +0 -6
  780. data/sig/herb/diff_operation.rbs +0 -34
  781. data/sig/herb/diff_result.rbs +0 -34
  782. data/sig/herb/engine/debug.rbs +0 -38
  783. data/sig/herb/engine/error_formatter.rbs +0 -47
  784. data/sig/herb/engine/parser_error_overlay.rbs +0 -41
  785. data/sig/herb/engine/validation_error_overlay.rbs +0 -35
  786. data/sig/herb/engine/validation_errors.rbs +0 -45
  787. data/sig/herb/engine/validator.rbs +0 -41
  788. data/sig/herb_c_extension.rbs +0 -10
data/src/parser.c CHANGED
@@ -23,9 +23,10 @@
23
23
  #include <strings.h>
24
24
 
25
25
  #define MAX_CONSECUTIVE_ERRORS 10
26
- static void parser_parse_in_data_state(parser_T* parser, hb_array_T* children, hb_array_T* errors);
27
- static void parser_parse_foreign_content(parser_T* parser, hb_array_T* children, hb_array_T* errors);
28
- static AST_ERB_CONTENT_NODE_T* parser_parse_erb_tag(parser_T* parser);
26
+ static void parser_parse_in_data_state(parser_T* parser, hb_array_T* children, hb_array_T** errors);
27
+ static void parser_parse_foreign_content(parser_T* parser, hb_array_T* children, hb_array_T** errors);
28
+ static bool parser_element_has_foreign_content(parser_T* parser, hb_string_T tag_name);
29
+ static AST_NODE_T* parser_parse_erb_tag(parser_T* parser);
29
30
  static void parser_handle_whitespace(parser_T* parser, token_T* whitespace_token, hb_array_T* children);
30
31
  static void parser_consume_whitespace(parser_T* parser, hb_array_T* children);
31
32
  static void parser_skip_erb_content(lexer_T* lexer);
@@ -34,24 +35,6 @@ static bool parser_lookahead_erb_is_control_flow(parser_T* parser);
34
35
  static void parser_handle_erb_in_open_tag(parser_T* parser, hb_array_T* children);
35
36
  static void parser_handle_whitespace_in_open_tag(parser_T* parser, hb_array_T* children);
36
37
 
37
- const parser_options_T HERB_DEFAULT_PARSER_OPTIONS = { .track_whitespace = false,
38
- .analyze = true,
39
- .strict = true,
40
- .action_view_helpers = false,
41
- .transform_conditionals = false,
42
- .render_nodes = false,
43
- .strict_locals = false,
44
- .prism_nodes_deep = false,
45
- .prism_nodes = false,
46
- .prism_program = false,
47
- .dot_notation_tags = false,
48
- .html = true,
49
- .start_line = 0,
50
- .start_column = 0,
51
- .timeout_ms = 1000,
52
- .max_errors = 25,
53
- .deadline_ms = 0 };
54
-
55
38
  size_t parser_sizeof(void) {
56
39
  return sizeof(struct PARSER_STRUCT);
57
40
  }
@@ -62,25 +45,32 @@ void herb_parser_init(parser_T* parser, lexer_T* lexer, parser_options_T options
62
45
  parser->current_token = lexer_next_token(lexer);
63
46
  parser->open_tags_stack = hb_array_init(16, parser->allocator);
64
47
  parser->state = PARSER_STATE_DATA;
65
- parser->foreign_content_type = FOREIGN_CONTENT_UNKNOWN;
48
+ parser->foreign_content_kind = FOREIGN_CONTENT_NONE;
49
+ parser->foreign_content_tag_name = HB_STRING_NULL;
50
+ parser->svg_depth = 0;
51
+ parser->xml_document = false;
66
52
  parser->options = options;
67
53
  parser->consecutive_error_count = 0;
68
54
  parser->in_recovery_mode = false;
55
+
56
+ for (size_t index = 0; index < HERB_MAX_FOREIGN_CONTENT_ELEMENTS; index++) {
57
+ parser->foreign_content_absent_from[index] = UINT32_MAX;
58
+ }
69
59
  }
70
60
 
71
61
  static AST_CDATA_NODE_T* parser_parse_cdata(parser_T* parser) {
72
- hb_array_T* errors = hb_array_init(8, parser->allocator);
62
+ hb_array_T* errors = NULL;
73
63
  hb_array_T* children = hb_array_init(8, parser->allocator);
74
64
  hb_buffer_T content;
75
65
  hb_buffer_init(&content, 128, parser->allocator);
76
66
 
77
- token_T* tag_opening = parser_consume_expected(parser, TOKEN_CDATA_START, errors);
67
+ token_T* tag_opening = parser_consume_expected(parser, TOKEN_CDATA_START, &errors);
78
68
  position_T start = parser->current_token->location.start;
79
69
 
80
70
  while (token_is_none_of(parser, TOKEN_CDATA_END, TOKEN_EOF)) {
81
71
  if (token_is(parser, TOKEN_ERB_START)) {
82
72
  parser_append_literal_node_from_buffer(parser, &content, children, start);
83
- AST_ERB_CONTENT_NODE_T* erb_node = parser_parse_erb_tag(parser);
73
+ AST_NODE_T* erb_node = parser_parse_erb_tag(parser);
84
74
  hb_array_append(children, erb_node);
85
75
  start = parser->current_token->location.start;
86
76
  continue;
@@ -92,7 +82,7 @@ static AST_CDATA_NODE_T* parser_parse_cdata(parser_T* parser) {
92
82
  }
93
83
 
94
84
  parser_append_literal_node_from_buffer(parser, &content, children, start);
95
- token_T* tag_closing = parser_consume_expected(parser, TOKEN_CDATA_END, errors);
85
+ token_T* tag_closing = parser_consume_expected(parser, TOKEN_CDATA_END, &errors);
96
86
 
97
87
  AST_CDATA_NODE_T* cdata = ast_cdata_node_init(
98
88
  tag_opening,
@@ -105,26 +95,59 @@ static AST_CDATA_NODE_T* parser_parse_cdata(parser_T* parser) {
105
95
  );
106
96
 
107
97
  hb_buffer_free(&content);
108
- token_free(tag_opening, parser->allocator);
109
- token_free(tag_closing, parser->allocator);
110
98
 
111
99
  return cdata;
112
100
  }
113
101
 
102
+ // <!-- a <!-->
103
+ static token_T* parser_parse_html_comment_abrupt_end(
104
+ parser_T* parser,
105
+ hb_buffer_T* comment,
106
+ hb_array_T* children,
107
+ position_T start
108
+ ) {
109
+ token_T* nested_start = parser_advance(parser);
110
+ token_T* tag_end = parser_advance(parser);
111
+
112
+ position_T dashes_start = nested_start->location.start;
113
+ dashes_start.column += 2;
114
+
115
+ hb_buffer_append_string(comment, hb_string("<!"));
116
+
117
+ hb_string_T content = { .data = comment->value, .length = (uint32_t) comment->length };
118
+ hb_array_append(children, ast_literal_node_init(content, start, dashes_start, NULL, parser->allocator));
119
+ hb_buffer_clear(comment);
120
+
121
+ token_T* comment_end = hb_allocator_alloc(parser->allocator, sizeof(token_T));
122
+
123
+ comment_end->type = TOKEN_HTML_COMMENT_END;
124
+ comment_end->value = hb_string_from_data(hb_allocator_strndup(parser->allocator, "-->", 3), 3);
125
+ comment_end->owns_value = true;
126
+ comment_end->location = (location_T) { .start = dashes_start, .end = tag_end->location.end };
127
+ comment_end->range = (range_T) { .from = nested_start->range.from + 2, .to = tag_end->range.to };
128
+
129
+ token_free(nested_start, parser->allocator);
130
+ token_free(tag_end, parser->allocator);
131
+
132
+ return comment_end;
133
+ }
134
+
114
135
  static AST_HTML_COMMENT_NODE_T* parser_parse_html_comment(parser_T* parser) {
115
- hb_array_T* errors = hb_array_init(8, parser->allocator);
136
+ hb_array_T* errors = NULL;
116
137
  hb_array_T* children = hb_array_init(8, parser->allocator);
117
- token_T* comment_start = parser_consume_expected(parser, TOKEN_HTML_COMMENT_START, errors);
138
+ token_T* comment_start = parser_consume_expected(parser, TOKEN_HTML_COMMENT_START, &errors);
118
139
  position_T start = parser->current_token->location.start;
119
140
 
120
141
  hb_buffer_T comment;
121
142
  hb_buffer_init(&comment, 512, parser->allocator);
122
143
 
144
+ token_T* comment_end = NULL;
145
+
123
146
  while (token_is_none_of(parser, TOKEN_HTML_COMMENT_END, TOKEN_HTML_COMMENT_INVALID_END, TOKEN_EOF)) {
124
147
  if (token_is(parser, TOKEN_ERB_START)) {
125
148
  parser_append_literal_node_from_buffer(parser, &comment, children, start);
126
149
 
127
- AST_ERB_CONTENT_NODE_T* erb_node = parser_parse_erb_tag(parser);
150
+ AST_NODE_T* erb_node = parser_parse_erb_tag(parser);
128
151
  hb_array_append(children, erb_node);
129
152
 
130
153
  start = parser->current_token->location.start;
@@ -132,6 +155,33 @@ static AST_HTML_COMMENT_NODE_T* parser_parse_html_comment(parser_T* parser) {
132
155
  continue;
133
156
  }
134
157
 
158
+ if (token_is(parser, TOKEN_HTML_COMMENT_START)) {
159
+ lexer_T lexer_copy = *parser->lexer;
160
+ token_T* next_token = lexer_next_token(&lexer_copy);
161
+ token_type_T next_type = next_token ? next_token->type : TOKEN_EOF;
162
+
163
+ if (next_token) { token_free(next_token, parser->allocator); }
164
+
165
+ // <!--[if !mso]><!--><meta><!--<![endif]-->
166
+ if (next_type == TOKEN_HTML_TAG_END) {
167
+ comment_end = parser_parse_html_comment_abrupt_end(parser, &comment, children, start);
168
+ break;
169
+ }
170
+
171
+ // <!-- outer <!-- inner -->
172
+ if (next_type != TOKEN_EOF && parser->options.strict) {
173
+ append_nested_comment_error(
174
+ comment_start,
175
+ parser->current_token,
176
+ parser->current_token->location.start,
177
+ parser->current_token->location.end,
178
+ parser->allocator,
179
+ &errors,
180
+ &parser->options
181
+ );
182
+ }
183
+ }
184
+
135
185
  token_T* token = parser_advance(parser);
136
186
  hb_buffer_append_string(&comment, token->value);
137
187
  token_free(token, parser->allocator);
@@ -139,19 +189,31 @@ static AST_HTML_COMMENT_NODE_T* parser_parse_html_comment(parser_T* parser) {
139
189
 
140
190
  parser_append_literal_node_from_buffer(parser, &comment, children, start);
141
191
 
142
- token_T* comment_end = NULL;
143
-
144
- if (token_is(parser, TOKEN_HTML_COMMENT_INVALID_END)) {
192
+ if (comment_end != NULL) {
193
+ // closed by <!-->
194
+ } else if (token_is(parser, TOKEN_HTML_COMMENT_INVALID_END)) {
145
195
  comment_end = parser_advance(parser);
146
196
  append_invalid_comment_closing_tag_error(
147
197
  comment_end,
148
198
  comment_end->location.start,
149
199
  comment_end->location.end,
150
200
  parser->allocator,
151
- errors
201
+ &errors,
202
+ &parser->options
152
203
  );
204
+ } else if (token_is(parser, TOKEN_EOF)) {
205
+ append_unclosed_comment_error(
206
+ comment_start,
207
+ comment_start->location.start,
208
+ parser->current_token->location.start,
209
+ parser->allocator,
210
+ &errors,
211
+ &parser->options
212
+ );
213
+
214
+ comment_end = parser_advance(parser);
153
215
  } else {
154
- comment_end = parser_consume_expected(parser, TOKEN_HTML_COMMENT_END, errors);
216
+ comment_end = parser_consume_expected(parser, TOKEN_HTML_COMMENT_END, &errors);
155
217
  }
156
218
 
157
219
  AST_HTML_COMMENT_NODE_T* comment_node = ast_html_comment_node_init(
@@ -165,19 +227,17 @@ static AST_HTML_COMMENT_NODE_T* parser_parse_html_comment(parser_T* parser) {
165
227
  );
166
228
 
167
229
  hb_buffer_free(&comment);
168
- token_free(comment_start, parser->allocator);
169
- token_free(comment_end, parser->allocator);
170
230
 
171
231
  return comment_node;
172
232
  }
173
233
 
174
234
  static AST_HTML_DOCTYPE_NODE_T* parser_parse_html_doctype(parser_T* parser) {
175
- hb_array_T* errors = hb_array_init(8, parser->allocator);
235
+ hb_array_T* errors = NULL;
176
236
  hb_array_T* children = hb_array_init(8, parser->allocator);
177
237
  hb_buffer_T content;
178
238
  hb_buffer_init(&content, 64, parser->allocator);
179
239
 
180
- token_T* tag_opening = parser_consume_expected(parser, TOKEN_HTML_DOCTYPE, errors);
240
+ token_T* tag_opening = parser_consume_expected(parser, TOKEN_HTML_DOCTYPE, &errors);
181
241
 
182
242
  position_T start = parser->current_token->location.start;
183
243
 
@@ -185,20 +245,20 @@ static AST_HTML_DOCTYPE_NODE_T* parser_parse_html_doctype(parser_T* parser) {
185
245
  if (token_is(parser, TOKEN_ERB_START)) {
186
246
  parser_append_literal_node_from_buffer(parser, &content, children, start);
187
247
 
188
- AST_ERB_CONTENT_NODE_T* erb_node = parser_parse_erb_tag(parser);
248
+ AST_NODE_T* erb_node = parser_parse_erb_tag(parser);
189
249
  hb_array_append(children, erb_node);
190
250
 
191
251
  continue;
192
252
  }
193
253
 
194
- token_T* token = parser_consume_expected(parser, parser->current_token->type, errors);
254
+ token_T* token = parser_consume_expected(parser, parser->current_token->type, &errors);
195
255
  hb_buffer_append_string(&content, token->value);
196
256
  token_free(token, parser->allocator);
197
257
  }
198
258
 
199
259
  parser_append_literal_node_from_buffer(parser, &content, children, start);
200
260
 
201
- token_T* tag_closing = parser_consume_expected(parser, TOKEN_HTML_TAG_END, errors);
261
+ token_T* tag_closing = parser_consume_expected(parser, TOKEN_HTML_TAG_END, &errors);
202
262
 
203
263
  AST_HTML_DOCTYPE_NODE_T* doctype = ast_html_doctype_node_init(
204
264
  tag_opening,
@@ -210,20 +270,20 @@ static AST_HTML_DOCTYPE_NODE_T* parser_parse_html_doctype(parser_T* parser) {
210
270
  parser->allocator
211
271
  );
212
272
 
213
- token_free(tag_opening, parser->allocator);
214
- token_free(tag_closing, parser->allocator);
215
273
  hb_buffer_free(&content);
216
274
 
217
275
  return doctype;
218
276
  }
219
277
 
220
278
  static AST_XML_DECLARATION_NODE_T* parser_parse_xml_declaration(parser_T* parser) {
221
- hb_array_T* errors = hb_array_init(8, parser->allocator);
279
+ hb_array_T* errors = NULL;
222
280
  hb_array_T* children = hb_array_init(8, parser->allocator);
223
281
  hb_buffer_T content;
224
282
  hb_buffer_init(&content, 64, parser->allocator);
225
283
 
226
- token_T* tag_opening = parser_consume_expected(parser, TOKEN_XML_DECLARATION, errors);
284
+ token_T* tag_opening = parser_consume_expected(parser, TOKEN_XML_DECLARATION, &errors);
285
+
286
+ parser->xml_document = true;
227
287
 
228
288
  position_T start = parser->current_token->location.start;
229
289
 
@@ -231,7 +291,7 @@ static AST_XML_DECLARATION_NODE_T* parser_parse_xml_declaration(parser_T* parser
231
291
  if (token_is(parser, TOKEN_ERB_START)) {
232
292
  parser_append_literal_node_from_buffer(parser, &content, children, start);
233
293
 
234
- AST_ERB_CONTENT_NODE_T* erb_node = parser_parse_erb_tag(parser);
294
+ AST_NODE_T* erb_node = parser_parse_erb_tag(parser);
235
295
  hb_array_append(children, erb_node);
236
296
 
237
297
  start = parser->current_token->location.start;
@@ -246,7 +306,7 @@ static AST_XML_DECLARATION_NODE_T* parser_parse_xml_declaration(parser_T* parser
246
306
 
247
307
  parser_append_literal_node_from_buffer(parser, &content, children, start);
248
308
 
249
- token_T* tag_closing = parser_consume_expected(parser, TOKEN_XML_DECLARATION_END, errors);
309
+ token_T* tag_closing = parser_consume_expected(parser, TOKEN_XML_DECLARATION_END, &errors);
250
310
 
251
311
  AST_XML_DECLARATION_NODE_T* xml_declaration = ast_xml_declaration_node_init(
252
312
  tag_opening,
@@ -258,14 +318,61 @@ static AST_XML_DECLARATION_NODE_T* parser_parse_xml_declaration(parser_T* parser
258
318
  parser->allocator
259
319
  );
260
320
 
261
- token_free(tag_opening, parser->allocator);
262
- token_free(tag_closing, parser->allocator);
263
321
  hb_buffer_free(&content);
264
322
 
265
323
  return xml_declaration;
266
324
  }
267
325
 
268
- static AST_HTML_TEXT_NODE_T* parser_parse_text_content(parser_T* parser, hb_array_T* document_errors) {
326
+ static AST_XML_PROCESSING_INSTRUCTION_NODE_T* parser_parse_xml_processing_instruction(parser_T* parser) {
327
+ hb_array_T* errors = NULL;
328
+ hb_array_T* children = hb_array_init(8, parser->allocator);
329
+ hb_buffer_T content;
330
+ hb_buffer_init(&content, 64, parser->allocator);
331
+
332
+ token_T* tag_opening = parser_consume_expected(parser, TOKEN_XML_PROCESSING_INSTRUCTION_START, &errors);
333
+ token_T* target = parser_consume_expected(parser, TOKEN_IDENTIFIER, &errors);
334
+
335
+ position_T start = parser->current_token->location.start;
336
+
337
+ while (token_is_none_of(parser, TOKEN_XML_DECLARATION_END, TOKEN_HTML_TAG_END, TOKEN_EOF)) {
338
+ if (token_is(parser, TOKEN_ERB_START)) {
339
+ parser_append_literal_node_from_buffer(parser, &content, children, start);
340
+
341
+ AST_NODE_T* erb_node = parser_parse_erb_tag(parser);
342
+ hb_array_append(children, erb_node);
343
+
344
+ start = parser->current_token->location.start;
345
+
346
+ continue;
347
+ }
348
+
349
+ token_T* token = parser_advance(parser);
350
+ hb_buffer_append_string(&content, token->value);
351
+ token_free(token, parser->allocator);
352
+ }
353
+
354
+ parser_append_literal_node_from_buffer(parser, &content, children, start);
355
+
356
+ token_type_T closing_type = token_is(parser, TOKEN_HTML_TAG_END) ? TOKEN_HTML_TAG_END : TOKEN_XML_DECLARATION_END;
357
+ token_T* tag_closing = parser_consume_expected(parser, closing_type, &errors);
358
+
359
+ AST_XML_PROCESSING_INSTRUCTION_NODE_T* processing_instruction = ast_xml_processing_instruction_node_init(
360
+ tag_opening,
361
+ target,
362
+ children,
363
+ tag_closing,
364
+ tag_opening->location.start,
365
+ tag_closing->location.end,
366
+ errors,
367
+ parser->allocator
368
+ );
369
+
370
+ hb_buffer_free(&content);
371
+
372
+ return processing_instruction;
373
+ }
374
+
375
+ static AST_HTML_TEXT_NODE_T* parser_parse_text_content(parser_T* parser, hb_array_T** document_errors) {
269
376
  position_T start = parser->current_token->location.start;
270
377
 
271
378
  hb_buffer_T content;
@@ -277,6 +384,8 @@ static AST_HTML_TEXT_NODE_T* parser_parse_text_content(parser_T* parser, hb_arra
277
384
  TOKEN_HTML_TAG_START_CLOSE,
278
385
  TOKEN_HTML_DOCTYPE,
279
386
  TOKEN_HTML_COMMENT_START,
387
+ TOKEN_XML_DECLARATION,
388
+ TOKEN_XML_PROCESSING_INSTRUCTION_START,
280
389
  TOKEN_ERB_START,
281
390
  TOKEN_EOF
282
391
  )) {
@@ -297,7 +406,13 @@ static AST_HTML_TEXT_NODE_T* parser_parse_text_content(parser_T* parser, hb_arra
297
406
  position_T stray_end = peek_token->location.end;
298
407
  token_free(peek_token, parser->allocator);
299
408
 
300
- append_stray_erb_closing_tag_error(stray_start, stray_end, parser->allocator, document_errors);
409
+ append_stray_erb_closing_tag_error(
410
+ stray_start,
411
+ stray_end,
412
+ parser->allocator,
413
+ document_errors,
414
+ &parser->options
415
+ );
301
416
 
302
417
  token_T* percent = parser_advance(parser);
303
418
  hb_buffer_append_string(&content, percent->value);
@@ -318,7 +433,7 @@ static AST_HTML_TEXT_NODE_T* parser_parse_text_content(parser_T* parser, hb_arra
318
433
  token_free(token, parser->allocator);
319
434
  }
320
435
 
321
- hb_array_T* errors = hb_array_init(8, parser->allocator);
436
+ hb_array_T* errors = NULL;
322
437
 
323
438
  AST_HTML_TEXT_NODE_T* text_node = NULL;
324
439
 
@@ -336,22 +451,42 @@ static AST_HTML_TEXT_NODE_T* parser_parse_text_content(parser_T* parser, hb_arra
336
451
  return text_node;
337
452
  }
338
453
 
454
+ static bool parser_token_starts_tag(const parser_T* parser) {
455
+ return token_is_any_of(
456
+ parser,
457
+ TOKEN_HTML_TAG_START,
458
+ TOKEN_HTML_TAG_START_CLOSE,
459
+ TOKEN_HTML_COMMENT_START,
460
+ TOKEN_HTML_DOCTYPE,
461
+ TOKEN_XML_PROCESSING_INSTRUCTION_START
462
+ );
463
+ }
464
+
339
465
  static AST_HTML_ATTRIBUTE_NAME_NODE_T* parser_parse_html_attribute_name(parser_T* parser) {
340
- hb_array_T* errors = hb_array_init(8, parser->allocator);
466
+ hb_array_T* errors = NULL;
341
467
  hb_array_T* children = hb_array_init(8, parser->allocator);
342
468
  hb_buffer_T buffer;
343
469
  hb_buffer_init(&buffer, 128, parser->allocator);
344
470
  position_T start = parser->current_token->location.start;
345
471
 
472
+ // <div =foo>
473
+ if (token_is(parser, TOKEN_EQUALS)) {
474
+ token_T* equals = parser_advance(parser);
475
+ hb_buffer_append_string(&buffer, equals->value);
476
+ token_free(equals, parser->allocator);
477
+ }
478
+
346
479
  while (token_is_none_of(
347
- parser,
348
- TOKEN_EQUALS,
349
- TOKEN_WHITESPACE,
350
- TOKEN_NEWLINE,
351
- TOKEN_HTML_TAG_END,
352
- TOKEN_HTML_TAG_SELF_CLOSE,
353
- TOKEN_EOF
354
- )) {
480
+ parser,
481
+ TOKEN_EQUALS,
482
+ TOKEN_WHITESPACE,
483
+ TOKEN_NEWLINE,
484
+ TOKEN_SLASH,
485
+ TOKEN_HTML_TAG_END,
486
+ TOKEN_HTML_TAG_SELF_CLOSE,
487
+ TOKEN_EOF
488
+ )
489
+ && !parser_token_starts_tag(parser)) {
355
490
  if (token_is(parser, TOKEN_ERB_START)) {
356
491
  hb_string_T tag = parser->current_token->value;
357
492
  bool is_output_tag = (tag.length >= 3 && tag.data[2] == '=');
@@ -365,13 +500,24 @@ static AST_HTML_ATTRIBUTE_NAME_NODE_T* parser_parse_html_attribute_name(parser_T
365
500
 
366
501
  parser_append_literal_node_from_buffer(parser, &buffer, children, start);
367
502
 
368
- AST_ERB_CONTENT_NODE_T* erb_node = parser_parse_erb_tag(parser);
503
+ AST_NODE_T* erb_node = parser_parse_erb_tag(parser);
369
504
  hb_array_append(children, erb_node);
370
505
 
371
506
  start = parser->current_token->location.start;
372
507
  continue;
373
508
  }
374
509
 
510
+ if (token_is(parser, TOKEN_QUOTE)) {
511
+ append_unexpected_character_in_attribute_name_error(
512
+ parser->current_token,
513
+ parser->current_token->location.start,
514
+ parser->current_token->location.end,
515
+ parser->allocator,
516
+ &errors,
517
+ &parser->options
518
+ );
519
+ }
520
+
375
521
  token_T* token = parser_advance(parser);
376
522
  hb_buffer_append_string(&buffer, token->value);
377
523
  token_free(token, parser->allocator);
@@ -404,7 +550,7 @@ static AST_HTML_ATTRIBUTE_NAME_NODE_T* parser_parse_html_attribute_name(parser_T
404
550
  static AST_HTML_ATTRIBUTE_VALUE_NODE_T* parser_parse_quoted_html_attribute_value(
405
551
  parser_T* parser,
406
552
  hb_array_T* children,
407
- hb_array_T* errors
553
+ hb_array_T** errors
408
554
  ) {
409
555
  hb_buffer_T buffer;
410
556
  hb_buffer_init(&buffer, 512, parser->allocator);
@@ -452,26 +598,23 @@ static AST_HTML_ATTRIBUTE_VALUE_NODE_T* parser_parse_quoted_html_attribute_value
452
598
  opening_quote->location.start,
453
599
  parser->current_token->location.start,
454
600
  parser->allocator,
455
- errors
601
+ errors,
602
+ &parser->options
456
603
  );
457
604
 
458
605
  parser_append_literal_node_from_buffer(parser, &buffer, children, start);
459
606
  hb_buffer_free(&buffer);
460
607
 
461
- AST_HTML_ATTRIBUTE_VALUE_NODE_T* attribute_value = ast_html_attribute_value_node_init(
608
+ return ast_html_attribute_value_node_init(
462
609
  opening_quote,
463
610
  children,
464
611
  NULL,
465
612
  true,
466
613
  opening_quote->location.start,
467
614
  parser->current_token->location.start,
468
- errors,
615
+ *errors,
469
616
  parser->allocator
470
617
  );
471
-
472
- token_free(opening_quote, parser->allocator);
473
-
474
- return attribute_value;
475
618
  }
476
619
 
477
620
  bool buffer_ends_with_whitespace = buffer.length > 0 && is_whitespace(buffer.value[buffer.length - 1]);
@@ -499,26 +642,23 @@ static AST_HTML_ATTRIBUTE_VALUE_NODE_T* parser_parse_quoted_html_attribute_value
499
642
  opening_quote->location.start,
500
643
  parser->current_token->location.start,
501
644
  parser->allocator,
502
- errors
645
+ errors,
646
+ &parser->options
503
647
  );
504
648
 
505
649
  parser_append_literal_node_from_buffer(parser, &buffer, children, start);
506
650
  hb_buffer_free(&buffer);
507
651
 
508
- AST_HTML_ATTRIBUTE_VALUE_NODE_T* attribute_value = ast_html_attribute_value_node_init(
652
+ return ast_html_attribute_value_node_init(
509
653
  opening_quote,
510
654
  children,
511
655
  NULL,
512
656
  true,
513
657
  opening_quote->location.start,
514
658
  parser->current_token->location.start,
515
- errors,
659
+ *errors,
516
660
  parser->allocator
517
661
  );
518
-
519
- token_free(opening_quote, parser->allocator);
520
-
521
- return attribute_value;
522
662
  }
523
663
  }
524
664
 
@@ -538,176 +678,133 @@ static AST_HTML_ATTRIBUTE_VALUE_NODE_T* parser_parse_quoted_html_attribute_value
538
678
  parser->current_token = lexer_next_token(parser->lexer);
539
679
  }
540
680
 
541
- if (token_is(parser, TOKEN_QUOTE) && opening_quote != NULL
542
- && hb_string_equals(parser->current_token->value, opening_quote->value)) {
543
- lexer_state_snapshot_T saved_state = lexer_save_state(parser->lexer);
544
-
545
- token_T* potential_closing = parser->current_token;
546
- parser->current_token = lexer_next_token(parser->lexer);
547
-
548
- if (token_is(parser, TOKEN_IDENTIFIER) || token_is(parser, TOKEN_CHARACTER)) {
549
- append_unexpected_error(
550
- hb_string("Unescaped quote character in attribute value"),
551
- hb_string("HTML entity (&apos;/&quot;) or different quote style"),
552
- opening_quote->value,
553
- potential_closing->location.start,
554
- potential_closing->location.end,
555
- parser->allocator,
556
- errors
557
- );
558
-
559
- lexer_restore_state(parser->lexer, saved_state);
560
-
561
- token_free(parser->current_token, parser->allocator);
562
- parser->current_token = potential_closing;
563
-
564
- hb_buffer_append_string(&buffer, parser->current_token->value);
565
- token_free(parser->current_token, parser->allocator);
566
- parser->current_token = lexer_next_token(parser->lexer);
567
-
568
- while (!token_is(parser, TOKEN_EOF)
569
- && !(
570
- token_is(parser, TOKEN_QUOTE) && opening_quote != NULL
571
- && hb_string_equals(parser->current_token->value, opening_quote->value)
572
- )) {
573
- if (token_is(parser, TOKEN_ERB_START)) {
574
- parser_append_literal_node_from_buffer(parser, &buffer, children, start);
575
-
576
- hb_array_append(children, parser_parse_erb_tag(parser));
577
-
578
- start = parser->current_token->location.start;
579
-
580
- continue;
581
- }
582
-
583
- hb_buffer_append_string(&buffer, parser->current_token->value);
584
- token_free(parser->current_token, parser->allocator);
585
-
586
- parser->current_token = lexer_next_token(parser->lexer);
587
- }
588
- } else {
589
- token_free(parser->current_token, parser->allocator);
590
- parser->current_token = potential_closing;
591
-
592
- lexer_restore_state(parser->lexer, saved_state);
593
- }
594
- }
595
-
596
681
  parser_append_literal_node_from_buffer(parser, &buffer, children, start);
597
682
  hb_buffer_free(&buffer);
598
683
 
599
684
  token_T* closing_quote = parser_consume_expected(parser, TOKEN_QUOTE, errors);
600
685
 
601
- AST_HTML_ATTRIBUTE_VALUE_NODE_T* attribute_value = ast_html_attribute_value_node_init(
686
+ return ast_html_attribute_value_node_init(
602
687
  opening_quote,
603
688
  children,
604
689
  closing_quote,
605
690
  true,
606
691
  opening_quote->location.start,
607
692
  closing_quote->location.end,
608
- errors,
693
+ *errors,
609
694
  parser->allocator
610
695
  );
696
+ }
611
697
 
612
- token_free(opening_quote, parser->allocator);
613
- token_free(closing_quote, parser->allocator);
614
-
615
- return attribute_value;
698
+ static bool parser_attribute_value_is_missing(const parser_T* parser) {
699
+ return token_is_any_of(parser, TOKEN_HTML_TAG_END, TOKEN_HTML_TAG_SELF_CLOSE, TOKEN_EOF)
700
+ || parser_token_starts_tag(parser);
616
701
  }
617
702
 
618
703
  static AST_HTML_ATTRIBUTE_VALUE_NODE_T* parser_parse_html_attribute_value(parser_T* parser) {
619
704
  hb_array_T* children = hb_array_init(8, parser->allocator);
620
- hb_array_T* errors = hb_array_init(8, parser->allocator);
705
+ hb_array_T* errors = NULL;
621
706
 
622
- // <div id=<%= "home" %>>
623
- if (token_is(parser, TOKEN_ERB_START)) {
624
- AST_ERB_CONTENT_NODE_T* erb_node = parser_parse_erb_tag(parser);
625
- hb_array_append(children, erb_node);
707
+ // <div id="home">
708
+ if (token_is(parser, TOKEN_QUOTE)) { return parser_parse_quoted_html_attribute_value(parser, children, &errors); }
626
709
 
627
- return ast_html_attribute_value_node_init(
628
- NULL,
629
- children,
630
- NULL,
631
- false,
632
- erb_node->base.location.start,
633
- erb_node->base.location.end,
634
- errors,
635
- parser->allocator
636
- );
637
- }
710
+ // <div id=home> or <div id=<%= "home" %>>
711
+ hb_buffer_T buffer;
712
+ hb_buffer_init(&buffer, 128, parser->allocator);
713
+ position_T value_start = parser->current_token->location.start;
714
+ position_T value_end = value_start;
715
+ position_T start = value_start;
716
+
717
+ while (
718
+ token_is_none_of(parser, TOKEN_WHITESPACE, TOKEN_NEWLINE, TOKEN_HTML_TAG_END, TOKEN_HTML_TAG_SELF_CLOSE, TOKEN_EOF)
719
+ && !parser_token_starts_tag(parser)
720
+ ) {
721
+ if (token_is(parser, TOKEN_ERB_START)) {
722
+ parser_append_literal_node_from_buffer(parser, &buffer, children, start);
638
723
 
639
- // <div id=home>
640
- if (token_is(parser, TOKEN_IDENTIFIER)) {
641
- token_T* identifier = parser_consume_expected(parser, TOKEN_IDENTIFIER, errors);
642
- AST_LITERAL_NODE_T* literal = ast_literal_node_init_from_token(identifier, parser->allocator);
643
- token_free(identifier, parser->allocator);
724
+ AST_NODE_T* erb_node = parser_parse_erb_tag(parser);
725
+ hb_array_append(children, erb_node);
644
726
 
645
- hb_array_append(children, literal);
727
+ value_end = erb_node->location.end;
728
+ start = parser->current_token->location.start;
646
729
 
647
- return ast_html_attribute_value_node_init(
648
- NULL,
649
- children,
650
- NULL,
651
- false,
652
- literal->base.location.start,
653
- literal->base.location.end,
654
- errors,
655
- parser->allocator
656
- );
657
- }
730
+ continue;
731
+ }
658
732
 
659
- // <div id="home">
660
- if (token_is(parser, TOKEN_QUOTE)) { return parser_parse_quoted_html_attribute_value(parser, children, errors); }
733
+ if (token_is_any_of(parser, TOKEN_QUOTE, TOKEN_BACKTICK, TOKEN_EQUALS)) {
734
+ append_unexpected_character_in_unquoted_attribute_value_error(
735
+ parser->current_token,
736
+ parser->current_token->location.start,
737
+ parser->current_token->location.end,
738
+ parser->allocator,
739
+ &errors,
740
+ &parser->options
741
+ );
742
+ }
661
743
 
662
- if (token_is(parser, TOKEN_BACKTICK)) {
663
744
  token_T* token = parser_advance(parser);
664
- position_T start = token->location.start;
665
- position_T end = token->location.end;
666
-
667
- append_unexpected_error(
668
- hb_string("Invalid quote character for HTML attribute"),
669
- hb_string("single quote (') or double quote (\")"),
670
- hb_string("a backtick"),
671
- start,
672
- end,
673
- parser->allocator,
674
- errors
675
- );
745
+ hb_buffer_append_string(&buffer, token->value);
746
+ value_end = token->location.end;
747
+ token_free(token, parser->allocator);
748
+ }
676
749
 
677
- AST_HTML_ATTRIBUTE_VALUE_NODE_T* value =
678
- ast_html_attribute_value_node_init(NULL, children, NULL, false, start, end, errors, parser->allocator);
750
+ parser_append_literal_node_from_buffer(parser, &buffer, children, start);
751
+ hb_buffer_free(&buffer);
679
752
 
680
- token_free(token, parser->allocator);
753
+ return ast_html_attribute_value_node_init(
754
+ NULL,
755
+ children,
756
+ NULL,
757
+ false,
758
+ value_start,
759
+ value_end,
760
+ errors,
761
+ parser->allocator
762
+ );
763
+ }
681
764
 
682
- return value;
683
- }
765
+ static AST_HTML_ATTRIBUTE_NODE_T* parser_parse_html_attribute_missing_value(
766
+ parser_T* parser,
767
+ AST_HTML_ATTRIBUTE_NAME_NODE_T* attribute_name,
768
+ token_T* equals
769
+ ) {
770
+ hb_array_T* errors = NULL;
771
+ hb_string_T attribute_name_string = hb_string("unknown");
772
+
773
+ if (hb_array_size(attribute_name->children) > 0) {
774
+ AST_LITERAL_NODE_T* first_child = (AST_LITERAL_NODE_T*) hb_array_get(attribute_name->children, 0);
684
775
 
685
- char* expected = token_types_to_friendly_string(parser->allocator, TOKEN_IDENTIFIER, TOKEN_QUOTE, TOKEN_ERB_START);
776
+ if (first_child && !hb_string_is_empty(first_child->content)) { attribute_name_string = first_child->content; }
777
+ }
686
778
 
687
- append_unexpected_error(
688
- hb_string("Unexpected Token"),
689
- hb_string(expected),
690
- token_type_to_friendly_string(parser->current_token->type),
779
+ append_missing_attribute_value_error(
780
+ attribute_name_string,
781
+ equals->location.start,
691
782
  parser->current_token->location.start,
692
- parser->current_token->location.end,
693
783
  parser->allocator,
694
- errors
784
+ &errors,
785
+ &parser->options
695
786
  );
696
787
 
697
- hb_allocator_dealloc(parser->allocator, expected);
698
-
699
- AST_HTML_ATTRIBUTE_VALUE_NODE_T* value = ast_html_attribute_value_node_init(
788
+ AST_HTML_ATTRIBUTE_VALUE_NODE_T* empty_value = ast_html_attribute_value_node_init(
700
789
  NULL,
701
- children,
790
+ hb_array_init(8, parser->allocator),
702
791
  NULL,
703
792
  false,
793
+ equals->location.end,
704
794
  parser->current_token->location.start,
705
- parser->current_token->location.end,
706
795
  errors,
707
796
  parser->allocator
708
797
  );
709
798
 
710
- return value;
799
+ return ast_html_attribute_node_init(
800
+ attribute_name,
801
+ equals,
802
+ empty_value,
803
+ attribute_name->base.location.start,
804
+ parser->current_token->location.start,
805
+ NULL,
806
+ parser->allocator
807
+ );
711
808
  }
712
809
 
713
810
  static AST_HTML_ATTRIBUTE_NODE_T* parser_parse_html_attribute(parser_T* parser) {
@@ -780,12 +877,18 @@ static AST_HTML_ATTRIBUTE_NODE_T* parser_parse_html_attribute(parser_T* parser)
780
877
 
781
878
  char* arena_copy = hb_allocator_strndup(parser->allocator, equals_buffer.value, equals_buffer.length);
782
879
  equals_with_whitespace->value = hb_string_from_data(arena_copy, equals_buffer.length);
880
+ equals_with_whitespace->owns_value = true;
783
881
 
784
882
  hb_buffer_free(&equals_buffer);
785
883
 
786
884
  equals_with_whitespace->location = (location_T) { .start = equals_start, .end = equals_end };
787
885
  equals_with_whitespace->range = (range_T) { .from = range_start, .to = range_end };
788
886
 
887
+ // <div class = >
888
+ if (parser_attribute_value_is_missing(parser)) {
889
+ return parser_parse_html_attribute_missing_value(parser, attribute_name, equals_with_whitespace);
890
+ }
891
+
789
892
  AST_HTML_ATTRIBUTE_VALUE_NODE_T* attribute_value = parser_parse_html_attribute_value(parser);
790
893
 
791
894
  return ast_html_attribute_node_init(
@@ -818,53 +921,13 @@ static AST_HTML_ATTRIBUTE_NODE_T* parser_parse_html_attribute(parser_T* parser)
818
921
  parser_consume_whitespace(parser, NULL);
819
922
 
820
923
  // <div class= >
821
- if (token_is(parser, TOKEN_HTML_TAG_END) || token_is(parser, TOKEN_HTML_TAG_SELF_CLOSE)) {
822
- hb_array_T* errors = hb_array_init(8, parser->allocator);
823
- hb_string_T attribute_name_string = hb_string("unknown");
824
-
825
- if (hb_array_size(attribute_name->children) > 0) {
826
- AST_LITERAL_NODE_T* first_child = (AST_LITERAL_NODE_T*) hb_array_get(attribute_name->children, 0);
827
-
828
- if (first_child && !hb_string_is_empty(first_child->content)) { attribute_name_string = first_child->content; }
829
- }
830
-
831
- append_missing_attribute_value_error(
832
- attribute_name_string,
833
- equals->location.start,
834
- parser->current_token->location.start,
835
- parser->allocator,
836
- errors
837
- );
838
-
839
- AST_HTML_ATTRIBUTE_VALUE_NODE_T* empty_value = ast_html_attribute_value_node_init(
840
- NULL,
841
- hb_array_init(8, parser->allocator),
842
- NULL,
843
- false,
844
- equals->location.end,
845
- parser->current_token->location.start,
846
- errors,
847
- parser->allocator
848
- );
849
-
850
- AST_HTML_ATTRIBUTE_NODE_T* attribute_node = ast_html_attribute_node_init(
851
- attribute_name,
852
- equals,
853
- empty_value,
854
- attribute_name->base.location.start,
855
- parser->current_token->location.start,
856
- NULL,
857
- parser->allocator
858
- );
859
-
860
- token_free(equals, parser->allocator);
861
-
862
- return attribute_node;
924
+ if (parser_attribute_value_is_missing(parser)) {
925
+ return parser_parse_html_attribute_missing_value(parser, attribute_name, equals);
863
926
  }
864
927
 
865
928
  AST_HTML_ATTRIBUTE_VALUE_NODE_T* attribute_value = parser_parse_html_attribute_value(parser);
866
929
 
867
- AST_HTML_ATTRIBUTE_NODE_T* attribute_node = ast_html_attribute_node_init(
930
+ return ast_html_attribute_node_init(
868
931
  attribute_name,
869
932
  equals,
870
933
  attribute_value,
@@ -873,10 +936,6 @@ static AST_HTML_ATTRIBUTE_NODE_T* parser_parse_html_attribute(parser_T* parser)
873
936
  NULL,
874
937
  parser->allocator
875
938
  );
876
-
877
- token_free(equals, parser->allocator);
878
-
879
- return attribute_node;
880
939
  }
881
940
 
882
941
  return ast_html_attribute_node_init(
@@ -1032,25 +1091,28 @@ static void parser_handle_whitespace_in_open_tag(parser_T* parser, hb_array_T* c
1032
1091
  }
1033
1092
 
1034
1093
  static AST_HTML_OPEN_TAG_NODE_T* parser_parse_html_open_tag(parser_T* parser) {
1035
- hb_array_T* errors = hb_array_init(8, parser->allocator);
1094
+ hb_array_T* errors = NULL;
1036
1095
  hb_array_T* children = hb_array_init(8, parser->allocator);
1037
1096
 
1038
- token_T* tag_start = parser_consume_expected(parser, TOKEN_HTML_TAG_START, errors);
1039
- token_T* tag_name = parser_consume_expected(parser, TOKEN_IDENTIFIER, errors);
1097
+ token_T* tag_start = parser_consume_expected(parser, TOKEN_HTML_TAG_START, &errors);
1098
+ token_T* tag_name = parser_consume_expected(parser, TOKEN_IDENTIFIER, &errors);
1099
+
1100
+ parser_consume_dot_notation_segments(parser, tag_name, &errors);
1040
1101
 
1041
- parser_consume_dot_notation_segments(parser, tag_name, errors);
1102
+ bool previous_was_quoted_value = false;
1042
1103
 
1043
1104
  while (token_is_none_of(parser, TOKEN_HTML_TAG_END, TOKEN_HTML_TAG_SELF_CLOSE, TOKEN_EOF)) {
1044
- if (token_is_any_of(parser, TOKEN_HTML_TAG_START, TOKEN_HTML_TAG_START_CLOSE)) {
1105
+ if (parser_token_starts_tag(parser)) {
1045
1106
  append_unclosed_open_tag_error(
1046
1107
  tag_name,
1047
1108
  tag_name->location.start,
1048
1109
  parser->current_token->location.start,
1049
1110
  parser->allocator,
1050
- errors
1111
+ &errors,
1112
+ &parser->options
1051
1113
  );
1052
1114
 
1053
- AST_HTML_OPEN_TAG_NODE_T* open_tag_node = ast_html_open_tag_node_init(
1115
+ return ast_html_open_tag_node_init(
1054
1116
  tag_start,
1055
1117
  tag_name,
1056
1118
  NULL,
@@ -1061,48 +1123,21 @@ static AST_HTML_OPEN_TAG_NODE_T* parser_parse_html_open_tag(parser_T* parser) {
1061
1123
  errors,
1062
1124
  parser->allocator
1063
1125
  );
1064
-
1065
- token_free(tag_start, parser->allocator);
1066
- token_free(tag_name, parser->allocator);
1067
-
1068
- return open_tag_node;
1069
1126
  }
1070
1127
 
1071
1128
  if (token_is_any_of(parser, TOKEN_WHITESPACE, TOKEN_NEWLINE)) {
1129
+ previous_was_quoted_value = false;
1072
1130
  parser_handle_whitespace_in_open_tag(parser, children);
1073
1131
  continue;
1074
1132
  }
1075
1133
 
1076
- if (parser->current_token->type == TOKEN_IDENTIFIER) {
1077
- hb_array_append(children, parser_parse_html_attribute(parser));
1078
- continue;
1079
- }
1080
-
1081
- if (parser->current_token->type == TOKEN_ERB_START) {
1134
+ if (token_is(parser, TOKEN_ERB_START)) {
1135
+ previous_was_quoted_value = false;
1082
1136
  parser_handle_erb_in_open_tag(parser, children);
1083
1137
  continue;
1084
1138
  }
1085
1139
 
1086
- if (parser->current_token->type == TOKEN_AT) {
1087
- hb_array_append(children, parser_parse_html_attribute(parser));
1088
- continue;
1089
- }
1090
-
1091
- if (parser->current_token->type == TOKEN_COLON) {
1092
- lexer_T lexer_copy = *parser->lexer;
1093
- token_T* next_token = lexer_next_token(&lexer_copy);
1094
-
1095
- if (next_token && next_token->type == TOKEN_IDENTIFIER) {
1096
- token_free(next_token, parser->allocator);
1097
- hb_array_append(children, parser_parse_html_attribute(parser));
1098
-
1099
- continue;
1100
- }
1101
-
1102
- token_free(next_token, parser->allocator);
1103
- }
1104
-
1105
- if (parser->current_token->type == TOKEN_PERCENT) {
1140
+ if (token_is(parser, TOKEN_PERCENT)) {
1106
1141
  lexer_T lexer_copy = *parser->lexer;
1107
1142
  token_T* peek_token = lexer_next_token(&lexer_copy);
1108
1143
 
@@ -1111,7 +1146,7 @@ static AST_HTML_OPEN_TAG_NODE_T* parser_parse_html_open_tag(parser_T* parser) {
1111
1146
  position_T stray_end = peek_token->location.end;
1112
1147
  token_free(peek_token, parser->allocator);
1113
1148
 
1114
- append_stray_erb_closing_tag_error(stray_start, stray_end, parser->allocator, errors);
1149
+ append_stray_erb_closing_tag_error(stray_start, stray_end, parser->allocator, &errors, &parser->options);
1115
1150
 
1116
1151
  token_T* percent = parser_advance(parser);
1117
1152
  token_T* gt = parser_advance(parser);
@@ -1123,22 +1158,73 @@ static AST_HTML_OPEN_TAG_NODE_T* parser_parse_html_open_tag(parser_T* parser) {
1123
1158
  token_free(percent, parser->allocator);
1124
1159
  token_free(gt, parser->allocator);
1125
1160
 
1161
+ previous_was_quoted_value = false;
1126
1162
  continue;
1127
1163
  }
1128
1164
 
1129
1165
  token_free(peek_token, parser->allocator);
1130
1166
  }
1131
1167
 
1132
- parser_append_unexpected_error(
1133
- parser,
1134
- errors,
1135
- "Unexpected Token",
1136
- TOKEN_IDENTIFIER,
1137
- TOKEN_AT,
1138
- TOKEN_ERB_START,
1139
- TOKEN_WHITESPACE,
1140
- TOKEN_NEWLINE
1141
- );
1168
+ // <div / class="a">
1169
+ if (token_is(parser, TOKEN_SLASH)) {
1170
+ token_T* solidus = parser_advance(parser);
1171
+
1172
+ append_unexpected_solidus_in_tag_error(
1173
+ solidus,
1174
+ solidus->location.start,
1175
+ solidus->location.end,
1176
+ parser->allocator,
1177
+ &errors,
1178
+ &parser->options
1179
+ );
1180
+
1181
+ token_free(solidus, parser->allocator);
1182
+ previous_was_quoted_value = false;
1183
+ continue;
1184
+ }
1185
+
1186
+ if (token_is(parser, TOKEN_ERROR)) {
1187
+ parser_append_unexpected_error(
1188
+ parser,
1189
+ &errors,
1190
+ "Unexpected Token",
1191
+ TOKEN_IDENTIFIER,
1192
+ TOKEN_AT,
1193
+ TOKEN_ERB_START,
1194
+ TOKEN_WHITESPACE,
1195
+ TOKEN_NEWLINE
1196
+ );
1197
+ continue;
1198
+ }
1199
+
1200
+ // <div class="a"id="b">
1201
+ if (previous_was_quoted_value) {
1202
+ append_missing_whitespace_between_attributes_error(
1203
+ parser->current_token,
1204
+ parser->current_token->location.start,
1205
+ parser->current_token->location.end,
1206
+ parser->allocator,
1207
+ &errors,
1208
+ &parser->options
1209
+ );
1210
+ }
1211
+
1212
+ // <div =foo>
1213
+ if (token_is(parser, TOKEN_EQUALS)) {
1214
+ append_unexpected_equals_sign_before_attribute_name_error(
1215
+ parser->current_token,
1216
+ parser->current_token->location.start,
1217
+ parser->current_token->location.end,
1218
+ parser->allocator,
1219
+ &errors,
1220
+ &parser->options
1221
+ );
1222
+ }
1223
+
1224
+ AST_HTML_ATTRIBUTE_NODE_T* attribute = parser_parse_html_attribute(parser);
1225
+ hb_array_append(children, attribute);
1226
+
1227
+ previous_was_quoted_value = attribute->value != NULL && attribute->value->close_quote != NULL;
1142
1228
  }
1143
1229
 
1144
1230
  if (token_is(parser, TOKEN_EOF)) {
@@ -1147,10 +1233,11 @@ static AST_HTML_OPEN_TAG_NODE_T* parser_parse_html_open_tag(parser_T* parser) {
1147
1233
  tag_name->location.start,
1148
1234
  parser->current_token->location.start,
1149
1235
  parser->allocator,
1150
- errors
1236
+ &errors,
1237
+ &parser->options
1151
1238
  );
1152
1239
 
1153
- AST_HTML_OPEN_TAG_NODE_T* open_tag_node = ast_html_open_tag_node_init(
1240
+ return ast_html_open_tag_node_init(
1154
1241
  tag_start,
1155
1242
  tag_name,
1156
1243
  NULL,
@@ -1161,11 +1248,6 @@ static AST_HTML_OPEN_TAG_NODE_T* parser_parse_html_open_tag(parser_T* parser) {
1161
1248
  errors,
1162
1249
  parser->allocator
1163
1250
  );
1164
-
1165
- token_free(tag_start, parser->allocator);
1166
- token_free(tag_name, parser->allocator);
1167
-
1168
- return open_tag_node;
1169
1251
  }
1170
1252
 
1171
1253
  bool is_self_closing = false;
@@ -1173,7 +1255,7 @@ static AST_HTML_OPEN_TAG_NODE_T* parser_parse_html_open_tag(parser_T* parser) {
1173
1255
  token_T* tag_end = parser_consume_if_present(parser, TOKEN_HTML_TAG_END);
1174
1256
 
1175
1257
  if (tag_end == NULL) {
1176
- tag_end = parser_consume_expected(parser, TOKEN_HTML_TAG_SELF_CLOSE, errors);
1258
+ tag_end = parser_consume_expected(parser, TOKEN_HTML_TAG_SELF_CLOSE, &errors);
1177
1259
 
1178
1260
  if (tag_end == NULL) {
1179
1261
  token_free(tag_start, parser->allocator);
@@ -1188,7 +1270,7 @@ static AST_HTML_OPEN_TAG_NODE_T* parser_parse_html_open_tag(parser_T* parser) {
1188
1270
  is_self_closing = true;
1189
1271
  }
1190
1272
 
1191
- AST_HTML_OPEN_TAG_NODE_T* open_tag_node = ast_html_open_tag_node_init(
1273
+ return ast_html_open_tag_node_init(
1192
1274
  tag_start,
1193
1275
  tag_name,
1194
1276
  tag_end,
@@ -1199,37 +1281,50 @@ static AST_HTML_OPEN_TAG_NODE_T* parser_parse_html_open_tag(parser_T* parser) {
1199
1281
  errors,
1200
1282
  parser->allocator
1201
1283
  );
1202
-
1203
- token_free(tag_start, parser->allocator);
1204
- token_free(tag_name, parser->allocator);
1205
- token_free(tag_end, parser->allocator);
1206
-
1207
- return open_tag_node;
1208
1284
  }
1209
1285
 
1210
1286
  static AST_HTML_CLOSE_TAG_NODE_T* parser_parse_html_close_tag(parser_T* parser) {
1211
- hb_array_T* errors = hb_array_init(8, parser->allocator);
1287
+ hb_array_T* errors = NULL;
1212
1288
  hb_array_T* children = hb_array_init(8, parser->allocator);
1213
1289
 
1214
- token_T* tag_opening = parser_consume_expected(parser, TOKEN_HTML_TAG_START_CLOSE, errors);
1290
+ token_T* tag_opening = parser_consume_expected(parser, TOKEN_HTML_TAG_START_CLOSE, &errors);
1215
1291
 
1216
1292
  parser_consume_whitespace(parser, children);
1217
1293
 
1218
- token_T* tag_name = parser_consume_expected(parser, TOKEN_IDENTIFIER, errors);
1294
+ token_T* tag_name = parser_consume_expected(parser, TOKEN_IDENTIFIER, &errors);
1219
1295
 
1220
- parser_consume_dot_notation_segments(parser, tag_name, errors);
1296
+ parser_consume_dot_notation_segments(parser, tag_name, &errors);
1297
+
1298
+ if (parser->svg_depth > 0 && hb_string_equals_case_insensitive(tag_name->value, hb_string("svg"))) {
1299
+ parser->svg_depth--;
1300
+ }
1221
1301
 
1222
1302
  parser_consume_whitespace(parser, children);
1223
1303
 
1224
1304
  token_T* tag_closing = parser_consume_if_present(parser, TOKEN_HTML_TAG_END);
1225
1305
 
1306
+ // </div/>
1307
+ if (tag_closing == NULL && token_is(parser, TOKEN_HTML_TAG_SELF_CLOSE) && tag_name != NULL) {
1308
+ tag_closing = parser_advance(parser);
1309
+
1310
+ append_end_tag_with_trailing_solidus_error(
1311
+ tag_name,
1312
+ tag_closing->location.start,
1313
+ tag_closing->location.end,
1314
+ parser->allocator,
1315
+ &errors,
1316
+ &parser->options
1317
+ );
1318
+ }
1319
+
1226
1320
  if (tag_closing == NULL) {
1227
1321
  append_unclosed_close_tag_error(
1228
1322
  tag_name,
1229
1323
  tag_opening->location.start,
1230
1324
  tag_name->location.end,
1231
1325
  parser->allocator,
1232
- errors
1326
+ &errors,
1327
+ &parser->options
1233
1328
  );
1234
1329
  }
1235
1330
 
@@ -1245,7 +1340,8 @@ static AST_HTML_CLOSE_TAG_NODE_T* parser_parse_html_close_tag(parser_T* parser)
1245
1340
  tag_opening->location.start,
1246
1341
  tag_closing->location.end,
1247
1342
  parser->allocator,
1248
- errors
1343
+ &errors,
1344
+ &parser->options
1249
1345
  );
1250
1346
 
1251
1347
  hb_allocator_dealloc(parser->allocator, expected.data);
@@ -1254,7 +1350,7 @@ static AST_HTML_CLOSE_TAG_NODE_T* parser_parse_html_close_tag(parser_T* parser)
1254
1350
 
1255
1351
  position_T end_position = tag_closing != NULL ? tag_closing->location.end : tag_name->location.end;
1256
1352
 
1257
- AST_HTML_CLOSE_TAG_NODE_T* close_tag = ast_html_close_tag_node_init(
1353
+ return ast_html_close_tag_node_init(
1258
1354
  tag_opening,
1259
1355
  tag_name,
1260
1356
  children,
@@ -1264,12 +1360,6 @@ static AST_HTML_CLOSE_TAG_NODE_T* parser_parse_html_close_tag(parser_T* parser)
1264
1360
  errors,
1265
1361
  parser->allocator
1266
1362
  );
1267
-
1268
- token_free(tag_opening, parser->allocator);
1269
- token_free(tag_name, parser->allocator);
1270
- token_free(tag_closing, parser->allocator);
1271
-
1272
- return close_tag;
1273
1363
  }
1274
1364
 
1275
1365
  // TODO: this should probably be AST_HTML_ELEMENT_NODE_T with a AST_HTML_SELF_CLOSING_TAG_NODE_T
@@ -1279,7 +1369,7 @@ static AST_HTML_ELEMENT_NODE_T* parser_parse_html_self_closing_element(
1279
1369
  ) {
1280
1370
  return ast_html_element_node_init(
1281
1371
  (AST_NODE_T*) open_tag,
1282
- open_tag->tag_name,
1372
+ token_copy(open_tag->tag_name, parser->allocator),
1283
1373
  NULL,
1284
1374
  NULL,
1285
1375
  true,
@@ -1295,28 +1385,30 @@ static AST_HTML_ELEMENT_NODE_T* parser_parse_html_regular_element(
1295
1385
  parser_T* parser,
1296
1386
  AST_HTML_OPEN_TAG_NODE_T* open_tag
1297
1387
  ) {
1298
- hb_array_T* errors = hb_array_init(8, parser->allocator);
1388
+ hb_array_T* errors = NULL;
1299
1389
  hb_array_T* body = hb_array_init(8, parser->allocator);
1300
1390
 
1301
1391
  parser_push_open_tag(parser, open_tag->tag_name);
1302
1392
 
1303
- if (!hb_string_is_empty(open_tag->tag_name->value) && parser_is_foreign_content_tag(open_tag->tag_name->value)) {
1304
- foreign_content_type_T content_type = parser_get_foreign_content_type(open_tag->tag_name->value);
1305
- parser_enter_foreign_content(parser, content_type);
1306
- parser_parse_foreign_content(parser, body, errors);
1393
+ if (parser_element_has_foreign_content(parser, open_tag->tag_name->value)) {
1394
+ hb_string_T tag_name = open_tag->tag_name->value;
1395
+ hb_string_T end_tag_name = parser_foreign_content_has_end_tag(tag_name) ? tag_name : HB_STRING_NULL;
1396
+
1397
+ parser_enter_foreign_content(parser, parser_get_foreign_content_kind(tag_name), end_tag_name);
1398
+ parser_parse_foreign_content(parser, body, &errors);
1307
1399
  } else {
1308
- parser_parse_in_data_state(parser, body, errors);
1400
+ parser_parse_in_data_state(parser, body, &errors);
1309
1401
  }
1310
1402
 
1311
1403
  if (!token_is(parser, TOKEN_HTML_TAG_START_CLOSE)) {
1312
- return parser_handle_missing_close_tag(parser, open_tag, body, errors);
1404
+ return parser_handle_missing_close_tag(parser, open_tag, body, &errors);
1313
1405
  }
1314
1406
 
1315
1407
  AST_HTML_CLOSE_TAG_NODE_T* close_tag = parser_parse_html_close_tag(parser);
1316
1408
 
1317
1409
  if (parser_in_svg_context(parser) == false && is_void_element(close_tag->tag_name->value)) {
1318
1410
  hb_array_push(body, close_tag);
1319
- parser_parse_in_data_state(parser, body, errors);
1411
+ parser_parse_in_data_state(parser, body, &errors);
1320
1412
  close_tag = parser_parse_html_close_tag(parser);
1321
1413
  }
1322
1414
 
@@ -1337,7 +1429,8 @@ static AST_HTML_ELEMENT_NODE_T* parser_parse_html_regular_element(
1337
1429
  unclosed->location.start,
1338
1430
  unclosed->location.end,
1339
1431
  parser->allocator,
1340
- errors
1432
+ &errors,
1433
+ &parser->options
1341
1434
  );
1342
1435
  token_free(unclosed, parser->allocator);
1343
1436
  }
@@ -1346,12 +1439,12 @@ static AST_HTML_ELEMENT_NODE_T* parser_parse_html_regular_element(
1346
1439
  token_T* popped_token = parser_pop_open_tag(parser);
1347
1440
  token_free(popped_token, parser->allocator);
1348
1441
  } else {
1349
- parser_handle_mismatched_tags(parser, close_tag, errors);
1442
+ parser_handle_mismatched_tags(parser, close_tag, &errors);
1350
1443
  }
1351
1444
 
1352
1445
  return ast_html_element_node_init(
1353
1446
  (AST_NODE_T*) open_tag,
1354
- open_tag->tag_name,
1447
+ token_copy(open_tag->tag_name, parser->allocator),
1355
1448
  body,
1356
1449
  (AST_NODE_T*) close_tag,
1357
1450
  false,
@@ -1371,12 +1464,14 @@ static AST_NODE_T* parser_parse_html_element(parser_T* parser) {
1371
1464
  // <tag />
1372
1465
  if (open_tag->is_void) { return (AST_NODE_T*) parser_parse_html_self_closing_element(parser, open_tag); }
1373
1466
 
1467
+ if (hb_string_equals_case_insensitive(open_tag->tag_name->value, hb_string("svg"))) { parser->svg_depth++; }
1468
+
1374
1469
  // <tag>, in void element list, and not in inside an <svg> element
1375
1470
  if (!open_tag->is_void && is_void_element(open_tag->tag_name->value) && !parser_in_svg_context(parser)) {
1376
1471
  return (AST_NODE_T*) parser_parse_html_self_closing_element(parser, open_tag);
1377
1472
  }
1378
1473
 
1379
- if (!hb_string_is_empty(open_tag->tag_name->value) && parser_is_foreign_content_tag(open_tag->tag_name->value)) {
1474
+ if (parser_element_has_foreign_content(parser, open_tag->tag_name->value)) {
1380
1475
  AST_HTML_ELEMENT_NODE_T* regular_element = parser_parse_html_regular_element(parser, open_tag);
1381
1476
 
1382
1477
  if (regular_element != NULL) { return (AST_NODE_T*) regular_element; }
@@ -1385,18 +1480,35 @@ static AST_NODE_T* parser_parse_html_element(parser_T* parser) {
1385
1480
  return (AST_NODE_T*) open_tag;
1386
1481
  }
1387
1482
 
1388
- static AST_ERB_CONTENT_NODE_T* parser_parse_erb_tag(parser_T* parser) {
1389
- hb_array_T* errors = hb_array_init(8, parser->allocator);
1483
+ static AST_NODE_T* parser_parse_erb_tag(parser_T* parser) {
1484
+ hb_array_T* errors = NULL;
1390
1485
 
1391
- token_T* opening_tag = parser_consume_expected(parser, TOKEN_ERB_START, errors);
1392
- token_T* content = parser_consume_expected(parser, TOKEN_ERB_CONTENT, errors);
1486
+ token_T* opening_tag = parser_consume_expected(parser, TOKEN_ERB_START, &errors);
1487
+ token_T* content = parser_consume_expected(parser, TOKEN_ERB_CONTENT, &errors);
1393
1488
 
1394
1489
  token_T* closing_tag = NULL;
1395
1490
  position_T end_position;
1396
1491
 
1397
1492
  if (token_is(parser, TOKEN_ERB_END)) {
1398
- closing_tag = parser_consume_expected(parser, TOKEN_ERB_END, errors);
1493
+ closing_tag = parser_consume_expected(parser, TOKEN_ERB_END, &errors);
1399
1494
  end_position = closing_tag->location.end;
1495
+
1496
+ bool is_well_formed = hb_string_equals(closing_tag->value, hb_string("%>"))
1497
+ || hb_string_equals(closing_tag->value, hb_string("-%>"))
1498
+ || hb_string_equals(closing_tag->value, hb_string("=%>"))
1499
+ || hb_string_equals(closing_tag->value, hb_string("%%>"));
1500
+
1501
+ if (!is_well_formed) {
1502
+ append_malformed_erb_closing_tag_error(
1503
+ opening_tag,
1504
+ closing_tag,
1505
+ opening_tag->location.start,
1506
+ closing_tag->location.end,
1507
+ parser->allocator,
1508
+ &errors,
1509
+ &parser->options
1510
+ );
1511
+ }
1400
1512
  } else if (token_is(parser, TOKEN_ERB_START)) {
1401
1513
  append_nested_erb_tag_error(
1402
1514
  opening_tag,
@@ -1405,7 +1517,8 @@ static AST_ERB_CONTENT_NODE_T* parser_parse_erb_tag(parser_T* parser) {
1405
1517
  parser->current_token->location.start,
1406
1518
  parser->current_token->location.end,
1407
1519
  parser->allocator,
1408
- errors
1520
+ &errors,
1521
+ &parser->options
1409
1522
  );
1410
1523
  end_position = parser->current_token->location.start;
1411
1524
  } else {
@@ -1414,12 +1527,25 @@ static AST_ERB_CONTENT_NODE_T* parser_parse_erb_tag(parser_T* parser) {
1414
1527
  opening_tag->location.start,
1415
1528
  parser->current_token->location.start,
1416
1529
  parser->allocator,
1417
- errors
1530
+ &errors,
1531
+ &parser->options
1418
1532
  );
1419
1533
  end_position = parser->current_token->location.start;
1420
1534
  }
1421
1535
 
1422
- AST_ERB_CONTENT_NODE_T* erb_node = ast_erb_content_node_init(
1536
+ if (hb_string_equals(opening_tag->value, hb_string("<%#"))) {
1537
+ return (AST_NODE_T*) ast_erb_comment_node_init(
1538
+ opening_tag,
1539
+ content,
1540
+ closing_tag,
1541
+ opening_tag->location.start,
1542
+ end_position,
1543
+ errors,
1544
+ parser->allocator
1545
+ );
1546
+ }
1547
+
1548
+ return (AST_NODE_T*) ast_erb_content_node_init(
1423
1549
  opening_tag,
1424
1550
  content,
1425
1551
  closing_tag,
@@ -1432,26 +1558,103 @@ static AST_ERB_CONTENT_NODE_T* parser_parse_erb_tag(parser_T* parser) {
1432
1558
  errors,
1433
1559
  parser->allocator
1434
1560
  );
1561
+ }
1435
1562
 
1436
- token_free(opening_tag, parser->allocator);
1437
- token_free(content, parser->allocator);
1438
- if (closing_tag != NULL) { token_free(closing_tag, parser->allocator); }
1563
+ static bool is_tag_name_character(char character) {
1564
+ return (character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z')
1565
+ || (character >= '0' && character <= '9') || character == '-';
1566
+ }
1567
+
1568
+ static bool parser_scan_for_end_tag(const parser_T* parser, hb_string_T tag_name) {
1569
+ hb_string_T source = parser->lexer->source;
1570
+
1571
+ if (hb_string_is_empty(source) || hb_string_is_empty(tag_name)) { return false; }
1572
+
1573
+ for (uint32_t index = parser->current_token->range.from; index + 2 < source.length; index++) {
1574
+ if (source.data[index] != '<' || source.data[index + 1] != '/') { continue; }
1575
+
1576
+ uint32_t name_start = index + 2;
1577
+
1578
+ if (name_start + tag_name.length > source.length) { return false; }
1579
+
1580
+ hb_string_T candidate = { .data = source.data + name_start, .length = tag_name.length };
1581
+
1582
+ if (!hb_string_equals_case_insensitive(candidate, tag_name)) { continue; }
1583
+
1584
+ uint32_t after = name_start + tag_name.length;
1585
+
1586
+ if (after >= source.length || !is_tag_name_character(source.data[after])) { return true; }
1587
+ }
1439
1588
 
1440
- return erb_node;
1589
+ return false;
1590
+ }
1591
+
1592
+ static bool parser_foreign_content_end_tag_ahead(parser_T* parser, hb_string_T tag_name) {
1593
+ int index = parser_foreign_content_element_index(tag_name);
1594
+ uint32_t position = parser->current_token->range.from;
1595
+
1596
+ if (index >= 0 && position >= parser->foreign_content_absent_from[index]) { return false; }
1597
+
1598
+ if (parser_scan_for_end_tag(parser, tag_name)) { return true; }
1599
+
1600
+ if (index >= 0 && position < parser->foreign_content_absent_from[index]) {
1601
+ parser->foreign_content_absent_from[index] = position;
1602
+ }
1603
+
1604
+ return false;
1605
+ }
1606
+
1607
+ // <title> and <plaintext> are ordinary elements in XML documents, <title> also inside <svg>
1608
+ static bool parser_element_has_foreign_content(parser_T* parser, hb_string_T tag_name) {
1609
+ if (parser_get_foreign_content_kind(tag_name) == FOREIGN_CONTENT_NONE) { return false; }
1610
+ if (parser_foreign_content_is_html_only(tag_name) && parser->xml_document) { return false; }
1611
+ if (hb_string_equals_case_insensitive(tag_name, hb_string("title")) && parser_in_svg_context(parser)) {
1612
+ return false;
1613
+ }
1614
+
1615
+ if (parser_foreign_content_has_end_tag(tag_name) && !parser_foreign_content_end_tag_ahead(parser, tag_name)) {
1616
+ return false;
1617
+ }
1618
+
1619
+ return true;
1620
+ }
1621
+
1622
+ // <textarea> and <title> hold text, <script> and <style> hold raw source
1623
+ static void parser_append_foreign_content_from_buffer(
1624
+ const parser_T* parser,
1625
+ hb_buffer_T* buffer,
1626
+ hb_array_T* children,
1627
+ position_T start
1628
+ ) {
1629
+ bool is_text = parser->foreign_content_kind == FOREIGN_CONTENT_RCDATA;
1630
+
1631
+ if (!is_text || buffer->length == 0) {
1632
+ parser_append_literal_node_from_buffer(parser, buffer, children, start);
1633
+ return;
1634
+ }
1635
+
1636
+ hb_string_T content = { .data = buffer->value, .length = (uint32_t) buffer->length };
1637
+
1638
+ hb_array_append(
1639
+ children,
1640
+ ast_html_text_node_init(content, start, parser->current_token->location.start, NULL, parser->allocator)
1641
+ );
1642
+
1643
+ hb_buffer_free(buffer);
1644
+ hb_buffer_init(buffer, 128, parser->allocator);
1441
1645
  }
1442
1646
 
1443
- static void parser_parse_foreign_content(parser_T* parser, hb_array_T* children, hb_array_T* errors) {
1647
+ static void parser_parse_foreign_content(parser_T* parser, hb_array_T* children, hb_array_T** errors) {
1444
1648
  hb_buffer_T content;
1445
1649
  hb_buffer_init(&content, 1024, parser->allocator);
1446
1650
  position_T start = parser->current_token->location.start;
1447
- hb_string_T expected_closing_tag = parser_get_foreign_content_closing_tag(parser->foreign_content_type);
1448
- bool has_closing_tag = !hb_string_is_empty(expected_closing_tag);
1651
+ bool has_closing_tag = !hb_string_is_empty(parser->foreign_content_tag_name);
1449
1652
 
1450
1653
  while (!token_is(parser, TOKEN_EOF)) {
1451
1654
  if (token_is(parser, TOKEN_ERB_START)) {
1452
- parser_append_literal_node_from_buffer(parser, &content, children, start);
1655
+ parser_append_foreign_content_from_buffer(parser, &content, children, start);
1453
1656
 
1454
- AST_ERB_CONTENT_NODE_T* erb_node = parser_parse_erb_tag(parser);
1657
+ AST_NODE_T* erb_node = parser_parse_erb_tag(parser);
1455
1658
  hb_array_append(children, erb_node);
1456
1659
 
1457
1660
  start = parser->current_token->location.start;
@@ -1466,7 +1669,7 @@ static void parser_parse_foreign_content(parser_T* parser, hb_array_T* children,
1466
1669
  bool is_potential_match = false;
1467
1670
 
1468
1671
  if (next_token && next_token->type == TOKEN_IDENTIFIER && !hb_string_is_empty(next_token->value)) {
1469
- is_potential_match = parser_is_expected_closing_tag_name(next_token->value, parser->foreign_content_type);
1672
+ is_potential_match = parser_is_foreign_content_closing_tag_name(parser, next_token->value);
1470
1673
  }
1471
1674
 
1472
1675
  lexer_restore_state(parser->lexer, saved_state);
@@ -1474,7 +1677,7 @@ static void parser_parse_foreign_content(parser_T* parser, hb_array_T* children,
1474
1677
  if (next_token) { token_free(next_token, parser->allocator); }
1475
1678
 
1476
1679
  if (is_potential_match) {
1477
- parser_append_literal_node_from_buffer(parser, &content, children, start);
1680
+ parser_append_foreign_content_from_buffer(parser, &content, children, start);
1478
1681
  parser_exit_foreign_content(parser);
1479
1682
 
1480
1683
  hb_buffer_free(&content);
@@ -1488,12 +1691,12 @@ static void parser_parse_foreign_content(parser_T* parser, hb_array_T* children,
1488
1691
  token_free(token, parser->allocator);
1489
1692
  }
1490
1693
 
1491
- parser_append_literal_node_from_buffer(parser, &content, children, start);
1694
+ parser_append_foreign_content_from_buffer(parser, &content, children, start);
1492
1695
  parser_exit_foreign_content(parser);
1493
1696
  hb_buffer_free(&content);
1494
1697
  }
1495
1698
 
1496
- static void parser_parse_in_data_state(parser_T* parser, hb_array_T* children, hb_array_T* errors) {
1699
+ static void parser_parse_in_data_state(parser_T* parser, hb_array_T* children, hb_array_T** errors) {
1497
1700
  if (!parser->options.html) {
1498
1701
  parser_parse_foreign_content(parser, children, errors);
1499
1702
  return;
@@ -1520,6 +1723,12 @@ static void parser_parse_in_data_state(parser_T* parser, hb_array_T* children, h
1520
1723
  continue;
1521
1724
  }
1522
1725
 
1726
+ if (token_is(parser, TOKEN_XML_PROCESSING_INSTRUCTION_START)) {
1727
+ hb_array_append(children, parser_parse_xml_processing_instruction(parser));
1728
+ parser->consecutive_error_count = 0;
1729
+ continue;
1730
+ }
1731
+
1523
1732
  if (token_is(parser, TOKEN_CDATA_START)) {
1524
1733
  hb_array_append(children, parser_parse_cdata(parser));
1525
1734
  parser->consecutive_error_count = 0;
@@ -1694,6 +1903,17 @@ static hb_array_T* collect_close_tag_names(hb_array_T* nodes, hb_allocator_T* al
1694
1903
  return close_tag_names;
1695
1904
  }
1696
1905
 
1906
+ static void free_close_tag_names(hb_array_T** close_tag_names, hb_allocator_T* allocator) {
1907
+ if (close_tag_names == NULL || *close_tag_names == NULL) { return; }
1908
+
1909
+ for (size_t i = 0; i < hb_array_size(*close_tag_names); i++) {
1910
+ hb_string_T* name = (hb_string_T*) hb_array_get(*close_tag_names, i);
1911
+ if (name != NULL) { hb_allocator_dealloc(allocator, name); }
1912
+ }
1913
+
1914
+ hb_array_free(close_tag_names);
1915
+ }
1916
+
1697
1917
  static hb_array_T* parser_build_elements_from_tags(
1698
1918
  hb_array_T* nodes,
1699
1919
  hb_array_T* errors,
@@ -1740,7 +1960,7 @@ static hb_array_T* parser_build_elements_from_tags(
1740
1960
  if (last_body_node != NULL) { end_position = last_body_node->location.end; }
1741
1961
  }
1742
1962
 
1743
- hb_array_T* element_errors = hb_array_init(8, allocator);
1963
+ hb_array_T* element_errors = NULL;
1744
1964
 
1745
1965
  if (strict) {
1746
1966
  append_omitted_closing_tag_error(
@@ -1749,21 +1969,22 @@ static hb_array_T* parser_build_elements_from_tags(
1749
1969
  open_tag->base.location.start,
1750
1970
  open_tag->base.location.end,
1751
1971
  allocator,
1752
- element_errors
1972
+ &element_errors,
1973
+ options
1753
1974
  );
1754
1975
  }
1755
1976
 
1756
1977
  AST_HTML_OMITTED_CLOSE_TAG_NODE_T* omitted_close_tag = ast_html_omitted_close_tag_node_init(
1757
- open_tag->tag_name,
1978
+ token_copy(open_tag->tag_name, allocator),
1758
1979
  end_position,
1759
1980
  end_position,
1760
- hb_array_init(8, allocator),
1981
+ NULL,
1761
1982
  allocator
1762
1983
  );
1763
1984
 
1764
1985
  AST_HTML_ELEMENT_NODE_T* element = ast_html_element_node_init(
1765
1986
  (AST_NODE_T*) open_tag,
1766
- open_tag->tag_name,
1987
+ token_copy(open_tag->tag_name, allocator),
1767
1988
  processed_body,
1768
1989
  (AST_NODE_T*) omitted_close_tag,
1769
1990
  false,
@@ -1778,16 +1999,15 @@ static hb_array_T* parser_build_elements_from_tags(
1778
1999
 
1779
2000
  index = implicit_close_index - 1;
1780
2001
  } else {
1781
- if (hb_array_size(open_tag->base.errors) == 0 && !parser_options_errors_exceeded(options)) {
2002
+ if (hb_array_size(open_tag->base.errors) == 0) {
1782
2003
  append_missing_closing_tag_error(
1783
2004
  open_tag->tag_name,
1784
2005
  open_tag->base.location.start,
1785
2006
  open_tag->base.location.end,
1786
2007
  allocator,
1787
- open_tag->base.errors
2008
+ &open_tag->base.errors,
2009
+ options
1788
2010
  );
1789
-
1790
- parser_options_increment_error_count(options);
1791
2011
  }
1792
2012
 
1793
2013
  hb_array_append(result, node);
@@ -1804,11 +2024,11 @@ static hb_array_T* parser_build_elements_from_tags(
1804
2024
  hb_array_T* processed_body = parser_build_elements_from_tags(body, errors, options, allocator);
1805
2025
  hb_array_free(&body);
1806
2026
 
1807
- hb_array_T* element_errors = hb_array_init(8, allocator);
2027
+ hb_array_T* element_errors = NULL;
1808
2028
 
1809
2029
  AST_HTML_ELEMENT_NODE_T* element = ast_html_element_node_init(
1810
2030
  (AST_NODE_T*) open_tag,
1811
- open_tag->tag_name,
2031
+ token_copy(open_tag->tag_name, allocator),
1812
2032
  processed_body,
1813
2033
  (AST_NODE_T*) close_tag,
1814
2034
  false,
@@ -1827,16 +2047,15 @@ static hb_array_T* parser_build_elements_from_tags(
1827
2047
  AST_HTML_CLOSE_TAG_NODE_T* close_tag = (AST_HTML_CLOSE_TAG_NODE_T*) node;
1828
2048
 
1829
2049
  if (!is_void_element(close_tag->tag_name->value)) {
1830
- if (hb_array_size(close_tag->base.errors) == 0 && !parser_options_errors_exceeded(options)) {
2050
+ if (hb_array_size(close_tag->base.errors) == 0) {
1831
2051
  append_missing_opening_tag_error(
1832
2052
  close_tag->tag_name,
1833
2053
  close_tag->base.location.start,
1834
2054
  close_tag->base.location.end,
1835
2055
  allocator,
1836
- close_tag->base.errors
2056
+ &close_tag->base.errors,
2057
+ options
1837
2058
  );
1838
-
1839
- parser_options_increment_error_count(options);
1840
2059
  }
1841
2060
  }
1842
2061
 
@@ -1846,17 +2065,19 @@ static hb_array_T* parser_build_elements_from_tags(
1846
2065
  }
1847
2066
  }
1848
2067
 
2068
+ free_close_tag_names(&close_tag_names, allocator);
2069
+
1849
2070
  return result;
1850
2071
  }
1851
2072
 
1852
2073
  static AST_DOCUMENT_NODE_T* parser_parse_document(parser_T* parser) {
1853
2074
  hb_array_T* children = hb_array_init(8, parser->allocator);
1854
- hb_array_T* errors = hb_array_init(8, parser->allocator);
2075
+ hb_array_T* errors = NULL;
1855
2076
  position_T start = parser->current_token->location.start;
1856
2077
 
1857
- parser_parse_in_data_state(parser, children, errors);
2078
+ parser_parse_in_data_state(parser, children, &errors);
1858
2079
 
1859
- token_T* eof = parser_consume_expected(parser, TOKEN_EOF, errors);
2080
+ token_T* eof = parser_consume_expected(parser, TOKEN_EOF, &errors);
1860
2081
 
1861
2082
  AST_DOCUMENT_NODE_T* document_node =
1862
2083
  ast_document_node_init(children, NULL, HERB_PRISM_NODE_EMPTY, start, eof->location.end, errors, parser->allocator);
@@ -1872,7 +2093,7 @@ AST_DOCUMENT_NODE_T* herb_parser_parse(parser_T* parser) {
1872
2093
 
1873
2094
  static void parser_handle_whitespace(parser_T* parser, token_T* whitespace_token, hb_array_T* children) {
1874
2095
  if (parser->options.track_whitespace) {
1875
- hb_array_T* errors = hb_array_init(8, parser->allocator);
2096
+ hb_array_T* errors = NULL;
1876
2097
  AST_WHITESPACE_NODE_T* whitespace_node = ast_whitespace_node_init(
1877
2098
  whitespace_token,
1878
2099
  whitespace_token->location.start,
@@ -1881,6 +2102,8 @@ static void parser_handle_whitespace(parser_T* parser, token_T* whitespace_token
1881
2102
  parser->allocator
1882
2103
  );
1883
2104
  hb_array_append(children, whitespace_node);
2105
+
2106
+ return;
1884
2107
  }
1885
2108
 
1886
2109
  token_free(whitespace_token, parser->allocator);