herb 0.10.1 → 0.11.1

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 (793) hide show
  1. checksums.yaml +4 -4
  2. data/Makefile +17 -7
  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 +1249 -786
  284. data/exe/herb +2 -0
  285. data/ext/herb/error_helpers.c +379 -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 -74
  289. data/ext/herb/extension_helpers.c +81 -35
  290. data/ext/herb/extension_helpers.h +4 -1
  291. data/ext/herb/nodes.c +859 -374
  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 +3 -2
  296. data/lib/herb/action_view/helper_registry.rb +1358 -62
  297. data/lib/herb/analysis/partial_declaration.rb +140 -0
  298. data/lib/herb/analysis/partial_index.rb +189 -0
  299. data/lib/herb/analysis/partial_resolution.rb +161 -0
  300. data/lib/herb/analysis/partial_resolver.rb +144 -0
  301. data/lib/herb/analysis/project_index.rb +152 -0
  302. data/lib/herb/{action_view → analysis}/render_analyzer.rb +287 -77
  303. data/lib/herb/analysis/render_graph/ancestor_chain.rb +27 -0
  304. data/lib/herb/analysis/render_graph/builder.rb +376 -0
  305. data/lib/herb/analysis/render_graph/call_frame.rb +9 -0
  306. data/lib/herb/analysis/render_graph/call_site_location.rb +23 -0
  307. data/lib/herb/analysis/render_graph/inferred_signature.rb +21 -0
  308. data/lib/herb/analysis/render_graph/partial_call_site.rb +40 -0
  309. data/lib/herb/analysis/render_graph/partial_context.rb +46 -0
  310. data/lib/herb/analysis/render_graph/template_roots.rb +31 -0
  311. data/lib/herb/analysis/render_graph.rb +285 -0
  312. data/lib/herb/analysis/ruby_locals_index/local.rb +25 -0
  313. data/lib/herb/analysis/ruby_locals_index/named_reference.rb +22 -0
  314. data/lib/herb/analysis/ruby_locals_index/offset_table.rb +44 -0
  315. data/lib/herb/analysis/ruby_locals_index/reference_collector.rb +80 -0
  316. data/lib/herb/analysis/ruby_locals_index/state_locals.rb +49 -0
  317. data/lib/herb/analysis/ruby_locals_index.rb +165 -0
  318. data/lib/herb/analysis/ruby_reference.rb +67 -0
  319. data/lib/herb/analysis/template_dependencies/dependency_collector.rb +179 -0
  320. data/lib/herb/analysis/template_dependencies/node_dependency_collector.rb +309 -0
  321. data/lib/herb/analysis/template_dependencies.rb +562 -0
  322. data/lib/herb/ast/erb_render_node.rb +26 -94
  323. data/lib/herb/ast/helpers.rb +90 -10
  324. data/lib/herb/ast/herb_directive_node.rb +18 -0
  325. data/lib/herb/ast/node.rb +32 -2
  326. data/lib/herb/ast/nodes.rb +1368 -105
  327. data/lib/herb/backend.rb +73 -0
  328. data/lib/herb/bootstrap.rb +8 -8
  329. data/lib/herb/cli.rb +555 -32
  330. data/lib/herb/colors.rb +14 -0
  331. data/lib/herb/configuration.rb +71 -35
  332. data/lib/herb/defaults.yml +2 -1
  333. data/lib/herb/dev/assets.rb +57 -0
  334. data/lib/herb/dev/classifier.rb +84 -0
  335. data/lib/herb/dev/embedded.rb +27 -0
  336. data/lib/herb/dev/pipeline.rb +305 -0
  337. data/lib/herb/dev/protocol.rb +221 -0
  338. data/lib/herb/dev/runner.rb +180 -175
  339. data/lib/herb/dev/server.rb +89 -26
  340. data/lib/herb/dev/server_entry.rb +41 -6
  341. data/lib/herb/dev/watcher.rb +181 -0
  342. data/lib/herb/dev.rb +163 -0
  343. data/lib/herb/diagnostic/formatter.rb +88 -0
  344. data/lib/herb/diagnostic.rb +172 -0
  345. data/lib/herb/diff/operation.rb +36 -0
  346. data/lib/herb/diff/result.rb +62 -0
  347. data/lib/herb/diff.rb +10 -0
  348. data/lib/herb/engine/collector.rb +62 -0
  349. data/lib/herb/engine/compiler.rb +387 -113
  350. data/lib/herb/engine/component_tags/constant_resolver.rb +28 -0
  351. data/lib/herb/engine/component_tags/partial_resolver.rb +54 -0
  352. data/lib/herb/engine/component_tags/resolver.rb +32 -0
  353. data/lib/herb/engine/component_tags/visitor.rb +231 -0
  354. data/lib/herb/engine/css_inliner/inliner.rb +101 -0
  355. data/lib/herb/engine/css_inliner/visitor.rb +174 -0
  356. data/lib/herb/engine/errors.rb +122 -0
  357. data/lib/herb/engine/helpers.rb +82 -0
  358. data/lib/herb/engine/inline_render/inliner.rb +122 -0
  359. data/lib/herb/engine/inline_render/visitor.rb +267 -0
  360. data/lib/herb/engine/runtime/entry.rb +62 -0
  361. data/lib/herb/engine/runtime/error_page.rb +362 -0
  362. data/lib/herb/engine/runtime/html_safe_assertions.rb +217 -0
  363. data/lib/herb/engine/runtime/journal/summary.rb +227 -0
  364. data/lib/herb/engine/runtime/journal.rb +379 -0
  365. data/lib/herb/engine/runtime/middleware.rb +212 -0
  366. data/lib/herb/engine/runtime/observations.rb +73 -0
  367. data/lib/herb/engine/runtime/report.rb +204 -0
  368. data/lib/herb/engine/runtime/session.rb +437 -0
  369. data/lib/herb/engine/runtime.rb +21 -0
  370. data/lib/herb/engine/scoped_style/channel.rb +66 -0
  371. data/lib/herb/engine/scoped_style/collector.rb +74 -0
  372. data/lib/herb/engine/scoped_style/visitor.rb +533 -0
  373. data/lib/herb/engine/slots/annotation.rb +98 -0
  374. data/lib/herb/engine/slots/bindings.rb +61 -0
  375. data/lib/herb/engine/slots/callee.rb +118 -0
  376. data/lib/herb/engine/slots/component_definitions.rb +67 -0
  377. data/lib/herb/engine/slots/components/async.rb +24 -0
  378. data/lib/herb/engine/slots/components/base.rb +132 -0
  379. data/lib/herb/engine/slots/components/deferred.rb +81 -0
  380. data/lib/herb/engine/slots/components/fallback.rb +27 -0
  381. data/lib/herb/engine/slots/components/fragment.rb +99 -0
  382. data/lib/herb/engine/slots/components/lazy.rb +24 -0
  383. data/lib/herb/engine/slots/components.rb +52 -0
  384. data/lib/herb/engine/slots/dependencies/channel.rb +72 -0
  385. data/lib/herb/engine/slots/dependencies.rb +269 -0
  386. data/lib/herb/engine/slots/dynamics_compiler.rb +864 -0
  387. data/lib/herb/engine/slots/identifier.rb +35 -0
  388. data/lib/herb/engine/slots/manifest/channel.rb +74 -0
  389. data/lib/herb/engine/slots/manifest/collector.rb +75 -0
  390. data/lib/herb/engine/slots/markers.rb +152 -0
  391. data/lib/herb/engine/slots/render_bindings.rb +141 -0
  392. data/lib/herb/engine/slots/schema_compiler.rb +94 -0
  393. data/lib/herb/engine/slots/state_anchor.rb +94 -0
  394. data/lib/herb/engine/slots/state_compiler.rb +1325 -0
  395. data/lib/herb/engine/slots/state_directives.rb +854 -0
  396. data/lib/herb/engine/slots/state_kinds.rb +54 -0
  397. data/lib/herb/engine/slots/state_operators.rb +36 -0
  398. data/lib/herb/engine/slots/state_overrides.rb +79 -0
  399. data/lib/herb/engine/slots/state_predicates.rb +65 -0
  400. data/lib/herb/engine/slots/state_transforms.rb +42 -0
  401. data/lib/herb/engine/slots/statics.rb +185 -0
  402. data/lib/herb/engine/slots/subtree.rb +82 -0
  403. data/lib/herb/engine/slots/subtree_compiler.rb +255 -0
  404. data/lib/herb/engine/slots/types.rb +42 -0
  405. data/lib/herb/engine/slots/visitor.rb +2267 -0
  406. data/lib/herb/engine/validators/accessibility_validator.rb +3 -3
  407. data/lib/herb/engine/validators/base.rb +36 -0
  408. data/lib/herb/engine/validators/generator_template_validator.rb +39 -0
  409. data/lib/herb/engine/validators/nesting_validator.rb +3 -3
  410. data/lib/herb/engine/validators/render_validator.rb +13 -33
  411. data/lib/herb/engine/validators/security_validator.rb +3 -17
  412. data/lib/herb/engine/validators.rb +43 -0
  413. data/lib/herb/engine/visitors/auto_close_omitted_tags_visitor.rb +48 -0
  414. data/lib/herb/engine/visitors/content_for_visitor.rb +127 -0
  415. data/lib/herb/engine/{debug_visitor.rb → visitors/debug_visitor.rb} +122 -94
  416. data/lib/herb/engine/visitors/html_safe_assertions_visitor.rb +281 -0
  417. data/lib/herb/engine/visitors/instrumentation_visitor.rb +290 -0
  418. data/lib/herb/engine/visitors/optimize_visitor.rb +728 -0
  419. data/lib/herb/engine/visitors/remove_comments_visitor.rb +106 -0
  420. data/lib/herb/engine/visitors/source_attribution_visitor.rb +177 -0
  421. data/lib/herb/engine.rb +218 -259
  422. data/lib/herb/ensure_installed.rb +43 -0
  423. data/lib/herb/errors.rb +681 -153
  424. data/lib/herb/fingerprint.rb +82 -0
  425. data/lib/herb/html/elements.rb +18 -0
  426. data/lib/herb/html/util.rb +21 -2
  427. data/lib/herb/lex_result.rb +0 -10
  428. data/lib/herb/locate.rb +163 -0
  429. data/lib/herb/location.rb +20 -0
  430. data/lib/herb/parse_result.rb +17 -11
  431. data/lib/herb/parser_options.rb +69 -40
  432. data/lib/herb/position.rb +19 -0
  433. data/lib/herb/prism_inspect.rb +1 -4
  434. data/lib/herb/project.rb +39 -23
  435. data/lib/herb/range.rb +5 -0
  436. data/lib/herb/result.rb +2 -2
  437. data/lib/herb/ruby_program.rb +110 -0
  438. data/lib/herb/source_path.rb +232 -0
  439. data/lib/herb/token.rb +14 -3
  440. data/lib/herb/version.rb +1 -1
  441. data/lib/herb/visitor/context/origin.rb +52 -0
  442. data/lib/herb/visitor/context/replacements.rb +59 -0
  443. data/lib/herb/visitor/context.rb +169 -0
  444. data/lib/herb/visitor/context_aware.rb +45 -0
  445. data/lib/herb/visitor/diagnostics.rb +148 -0
  446. data/lib/herb/visitor/experimental.rb +33 -0
  447. data/lib/herb/visitor/parser_option_requirements.rb +151 -0
  448. data/lib/herb/visitor/stack.rb +192 -0
  449. data/lib/herb/visitor.rb +47 -1
  450. data/lib/herb/warnings.rb +25 -0
  451. data/lib/herb.rb +74 -63
  452. data/sig/herb/action_view/helper_registry.rbs +82 -10
  453. data/sig/herb/analysis/partial_declaration.rbs +80 -0
  454. data/sig/herb/analysis/partial_index.rbs +66 -0
  455. data/sig/herb/analysis/partial_resolution.rbs +57 -0
  456. data/sig/herb/analysis/partial_resolver.rbs +76 -0
  457. data/sig/herb/analysis/project_index.rbs +57 -0
  458. data/sig/herb/{action_view → analysis}/render_analyzer.rbs +25 -4
  459. data/sig/herb/analysis/render_graph/ancestor_chain.rbs +22 -0
  460. data/sig/herb/analysis/render_graph/builder.rbs +180 -0
  461. data/sig/herb/analysis/render_graph/call_frame.rbs +26 -0
  462. data/sig/herb/analysis/render_graph/call_site_location.rbs +28 -0
  463. data/sig/herb/analysis/render_graph/inferred_signature.rbs +27 -0
  464. data/sig/herb/analysis/render_graph/partial_call_site.rbs +36 -0
  465. data/sig/herb/analysis/render_graph/partial_context.rbs +31 -0
  466. data/sig/herb/analysis/render_graph/template_roots.rbs +32 -0
  467. data/sig/herb/analysis/render_graph.rbs +83 -0
  468. data/sig/herb/analysis/ruby_locals_index/local.rbs +21 -0
  469. data/sig/herb/analysis/ruby_locals_index/named_reference.rbs +20 -0
  470. data/sig/herb/analysis/ruby_locals_index/offset_table.rbs +26 -0
  471. data/sig/herb/analysis/ruby_locals_index/reference_collector.rbs +40 -0
  472. data/sig/herb/analysis/ruby_locals_index/state_locals.rbs +19 -0
  473. data/sig/herb/analysis/ruby_locals_index.rbs +62 -0
  474. data/sig/herb/analysis/ruby_reference.rbs +28 -0
  475. data/sig/herb/analysis/template_dependencies/dependency_collector.rbs +52 -0
  476. data/sig/herb/analysis/template_dependencies/node_dependency_collector.rbs +81 -0
  477. data/sig/herb/analysis/template_dependencies.rbs +125 -0
  478. data/sig/herb/ast/erb_render_node.rbs +10 -2
  479. data/sig/herb/ast/helpers.rbs +31 -1
  480. data/sig/herb/ast/herb_directive_node.rbs +13 -0
  481. data/sig/herb/ast/node.rbs +9 -0
  482. data/sig/herb/ast/nodes.rbs +668 -89
  483. data/sig/herb/backend.rbs +52 -0
  484. data/sig/herb/bootstrap.rbs +2 -0
  485. data/sig/herb/colors.rbs +3 -0
  486. data/sig/herb/configuration.rbs +27 -3
  487. data/sig/herb/dev/assets.rbs +27 -0
  488. data/sig/herb/dev/classifier.rbs +57 -0
  489. data/sig/herb/dev/embedded.rbs +21 -0
  490. data/sig/herb/dev/pipeline.rbs +118 -0
  491. data/sig/herb/dev/protocol.rbs +78 -0
  492. data/sig/herb/dev/runner.rbs +64 -23
  493. data/sig/herb/dev/server.rbs +49 -22
  494. data/sig/herb/dev/server_entry.rbs +46 -21
  495. data/sig/herb/dev/watcher.rbs +84 -0
  496. data/sig/herb/dev.rbs +58 -0
  497. data/sig/herb/diagnostic/formatter.rbs +28 -0
  498. data/sig/herb/diagnostic.rbs +88 -0
  499. data/sig/herb/diff/operation.rbs +36 -0
  500. data/sig/herb/diff/result.rbs +36 -0
  501. data/sig/herb/diff.rbs +6 -0
  502. data/sig/herb/engine/collector.rbs +48 -0
  503. data/sig/herb/engine/compiler.rbs +74 -24
  504. data/sig/herb/engine/component_tags/constant_resolver.rbs +19 -0
  505. data/sig/herb/engine/component_tags/partial_resolver.rbs +30 -0
  506. data/sig/herb/engine/component_tags/resolver.rbs +25 -0
  507. data/sig/herb/engine/component_tags/visitor.rbs +69 -0
  508. data/sig/herb/engine/css_inliner/inliner.rbs +65 -0
  509. data/sig/herb/engine/css_inliner/visitor.rbs +87 -0
  510. data/sig/herb/engine/errors.rbs +68 -0
  511. data/sig/herb/engine/helpers.rbs +28 -0
  512. data/sig/herb/engine/inline_render/inliner.rbs +39 -0
  513. data/sig/herb/engine/inline_render/visitor.rbs +104 -0
  514. data/sig/herb/engine/runtime/entry.rbs +43 -0
  515. data/sig/herb/engine/runtime/error_page.rbs +115 -0
  516. data/sig/herb/engine/runtime/html_safe_assertions.rbs +99 -0
  517. data/sig/herb/engine/runtime/journal/summary.rbs +172 -0
  518. data/sig/herb/engine/runtime/journal.rbs +126 -0
  519. data/sig/herb/engine/runtime/middleware.rbs +96 -0
  520. data/sig/herb/engine/runtime/observations.rbs +38 -0
  521. data/sig/herb/engine/runtime/report.rbs +97 -0
  522. data/sig/herb/engine/runtime/session.rbs +233 -0
  523. data/sig/herb/engine/runtime.rbs +14 -0
  524. data/sig/herb/engine/scoped_style/channel.rbs +45 -0
  525. data/sig/herb/engine/scoped_style/collector.rbs +45 -0
  526. data/sig/herb/engine/scoped_style/visitor.rbs +220 -0
  527. data/sig/herb/engine/slots/annotation.rbs +72 -0
  528. data/sig/herb/engine/slots/bindings.rbs +34 -0
  529. data/sig/herb/engine/slots/callee.rbs +62 -0
  530. data/sig/herb/engine/slots/component_definitions.rbs +19 -0
  531. data/sig/herb/engine/slots/components/async.rbs +20 -0
  532. data/sig/herb/engine/slots/components/base.rbs +60 -0
  533. data/sig/herb/engine/slots/components/deferred.rbs +29 -0
  534. data/sig/herb/engine/slots/components/fallback.rbs +21 -0
  535. data/sig/herb/engine/slots/components/fragment.rbs +34 -0
  536. data/sig/herb/engine/slots/components/lazy.rbs +20 -0
  537. data/sig/herb/engine/slots/components.rbs +30 -0
  538. data/sig/herb/engine/slots/dependencies/channel.rbs +51 -0
  539. data/sig/herb/engine/slots/dependencies.rbs +94 -0
  540. data/sig/herb/engine/slots/dynamics_compiler.rbs +352 -0
  541. data/sig/herb/engine/slots/identifier.rbs +19 -0
  542. data/sig/herb/engine/slots/manifest/channel.rbs +49 -0
  543. data/sig/herb/engine/slots/manifest/collector.rbs +45 -0
  544. data/sig/herb/engine/slots/markers.rbs +99 -0
  545. data/sig/herb/engine/slots/render_bindings.rbs +69 -0
  546. data/sig/herb/engine/slots/schema_compiler.rbs +62 -0
  547. data/sig/herb/engine/slots/state_anchor.rbs +41 -0
  548. data/sig/herb/engine/slots/state_compiler.rbs +258 -0
  549. data/sig/herb/engine/slots/state_directives.rbs +278 -0
  550. data/sig/herb/engine/slots/state_kinds.rbs +24 -0
  551. data/sig/herb/engine/slots/state_operators.rbs +19 -0
  552. data/sig/herb/engine/slots/state_overrides.rbs +33 -0
  553. data/sig/herb/engine/slots/state_predicates.rbs +16 -0
  554. data/sig/herb/engine/slots/state_transforms.rbs +15 -0
  555. data/sig/herb/engine/slots/statics.rbs +67 -0
  556. data/sig/herb/engine/slots/subtree.rbs +49 -0
  557. data/sig/herb/engine/slots/subtree_compiler.rbs +130 -0
  558. data/sig/herb/engine/slots/types.rbs +46 -0
  559. data/sig/herb/engine/slots/visitor.rbs +603 -0
  560. data/sig/herb/engine/validators/accessibility_validator.rbs +1 -1
  561. data/sig/herb/engine/validators/base.rbs +28 -0
  562. data/sig/herb/engine/validators/generator_template_validator.rbs +19 -0
  563. data/sig/herb/engine/validators/nesting_validator.rbs +1 -1
  564. data/sig/herb/engine/validators/render_validator.rbs +2 -6
  565. data/sig/herb/engine/validators/security_validator.rbs +1 -3
  566. data/sig/herb/engine/validators.rbs +21 -0
  567. data/sig/herb/engine/visitors/auto_close_omitted_tags_visitor.rbs +26 -0
  568. data/sig/herb/engine/visitors/content_for_visitor.rbs +52 -0
  569. data/sig/herb/engine/{debug_visitor.rbs → visitors/debug_visitor.rbs} +26 -15
  570. data/sig/herb/engine/visitors/html_safe_assertions_visitor.rbs +100 -0
  571. data/sig/herb/engine/visitors/instrumentation_visitor.rbs +111 -0
  572. data/sig/herb/engine/visitors/optimize_visitor.rbs +246 -0
  573. data/sig/herb/engine/visitors/remove_comments_visitor.rbs +66 -0
  574. data/sig/herb/engine/visitors/source_attribution_visitor.rbs +90 -0
  575. data/sig/herb/engine.rbs +47 -41
  576. data/sig/herb/ensure_installed.rbs +9 -0
  577. data/sig/herb/errors.rbs +311 -0
  578. data/sig/herb/fingerprint.rbs +37 -0
  579. data/sig/herb/html/elements.rbs +19 -0
  580. data/sig/herb/html/util.rbs +15 -1
  581. data/sig/herb/lex_result.rbs +0 -6
  582. data/sig/herb/locate.rbs +89 -0
  583. data/sig/herb/location.rbs +12 -0
  584. data/sig/herb/parse_result.rbs +10 -8
  585. data/sig/herb/parser_options.rbs +53 -19
  586. data/sig/herb/position.rbs +11 -0
  587. data/sig/herb/range.rbs +3 -0
  588. data/sig/herb/ruby_program.rbs +51 -0
  589. data/sig/herb/source_path.rbs +132 -0
  590. data/sig/herb/token.rbs +5 -2
  591. data/sig/herb/visitor/context/origin.rbs +51 -0
  592. data/sig/herb/visitor/context/replacements.rbs +39 -0
  593. data/sig/herb/visitor/context.rbs +83 -0
  594. data/sig/herb/visitor/context_aware.rbs +32 -0
  595. data/sig/herb/visitor/diagnostics.rbs +82 -0
  596. data/sig/herb/visitor/experimental.rbs +23 -0
  597. data/sig/herb/visitor/parser_option_requirements.rbs +76 -0
  598. data/sig/herb/visitor/stack.rbs +91 -0
  599. data/sig/herb/visitor.rbs +22 -0
  600. data/sig/herb/warnings.rbs +10 -0
  601. data/sig/herb.rbs +27 -10
  602. data/sig/manifest.yaml +13 -0
  603. data/sig/serialized_ast_errors.rbs +62 -1
  604. data/sig/serialized_ast_nodes.rbs +59 -2
  605. data/src/analyze/action_view/attribute_extraction_helpers.c +73 -36
  606. data/src/analyze/action_view/generated_handlers.c +37 -1
  607. data/src/analyze/action_view/generated_handlers.h +2 -1
  608. data/src/analyze/action_view/helper_registry.c +1353 -58
  609. data/src/analyze/action_view/registry.c +10 -17
  610. data/src/analyze/action_view/stylesheet_link_tag.c +40 -0
  611. data/src/analyze/action_view/tag_helper_node_builders.c +63 -3
  612. data/src/analyze/action_view/tag_helpers.c +502 -111
  613. data/src/analyze/analyze.c +306 -89
  614. data/src/analyze/analyze_helpers.c +502 -237
  615. data/src/analyze/analyzed_ruby.c +2 -0
  616. data/src/analyze/builders.c +80 -51
  617. data/src/analyze/conditional_elements.c +13 -8
  618. data/src/analyze/conditional_open_tags.c +11 -5
  619. data/src/analyze/control_type.c +36 -13
  620. data/src/analyze/herb_directives.c +701 -0
  621. data/src/analyze/invalid_structures.c +11 -7
  622. data/src/analyze/iteration_nodes.c +306 -0
  623. data/src/analyze/missing_end.c +116 -32
  624. data/src/analyze/parse_errors.c +8 -3
  625. data/src/analyze/postfix_conditionals.c +98 -63
  626. data/src/analyze/prism_annotate.c +45 -13
  627. data/src/analyze/render_nodes.c +348 -123
  628. data/src/analyze/strict_locals.c +30 -26
  629. data/src/analyze/ternary_conditionals.c +95 -70
  630. data/src/analyze/transform.c +43 -1
  631. data/src/ast/ast_node.c +3 -13
  632. data/src/ast/ast_nodes.c +914 -113
  633. data/src/ast/ast_pretty_print.c +166 -3
  634. data/src/diff/herb_diff.c +8 -0
  635. data/src/diff/herb_diff_attributes.c +1 -0
  636. data/src/diff/herb_diff_children.c +4 -3
  637. data/src/diff/herb_diff_helpers.c +7 -1
  638. data/src/diff/herb_diff_nodes.c +186 -71
  639. data/src/diff/herb_hash_tree.c +99 -1
  640. data/src/errors.c +1124 -105
  641. data/src/extract.c +227 -144
  642. data/src/herb.c +43 -8
  643. data/src/include/analyze/action_view/attribute_extraction_helpers.h +1 -1
  644. data/src/include/analyze/action_view/helper_registry.h +28 -4
  645. data/src/include/analyze/action_view/tag_helper_handler.h +1 -1
  646. data/src/include/analyze/action_view/tag_helper_node_builders.h +7 -0
  647. data/src/include/analyze/analyze.h +11 -22
  648. data/src/include/analyze/analyzed_ruby.h +25 -0
  649. data/src/include/analyze/builders.h +16 -0
  650. data/src/include/analyze/helpers.h +25 -24
  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 +78 -3
  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 +163 -38
  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.h +1 -0
  665. data/src/include/lexer/lexer_peek_helpers.h +8 -1
  666. data/src/include/lexer/lexer_struct.h +4 -0
  667. data/src/include/lexer/token.h +5 -0
  668. data/src/include/lexer/token_struct.h +8 -6
  669. data/src/include/lib/hb_allocator.h +2 -1
  670. data/src/include/lib/hb_array.h +2 -0
  671. data/src/include/lib/hb_clock.h +15 -0
  672. data/src/include/lib/hb_foreach.h +1 -1
  673. data/src/include/lib/hb_string.h +7 -0
  674. data/src/include/parser/dot_notation.h +1 -1
  675. data/src/include/parser/foreign_content_elements.h +32 -0
  676. data/src/include/parser/parser.h +40 -26
  677. data/src/include/parser/parser_helpers.h +13 -11
  678. data/src/include/parser/parser_options.h +41 -0
  679. data/src/include/prism/herb_prism_node.h +6 -1
  680. data/src/include/prism/ruby_parser.h +6 -0
  681. data/src/include/util/html_elements.h +85 -0
  682. data/src/include/util/html_util.h +1 -0
  683. data/src/include/version.h +1 -1
  684. data/src/lexer/lexer_peek_helpers.c +15 -1
  685. data/src/lexer/token.c +89 -1
  686. data/src/lexer.c +228 -12
  687. data/src/lib/hb_allocator.c +2 -0
  688. data/src/lib/hb_array.c +26 -0
  689. data/src/lib/hb_string.c +26 -0
  690. data/src/main.c +1 -1
  691. data/src/parser/dot_notation.c +4 -3
  692. data/src/parser/match_tags.c +66 -1
  693. data/src/parser/parser_helpers.c +69 -37
  694. data/src/parser.c +658 -385
  695. data/src/parser_options_defaults.c +30 -0
  696. data/src/prism/prism_helpers.c +94 -0
  697. data/src/prism/ruby_parser.c +49 -0
  698. data/src/util/html_util.c +11 -58
  699. data/src/visitor.c +77 -1
  700. data/templates/ext/herb/error_helpers.c.erb +1 -1
  701. data/templates/ext/herb/nodes.c.erb +19 -14
  702. data/templates/ext/herb/nodes.h.erb +2 -1
  703. data/templates/ext/herb/parser_options_helpers.c.erb +69 -0
  704. data/templates/ext/herb/parser_options_helpers.h.erb +11 -0
  705. data/templates/java/nodes.c.erb +21 -17
  706. data/templates/java/nodes.h.erb +3 -2
  707. data/templates/java/org/herb/ParserOptions.java.erb +24 -0
  708. data/templates/java/org/herb/ast/HelperRegistry.java.erb +67 -31
  709. data/templates/java/org/herb/ast/HelperType.java.erb +29 -0
  710. data/templates/java/org/herb/ast/Nodes.java.erb +11 -7
  711. data/templates/java/parser_options_helpers.c.erb +45 -0
  712. data/templates/java/parser_options_helpers.h.erb +10 -0
  713. data/templates/javascript/packages/client/src/state-kinds.ts.erb +49 -0
  714. data/templates/javascript/packages/client/src/state-operators.ts.erb +15 -0
  715. data/templates/javascript/packages/client/src/state-predicates.ts.erb +27 -0
  716. data/templates/javascript/packages/client/src/state-transforms.ts.erb +19 -0
  717. data/templates/javascript/packages/core/src/action-view-helpers.ts.erb +47 -0
  718. data/templates/javascript/packages/core/src/built-in-components.ts.erb +67 -0
  719. data/templates/javascript/packages/core/src/errors.ts.erb +9 -3
  720. data/templates/javascript/packages/core/src/foreign-content-elements.ts.erb +18 -0
  721. data/templates/javascript/packages/core/src/html-constants.ts.erb +32 -0
  722. data/templates/javascript/packages/core/src/html-elements.ts.erb +41 -0
  723. data/templates/javascript/packages/core/src/node-type-guards.ts.erb +11 -7
  724. data/templates/javascript/packages/core/src/nodes.ts.erb +103 -20
  725. data/templates/javascript/packages/core/src/parser-options.ts.erb +41 -0
  726. data/templates/javascript/packages/core/src/visitor.ts.erb +1 -1
  727. data/templates/javascript/packages/node/extension/error_helpers.cpp.erb +3 -3
  728. data/templates/javascript/packages/node/extension/nodes.cpp.erb +18 -13
  729. data/templates/javascript/packages/node/extension/nodes.h.erb +3 -3
  730. data/templates/javascript/packages/node/extension/parser_options_helpers.cpp.erb +41 -0
  731. data/templates/javascript/packages/node/extension/parser_options_helpers.h.erb +12 -0
  732. data/templates/lib/herb/action_view/helper_registry.rb.erb +77 -3
  733. data/templates/lib/herb/ast/nodes.rb.erb +16 -6
  734. data/templates/lib/herb/engine/slots/component_definitions.rb.erb +38 -0
  735. data/templates/lib/herb/engine/slots/state_kinds.rb.erb +37 -0
  736. data/templates/lib/herb/engine/slots/state_operators.rb.erb +26 -0
  737. data/templates/lib/herb/engine/slots/state_predicates.rb.erb +39 -0
  738. data/templates/lib/herb/engine/slots/state_transforms.rb.erb +27 -0
  739. data/templates/lib/herb/errors.rb.erb +34 -6
  740. data/templates/lib/herb/html/elements.rb.erb +12 -0
  741. data/templates/lib/herb/parser_options.rb.erb +32 -0
  742. data/templates/lib/herb/visitor.rb.erb +7 -1
  743. data/templates/rust/herb-printer/src/printer_visitor.rs.erb +14 -0
  744. data/templates/rust/src/action_view_helpers.rs.erb +38 -0
  745. data/templates/rust/src/ast/nodes.rs.erb +21 -31
  746. data/templates/rust/src/errors.rs.erb +1 -1
  747. data/templates/rust/src/nodes.rs.erb +58 -26
  748. data/templates/rust/src/parser_options.rs.erb +16 -0
  749. data/templates/rust/src/prism/generated_deserialize.rs.erb +109 -0
  750. data/templates/rust/src/prism/generated_prettyprint.rs.erb +153 -0
  751. data/templates/rust/src/union_types.rs.erb +7 -1
  752. data/templates/rust/src/visitor.rs.erb +1 -1
  753. data/templates/src/analyze/action_view/helper_registry.c.erb +22 -0
  754. data/templates/src/analyze/missing_end.c.erb +13 -5
  755. data/templates/src/ast/ast_nodes.c.erb +41 -2
  756. data/templates/src/diff/herb_diff_nodes.c.erb +44 -8
  757. data/templates/src/errors.c.erb +8 -3
  758. data/templates/src/include/analyze/action_view/helper_registry.h.erb +19 -0
  759. data/templates/src/include/analyze/herb_directive_kinds.h.erb +25 -0
  760. data/templates/src/include/errors.h.erb +3 -1
  761. data/templates/src/include/parser/foreign_content_elements.h.erb +23 -0
  762. data/templates/src/include/parser/parser_options.h.erb +21 -0
  763. data/templates/src/include/util/html_elements.h.erb +28 -0
  764. data/templates/src/parser_options_defaults.c.erb +9 -0
  765. data/templates/template.rb +564 -9
  766. data/templates/wasm/error_helpers.cpp.erb +4 -4
  767. data/templates/wasm/nodes.cpp.erb +17 -27
  768. data/templates/wasm/nodes.h.erb +3 -3
  769. data/templates/wasm/parser_options_helpers.cpp.erb +25 -0
  770. data/templates/wasm/parser_options_helpers.h.erb +12 -0
  771. metadata +309 -30
  772. data/config/action_view_helpers/actionview/form_helper/button.yml +0 -38
  773. data/config/action_view_helpers/actionview/form_helper/submit.yml +0 -35
  774. data/lib/herb/ast.rb +0 -7
  775. data/lib/herb/diff_operation.rb +0 -34
  776. data/lib/herb/diff_result.rb +0 -59
  777. data/lib/herb/engine/error_formatter.rb +0 -432
  778. data/lib/herb/engine/parser_error_overlay.rb +0 -771
  779. data/lib/herb/engine/validation_error_overlay.rb +0 -182
  780. data/lib/herb/engine/validation_errors.rb +0 -65
  781. data/lib/herb/engine/validator.rb +0 -80
  782. data/sig/herb/ast.rbs +0 -6
  783. data/sig/herb/diff_operation.rbs +0 -34
  784. data/sig/herb/diff_result.rbs +0 -34
  785. data/sig/herb/engine/debug.rbs +0 -38
  786. data/sig/herb/engine/error_formatter.rbs +0 -47
  787. data/sig/herb/engine/parser_error_overlay.rbs +0 -41
  788. data/sig/herb/engine/validation_error_overlay.rbs +0 -35
  789. data/sig/herb/engine/validation_errors.rbs +0 -45
  790. data/sig/herb/engine/validator.rbs +0 -41
  791. data/sig/herb_c_extension.rbs +0 -10
  792. data/sig/vendor/did_you_mean.rbs +0 -6
  793. data/sig/vendor/parallel.rbs +0 -4
data/lib/herb/project.rb CHANGED
@@ -8,11 +8,16 @@ require "pathname"
8
8
  require "English"
9
9
  require "stringio"
10
10
 
11
+ require_relative "colors"
12
+ require_relative "configuration"
13
+
11
14
  module Herb
12
15
  class Project
13
16
  include Colors
14
17
 
15
- attr_accessor :project_path, :output_file, :no_log_file, :no_timing, :silent, :verbose, :isolate, :validate_ruby, :file_paths, :arena_stats, :leak_check
18
+ attr_accessor :project_path, :output_file, :no_log_file, :no_timing, :silent, :verbose, :isolate, :validate_ruby, :file_paths, :arena_stats, :leak_check, :file_timeout
19
+
20
+ DEFAULT_FILE_TIMEOUT = 1 # seconds per file for parse + compile
16
21
 
17
22
  # Known error types that indicate issues in the user's template, not bugs in the parser.
18
23
  TEMPLATE_ERRORS = [
@@ -26,6 +31,7 @@ module Herb
26
31
  "MissingERBEndTagError",
27
32
  "ERBMultipleBlocksInTagError",
28
33
  "ERBCaseWithConditionsError",
34
+ "ERBCaseInlinePatternMatchError",
29
35
  "ConditionalElementMultipleTagsError",
30
36
  "ConditionalElementConditionMismatchError",
31
37
  "InvalidCommentClosingTagError",
@@ -35,8 +41,17 @@ module Herb
35
41
  "UnclosedQuoteError",
36
42
  "MissingAttributeValueError",
37
43
  "UnclosedERBTagError",
44
+ "MalformedERBClosingTagError",
38
45
  "StrayERBClosingTagError",
39
- "NestedERBTagError"
46
+ "NestedERBTagError",
47
+ "MissingWhitespaceBetweenAttributesError",
48
+ "UnexpectedCharacterInAttributeNameError",
49
+ "UnexpectedCharacterInUnquotedAttributeValueError",
50
+ "UnexpectedEqualsSignBeforeAttributeNameError",
51
+ "UnexpectedSolidusInTagError",
52
+ "EndTagWithTrailingSolidusError",
53
+ "UnclosedCommentError",
54
+ "NestedCommentError"
40
55
  ].freeze
41
56
 
42
57
  ISSUE_TYPES = [
@@ -126,6 +141,7 @@ module Herb
126
141
 
127
142
  date = Time.now.strftime("%Y-%m-%d_%H-%M-%S")
128
143
  @output_file = output_file || "#{date}_erb_parsing_result_#{@project_path.basename}.log"
144
+ @file_timeout = DEFAULT_FILE_TIMEOUT
129
145
  end
130
146
 
131
147
  def configuration
@@ -151,16 +167,7 @@ module Herb
151
167
  private
152
168
 
153
169
  def find_files
154
- included = include_patterns.flat_map do |pattern|
155
- Dir[File.join(@project_path, pattern)]
156
- end.uniq
157
-
158
- return included if exclude_patterns.empty?
159
-
160
- included.reject do |file|
161
- relative_path = file.sub("#{@project_path}/", "")
162
- exclude_patterns.any? { |pattern| File.fnmatch?(pattern, relative_path, File::FNM_PATHNAME) }
163
- end
170
+ configuration.find_files(@project_path)
164
171
  end
165
172
 
166
173
  public
@@ -325,13 +332,13 @@ module Herb
325
332
  return unless issue_type[:key] == :invalid_ruby && file_content
326
333
 
327
334
  begin
328
- engine = Herb::Engine.new(file_content, filename: file_path, escape: true, validation_mode: :none)
335
+ engine = Herb::Engine.new(file_content, filename: file_path, escape: true, visitors: [])
329
336
  puts ""
330
337
  puts "**Compiled Ruby:**"
331
338
  puts "```ruby"
332
339
  puts engine.src
333
340
  puts "```"
334
- rescue StandardError
341
+ rescue Herb::Engine::CompilationError, StandardError
335
342
  # Skip if compilation fails entirely
336
343
  end
337
344
  end
@@ -354,7 +361,7 @@ module Herb
354
361
  result[:leak_check] = capture_leak_check(file_content)
355
362
  end
356
363
 
357
- Timeout.timeout(1) do
364
+ Timeout.timeout(file_timeout) do
358
365
  parse_result = Herb.parse(file_content)
359
366
 
360
367
  if parse_result.failed?
@@ -369,7 +376,7 @@ module Herb
369
376
  result
370
377
  rescue Timeout::Error
371
378
  result.merge(status: :timeout, file_content: file_content,
372
- log: "⏱️ Parsing #{file_path} timed out after 1 second")
379
+ log: "⏱️ Parsing #{file_path} timed out after #{file_timeout} #{pluralize(file_timeout, "second")}")
373
380
  rescue StandardError => e
374
381
  file_content ||= begin
375
382
  File.read(file_path)
@@ -388,7 +395,7 @@ module Herb
388
395
  stdout_file = Tempfile.new("stdout")
389
396
  stderr_file = Tempfile.new("stderr")
390
397
 
391
- Timeout.timeout(1) do
398
+ Timeout.timeout(file_timeout) do
392
399
  pid = Process.fork do
393
400
  $stdout.reopen(stdout_file.path, "w")
394
401
  $stderr.reopen(stderr_file.path, "w")
@@ -431,7 +438,7 @@ module Herb
431
438
  nil
432
439
  end
433
440
 
434
- { file_path: file_path, status: :timeout, file_content: file_content, log: "⏱️ Parsing #{file_path} timed out after 1 second" }
441
+ { file_path: file_path, status: :timeout, file_content: file_content, log: "⏱️ Parsing #{file_path} timed out after #{file_timeout} #{pluralize(file_timeout, "second")}" }
435
442
  rescue StandardError => e
436
443
  file_content ||= begin
437
444
  File.read(file_path)
@@ -482,7 +489,16 @@ module Herb
482
489
  end
483
490
 
484
491
  def compile_file(file_path, file_content)
485
- Herb::Engine.new(file_content, filename: file_path, escape: true, validate_ruby: validate_ruby)
492
+ require_relative "engine"
493
+ require_relative "engine/validators"
494
+
495
+ Herb::Engine.new(
496
+ file_content,
497
+ filename: file_path,
498
+ escape: true,
499
+ validate_ruby: validate_ruby,
500
+ visitors: Herb::Engine::Validators.all
501
+ )
486
502
 
487
503
  { status: :successful, log: "✅ Compiled #{file_path} successfully" }
488
504
  rescue Herb::Engine::GeneratorTemplateError => e
@@ -498,13 +514,13 @@ module Herb
498
514
 
499
515
  # Retry without validators
500
516
  begin
501
- Herb::Engine.new(file_content, filename: file_path, escape: true, validation_mode: :none, validate_ruby: validate_ruby)
517
+ Herb::Engine.new(file_content, filename: file_path, escape: true, visitors: [], validate_ruby: validate_ruby)
502
518
  error_name = e.is_a?(Herb::Engine::SecurityError) ? "SecurityError" : "ValidationError"
503
519
  return { status: :validation_error, file_content: file_content,
504
520
  compilation_error: compilation_error,
505
521
  diagnostics: [{ name: error_name, message: e.message }],
506
522
  log: "⚠️ Compilation failed for #{file_path} (validation error)" }
507
- rescue StandardError
523
+ rescue Herb::Engine::CompilationError, StandardError
508
524
  # Not a validator-caused error, continue with other checks
509
525
  end
510
526
 
@@ -515,7 +531,7 @@ module Herb
515
531
  compilation_error: compilation_error,
516
532
  diagnostics: [{ name: "CompilationError", message: "#{e.message} (strict mode)" }],
517
533
  log: "🔒 Compilation failed for #{file_path} (strict mode error)" }
518
- rescue StandardError
534
+ rescue Herb::Engine::CompilationError, StandardError
519
535
  # Fall through
520
536
  end
521
537
 
@@ -903,7 +919,7 @@ module Herb
903
919
  def ensure_parallel!
904
920
  return if defined?(Parallel)
905
921
 
906
- Herb.ensure_installed { gem "parallel" } # steep:ignore
922
+ Herb.ensure_installed("parallel")
907
923
  end
908
924
 
909
925
  def separator
data/lib/herb/range.rb CHANGED
@@ -23,6 +23,11 @@ module Herb
23
23
  new(from, to)
24
24
  end
25
25
 
26
+ #: () -> Range
27
+ def self.zero
28
+ new(0, 0)
29
+ end
30
+
26
31
  #: () -> serialized_range
27
32
  def to_a
28
33
  [from, to] #: Herb::serialized_range
data/lib/herb/result.rb CHANGED
@@ -16,12 +16,12 @@ module Herb
16
16
 
17
17
  #: () -> bool
18
18
  def success?
19
- false
19
+ errors.empty?
20
20
  end
21
21
 
22
22
  #: () -> bool
23
23
  def failed?
24
- true
24
+ errors.any?
25
25
  end
26
26
  end
27
27
  end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ require "prism"
5
+
6
+ module Herb
7
+ # Answers what Ruby a span of a template holds, out of the one Prism program the parser built
8
+ # for the whole document. `extract_ruby` blanks the HTML in place, so a Prism byte offset and a
9
+ # template byte offset are the same number, which is what lets a `Herb::Location` address Ruby.
10
+ #
11
+ # program = Herb::RubyProgram.for(document)
12
+ # program.resolve(node.content.location)&.nodes #=> [#<Prism::CallNode>]
13
+ #
14
+ # The document has to have been parsed with the `prism_program` parser option, and `for` answers
15
+ # `nil` when it was not. `Herb::Locate` is the same question asked of the HTML tree, and
16
+ # `Herb::Analysis::RubyLocalsIndex::OffsetTable` walks the other way, from a Prism offset back
17
+ # to a `Herb::Location`.
18
+ #
19
+ class RubyProgram
20
+ Resolved = Data.define(
21
+ :nodes, #: Array[untyped]
22
+ :offset, #: Integer
23
+ :source #: String
24
+ )
25
+
26
+ #: (untyped) -> RubyProgram?
27
+ def self.for(document)
28
+ return nil unless document.respond_to?(:source) && document.respond_to?(:deserialized_prism_node)
29
+
30
+ source = document.source
31
+ program = document.deserialized_prism_node
32
+
33
+ return nil unless source.is_a?(String) && program
34
+
35
+ new(source, program)
36
+ end
37
+
38
+ #: (String, untyped) -> void
39
+ def initialize(source, program)
40
+ @source = source
41
+ @lines = source.lines
42
+ @starts = [0] #: Array[Integer]
43
+
44
+ @lines.each { |line| @starts << (@starts.fetch(-1) + line.bytesize) }
45
+
46
+ @nodes = flatten(program)
47
+ end
48
+
49
+ #: (Herb::Location?) -> Resolved?
50
+ def resolve(location)
51
+ return nil unless location
52
+
53
+ from = offset_of(location.start)
54
+ to = offset_of(location.end)
55
+
56
+ return nil unless from && to && from < to
57
+
58
+ Resolved.new(nodes: enclosed(from, to), offset: from, source: @source.byteslice(from, to - from).to_s)
59
+ end
60
+
61
+ private
62
+
63
+ #: (untyped) -> Array[untyped]
64
+ def flatten(program)
65
+ found = [] #: Array[untyped]
66
+ queue = [program] #: Array[untyped]
67
+
68
+ until queue.empty?
69
+ node = queue.shift
70
+
71
+ found << node unless node.is_a?(Prism::StatementsNode) || node.is_a?(Prism::ProgramNode)
72
+ queue.concat(node.compact_child_nodes)
73
+ end
74
+
75
+ found.sort_by { |node| [node.location.start_offset, -node.location.end_offset] }
76
+ end
77
+
78
+ #: (Integer, Integer) -> Array[untyped]
79
+ def enclosed(from, to)
80
+ found = [] #: Array[untyped]
81
+ index = @nodes.bsearch_index { |node| node.location.start_offset >= from } || @nodes.length
82
+ reach = from
83
+
84
+ while (node = @nodes[index])
85
+ location = node.location
86
+
87
+ break if location.start_offset >= to
88
+
89
+ if location.start_offset >= reach && location.end_offset <= to
90
+ found << node
91
+ reach = location.end_offset
92
+ end
93
+
94
+ index += 1
95
+ end
96
+
97
+ found
98
+ end
99
+
100
+ #: (untyped) -> Integer?
101
+ def offset_of(position)
102
+ start = @starts[position.line - 1]
103
+ line = @lines[position.line - 1]
104
+
105
+ return nil unless start && line
106
+
107
+ start + line[0, position.column].to_s.bytesize
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,232 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ require "pathname"
5
+
6
+ module Herb
7
+ # A path, and optionally where in it something is.
8
+ #
9
+ # Herb::SourcePath.new("app/views/posts/_card.html.erb", line: 8, column: 2).to_s
10
+ # #=> "app/views/posts/_card.html.erb:8:3"
11
+ #
12
+ # Herb::SourcePath.parse("app/views/posts/_card.html.erb:8:3").position
13
+ # #=> #<Herb::Position (8:2)>
14
+ #
15
+ # `path:line:column` is what an editor linkifies, what a terminal makes clickable, and what
16
+ # `data-herb-source` holds. Herb wrote it by hand in five places and they did not agree, because
17
+ # the column the parser reports is 0-based and the column an editor shows is 1-based. Everything
18
+ # that reads or writes one of these strings goes through here so that conversion happens once.
19
+ #
20
+ # A part that is not known is left out, so a path on its own stays a path:
21
+ #
22
+ # Herb::SourcePath.new("index.html.erb").to_s #=> "index.html.erb"
23
+ # Herb::SourcePath.new("index.html.erb", line: 8).to_s #=> "index.html.erb:8"
24
+ #
25
+ # A scheme is optional too, and turns the same reference into something an editor opens:
26
+ #
27
+ # Herb::SourcePath.new(path, line: 8, column: 2, scheme: "vscode").to_s
28
+ # #=> "vscode://file/app/views/posts/_card.html.erb:8:3"
29
+ #
30
+ # A project turns the same reference into either form. A stamp and a diagnostic show the relative
31
+ # path, and an editor scheme needs the absolute one:
32
+ #
33
+ # reference = Herb::SourcePath.at(path, position, project_path: Rails.root)
34
+ #
35
+ # reference.relative.to_s #=> "app/views/posts/_card.html.erb:8:3"
36
+ # reference.absolute.with_scheme("vscode").to_s
37
+ # #=> "vscode://file/Users/marco/blog/app/views/posts/_card.html.erb:8:3"
38
+ #
39
+ # Neither is guessed. `to_s` writes the path it was handed, so a caller says which one it wants.
40
+ # An editor scheme names a file from the root, so call `absolute` before putting one on.
41
+ #
42
+ # Lines count from one and columns count from zero, which is what the parser reports and what
43
+ # `Herb::Position` carries. Only `to_s` and `parse` speak the 1-based column an editor shows.
44
+ #
45
+ class SourcePath
46
+ PATTERN = %r{
47
+ \A
48
+ (?:(?<scheme>[a-z][a-z0-9+.-]*)://file(?=/))?
49
+ (?<path>.*?)
50
+ (?::(?<line>\d+)(?::(?<column>\d+))?)?
51
+ \z
52
+ }ix #: Regexp
53
+
54
+ FILE_SCHEME_SEPARATOR = "://file" #: String
55
+
56
+ attr_reader :path #: Pathname
57
+ attr_reader :project_path #: Pathname?
58
+ attr_reader :line #: Integer?
59
+ attr_reader :column #: Integer?
60
+ attr_reader :scheme #: String?
61
+
62
+ #: ((String | Pathname), ?project_path: (String | Pathname)?, ?line: Integer?, ?column: Integer?, ?scheme: String?) -> void
63
+ def initialize(path, project_path: nil, line: nil, column: nil, scheme: nil)
64
+ @path = self.class.coerce(path) #: Pathname
65
+ @project_path = project_path.nil? ? nil : self.class.coerce(project_path)
66
+ @line = line
67
+ @column = line.nil? ? nil : column
68
+ @scheme = scheme
69
+
70
+ freeze
71
+ end
72
+
73
+ #: ((String | Pathname), Herb::Position?, ?project_path: (String | Pathname)?, ?scheme: String?) -> SourcePath
74
+ def self.at(path, position, project_path: nil, scheme: nil)
75
+ return new(path, project_path: project_path, scheme: scheme) unless position
76
+
77
+ new(path, project_path: project_path, line: position.line, column: position.column, scheme: scheme)
78
+ end
79
+
80
+ #: ((String | Pathname)) -> Pathname
81
+ def self.coerce(path)
82
+ path.is_a?(Pathname) ? path : Pathname.new(path)
83
+ end
84
+
85
+ #: (String, ?project_path: (String | Pathname)?) -> SourcePath?
86
+ def self.parse(string, project_path: nil)
87
+ match = PATTERN.match(string.to_s)
88
+
89
+ return nil unless match
90
+
91
+ path = match[:path]
92
+
93
+ return nil if path.nil? || path.empty?
94
+
95
+ new(
96
+ path,
97
+ project_path: project_path,
98
+ line: match[:line]&.to_i,
99
+ column: reported_column(match[:column]),
100
+ scheme: match[:scheme]
101
+ )
102
+ end
103
+
104
+ #: (String?) -> Integer?
105
+ def self.reported_column(column)
106
+ return nil unless column
107
+
108
+ [column.to_i - 1, 0].max
109
+ end
110
+
111
+ #: () -> Herb::Position?
112
+ def position
113
+ line = @line
114
+
115
+ return nil unless line
116
+
117
+ Position.new(line, column || 0)
118
+ end
119
+
120
+ #: () -> bool
121
+ def position?
122
+ !@line.nil?
123
+ end
124
+
125
+ #: ((String | Pathname)) -> SourcePath
126
+ def with_path(path)
127
+ self.class.new(path, project_path: project_path, line: line, column: column, scheme: scheme)
128
+ end
129
+
130
+ #: ((String | Pathname)?) -> SourcePath
131
+ def with_project_path(project_path)
132
+ self.class.new(path, project_path: project_path, line: line, column: column, scheme: scheme)
133
+ end
134
+
135
+ #: () -> bool
136
+ def absolute?
137
+ path.absolute?
138
+ end
139
+
140
+ #: () -> Pathname
141
+ def absolute_path
142
+ return path if path.absolute?
143
+
144
+ project = project_path
145
+
146
+ return path unless project
147
+
148
+ project + path
149
+ end
150
+
151
+ #: () -> Pathname
152
+ def relative_path
153
+ project = project_path
154
+
155
+ return path unless project
156
+
157
+ absolute_path.relative_path_from(project)
158
+ rescue ArgumentError
159
+ path
160
+ end
161
+
162
+ #: () -> SourcePath
163
+ def absolute
164
+ with_path(absolute_path)
165
+ end
166
+
167
+ #: () -> SourcePath
168
+ def relative
169
+ with_path(relative_path)
170
+ end
171
+
172
+ #: (Herb::Position?) -> SourcePath
173
+ def with_position(position)
174
+ self.class.at(path, position, project_path: project_path, scheme: scheme)
175
+ end
176
+
177
+ #: (String?) -> SourcePath
178
+ def with_scheme(scheme)
179
+ self.class.new(path, project_path: project_path, line: line, column: column, scheme: scheme)
180
+ end
181
+
182
+ #: () -> String
183
+ def to_s
184
+ "#{prefix}#{written_path}#{suffix}"
185
+ end
186
+
187
+ #: () -> Pathname
188
+ def to_pathname
189
+ path
190
+ end
191
+
192
+ #: (untyped) -> bool
193
+ def ==(other)
194
+ other.is_a?(SourcePath) && to_s == other.to_s
195
+ end
196
+
197
+ alias eql? ==
198
+
199
+ #: () -> Integer
200
+ def hash
201
+ to_s.hash
202
+ end
203
+
204
+ #: () -> String
205
+ def inspect
206
+ %(#<Herb::SourcePath #{self}>)
207
+ end
208
+
209
+ private
210
+
211
+ #: () -> String
212
+ def prefix
213
+ scheme ? "#{scheme}#{FILE_SCHEME_SEPARATOR}" : ""
214
+ end
215
+
216
+ #: () -> String
217
+ def written_path
218
+ written = path.to_s
219
+
220
+ return written unless scheme
221
+
222
+ written.start_with?("/") ? written : "/#{written}"
223
+ end
224
+
225
+ #: () -> String
226
+ def suffix
227
+ return "" unless line
228
+
229
+ column ? ":#{line}:#{column + 1}" : ":#{line}"
230
+ end
231
+ end
232
+ end
data/lib/herb/token.rb CHANGED
@@ -16,6 +16,11 @@ module Herb
16
16
  attr_reader :location #: Location
17
17
  attr_reader :type #: String
18
18
 
19
+ #: (String | Symbol, String, ?location: Location, ?range: Range) -> Token
20
+ def self.from(type, value, location: Location.zero, range: Range.zero)
21
+ new(value.dup, range, location, type.to_s)
22
+ end
23
+
19
24
  #: (String, Range, Location, String) -> void
20
25
  def initialize(value, range, location, type)
21
26
  @value = value
@@ -41,7 +46,9 @@ module Herb
41
46
 
42
47
  #: () -> String
43
48
  def tree_inspect
44
- "#{green("\"#{value.force_encoding("utf-8")}\"")} #{dimmed("(location: #{location.tree_inspect})")}"
49
+ location_inspect = location ? location.tree_inspect : "∅"
50
+
51
+ "#{green("\"#{value.force_encoding("utf-8")}\"")} #{dimmed("(location: #{location_inspect})")}"
45
52
  end
46
53
 
47
54
  #: () -> String
@@ -55,17 +62,21 @@ module Herb
55
62
 
56
63
  #: () -> String
57
64
  def colorize_range
65
+ return "∅" unless range
66
+
58
67
  white("[") + cyan(range.from.to_s) + white(", ") + cyan(range.to.to_s) + white("]")
59
68
  end
60
69
 
61
- #: (Position) -> String
70
+ #: (Position?) -> String
62
71
  def colorize_position(position)
72
+ return "∅" unless position
73
+
63
74
  white("(") + cyan(position.line.to_s) + white(":") + cyan(position.column.to_s) + white(")")
64
75
  end
65
76
 
66
77
  #: () -> String
67
78
  def inspect
68
- "#{white("#<")}#{bold(yellow("Herb::Token"))} #{white("type=")}#{bright_magenta("\"#{type}\"")} #{white("value=")}#{green(value_inspect)} #{white("range=")}#{colorize_range} #{white("start=")}#{colorize_position(location.start)} #{white("end=")}#{colorize_position(location.end)}#{white(">")}"
79
+ "#{white("#<")}#{bold(yellow("Herb::Token"))} #{white("type=")}#{bright_magenta("\"#{type}\"")} #{white("value=")}#{green(value_inspect)} #{white("range=")}#{colorize_range} #{white("start=")}#{colorize_position(location&.start)} #{white("end=")}#{colorize_position(location&.end)}#{white(">")}"
69
80
  end
70
81
  end
71
82
  end
data/lib/herb/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # typed: true
3
3
 
4
4
  module Herb
5
- VERSION = "0.10.1"
5
+ VERSION = "0.11.1"
6
6
  end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+ # typed: false
3
+
4
+ module Herb
5
+ class Visitor
6
+ class Context
7
+ # Which nodes hold content that was written somewhere other than the file being compiled.
8
+ #
9
+ # A visitor that splices one template into another leaves a tree that no longer answers "where
10
+ # was this written" by itself, and the node it wraps the splice in is one nobody wrote at all.
11
+ # Both matter to anything reporting a position back to a developer, and neither is visible from
12
+ # the node alone.
13
+ #
14
+ # origin.authored(wrapper, "app/views/posts/_card.html.erb", from: render_node)
15
+ #
16
+ # origin.of(wrapper).file #=> "app/views/posts/_card.html.erb"
17
+ # origin.of(wrapper).from #=> the `render` tag it replaced
18
+ #
19
+ # What is recorded is the node holding the content rather than each node of it, because what
20
+ # was moved is the partial and not each node of the partial. Recording the nodes themselves
21
+ # would leave nothing saying which of them belong together, and a partial spliced into the
22
+ # middle of another would split the one around it in two.
23
+ #
24
+ # Only nodes that hold content from elsewhere are recorded, so an untouched tree costs nothing.
25
+ # This lives on the compile rather than on a visitor, because the visitor that needs the answer
26
+ # is never the one that knows it.
27
+ #
28
+ class Origin
29
+ Entry = Data.define(:file, :from)
30
+
31
+ #: () -> void
32
+ def initialize
33
+ entries = {} #: Hash[Herb::AST::Node, Entry]
34
+
35
+ @entries = entries.compare_by_identity
36
+ end
37
+
38
+ #: (Herb::AST::Node, String, ?from: Herb::AST::Node?) -> void
39
+ def authored(node, file, from: nil)
40
+ @entries[node] = Entry.new(file, from) unless @entries.key?(node)
41
+
42
+ nil
43
+ end
44
+
45
+ #: (Herb::AST::Node) -> Entry?
46
+ def of(node)
47
+ @entries[node]
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end