hyper-pro-gem 0.0.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 (380) hide show
  1. checksums.yaml +7 -0
  2. data/hyper-pro-gem.gemspec +12 -0
  3. data/yard-0.9.44/CHANGELOG.md +983 -0
  4. data/yard-0.9.44/LEGAL +94 -0
  5. data/yard-0.9.44/LICENSE +22 -0
  6. data/yard-0.9.44/README.md +329 -0
  7. data/yard-0.9.44/bin/yard +13 -0
  8. data/yard-0.9.44/bin/yardoc +13 -0
  9. data/yard-0.9.44/bin/yri +13 -0
  10. data/yard-0.9.44/docs/CodeObjects.md +115 -0
  11. data/yard-0.9.44/docs/GettingStarted.md +705 -0
  12. data/yard-0.9.44/docs/Handlers.md +152 -0
  13. data/yard-0.9.44/docs/Overview.md +61 -0
  14. data/yard-0.9.44/docs/Parser.md +166 -0
  15. data/yard-0.9.44/docs/Tags.md +304 -0
  16. data/yard-0.9.44/docs/TagsArch.md +123 -0
  17. data/yard-0.9.44/docs/Templates.md +497 -0
  18. data/yard-0.9.44/docs/WhatsNew.md +1297 -0
  19. data/yard-0.9.44/docs/images/code-objects-class-diagram.png +0 -0
  20. data/yard-0.9.44/docs/images/handlers-class-diagram.png +0 -0
  21. data/yard-0.9.44/docs/images/overview-class-diagram.png +0 -0
  22. data/yard-0.9.44/docs/images/parser-class-diagram.png +0 -0
  23. data/yard-0.9.44/docs/images/tags-class-diagram.png +0 -0
  24. data/yard-0.9.44/docs/templates/default/fulldoc/html/full_list_tag.erb +9 -0
  25. data/yard-0.9.44/docs/templates/default/fulldoc/html/setup.rb +6 -0
  26. data/yard-0.9.44/docs/templates/default/layout/html/setup.rb +9 -0
  27. data/yard-0.9.44/docs/templates/default/layout/html/tag_list.erb +11 -0
  28. data/yard-0.9.44/docs/templates/default/yard_tags/html/list.erb +18 -0
  29. data/yard-0.9.44/docs/templates/default/yard_tags/html/setup.rb +26 -0
  30. data/yard-0.9.44/docs/templates/plugin.rb +70 -0
  31. data/yard-0.9.44/lib/rubygems_plugin.rb +9 -0
  32. data/yard-0.9.44/lib/yard/autoload.rb +328 -0
  33. data/yard-0.9.44/lib/yard/cli/command.rb +85 -0
  34. data/yard-0.9.44/lib/yard/cli/command_parser.rb +93 -0
  35. data/yard-0.9.44/lib/yard/cli/config.rb +198 -0
  36. data/yard-0.9.44/lib/yard/cli/diff.rb +278 -0
  37. data/yard-0.9.44/lib/yard/cli/display.rb +69 -0
  38. data/yard-0.9.44/lib/yard/cli/gems.rb +84 -0
  39. data/yard-0.9.44/lib/yard/cli/graph.rb +125 -0
  40. data/yard-0.9.44/lib/yard/cli/help.rb +20 -0
  41. data/yard-0.9.44/lib/yard/cli/i18n.rb +70 -0
  42. data/yard-0.9.44/lib/yard/cli/list.rb +23 -0
  43. data/yard-0.9.44/lib/yard/cli/markup_types.rb +32 -0
  44. data/yard-0.9.44/lib/yard/cli/server.rb +266 -0
  45. data/yard-0.9.44/lib/yard/cli/stats.rb +234 -0
  46. data/yard-0.9.44/lib/yard/cli/yardoc.rb +791 -0
  47. data/yard-0.9.44/lib/yard/cli/yardopts_command.rb +110 -0
  48. data/yard-0.9.44/lib/yard/cli/yri.rb +215 -0
  49. data/yard-0.9.44/lib/yard/code_objects/base.rb +626 -0
  50. data/yard-0.9.44/lib/yard/code_objects/class_object.rb +146 -0
  51. data/yard-0.9.44/lib/yard/code_objects/class_variable_object.rb +11 -0
  52. data/yard-0.9.44/lib/yard/code_objects/constant_object.rb +16 -0
  53. data/yard-0.9.44/lib/yard/code_objects/extended_method_object.rb +24 -0
  54. data/yard-0.9.44/lib/yard/code_objects/extra_file_object.rb +135 -0
  55. data/yard-0.9.44/lib/yard/code_objects/macro_object.rb +171 -0
  56. data/yard-0.9.44/lib/yard/code_objects/method_object.rb +196 -0
  57. data/yard-0.9.44/lib/yard/code_objects/module_object.rb +21 -0
  58. data/yard-0.9.44/lib/yard/code_objects/namespace_mapper.rb +141 -0
  59. data/yard-0.9.44/lib/yard/code_objects/namespace_object.rb +200 -0
  60. data/yard-0.9.44/lib/yard/code_objects/proxy.rb +245 -0
  61. data/yard-0.9.44/lib/yard/code_objects/root_object.rb +19 -0
  62. data/yard-0.9.44/lib/yard/config.rb +274 -0
  63. data/yard-0.9.44/lib/yard/core_ext/array.rb +16 -0
  64. data/yard-0.9.44/lib/yard/core_ext/file.rb +69 -0
  65. data/yard-0.9.44/lib/yard/core_ext/hash.rb +16 -0
  66. data/yard-0.9.44/lib/yard/core_ext/insertion.rb +63 -0
  67. data/yard-0.9.44/lib/yard/core_ext/module.rb +11 -0
  68. data/yard-0.9.44/lib/yard/core_ext/string.rb +68 -0
  69. data/yard-0.9.44/lib/yard/core_ext/symbol_hash.rb +75 -0
  70. data/yard-0.9.44/lib/yard/docstring.rb +386 -0
  71. data/yard-0.9.44/lib/yard/docstring_parser.rb +344 -0
  72. data/yard-0.9.44/lib/yard/gem_index.rb +29 -0
  73. data/yard-0.9.44/lib/yard/globals.rb +22 -0
  74. data/yard-0.9.44/lib/yard/handlers/base.rb +617 -0
  75. data/yard-0.9.44/lib/yard/handlers/c/alias_handler.rb +16 -0
  76. data/yard-0.9.44/lib/yard/handlers/c/attribute_handler.rb +13 -0
  77. data/yard-0.9.44/lib/yard/handlers/c/base.rb +164 -0
  78. data/yard-0.9.44/lib/yard/handlers/c/class_handler.rb +27 -0
  79. data/yard-0.9.44/lib/yard/handlers/c/constant_handler.rb +13 -0
  80. data/yard-0.9.44/lib/yard/handlers/c/handler_methods.rb +212 -0
  81. data/yard-0.9.44/lib/yard/handlers/c/init_handler.rb +20 -0
  82. data/yard-0.9.44/lib/yard/handlers/c/method_handler.rb +45 -0
  83. data/yard-0.9.44/lib/yard/handlers/c/mixin_handler.rb +21 -0
  84. data/yard-0.9.44/lib/yard/handlers/c/module_handler.rb +17 -0
  85. data/yard-0.9.44/lib/yard/handlers/c/override_comment_handler.rb +31 -0
  86. data/yard-0.9.44/lib/yard/handlers/c/path_handler.rb +11 -0
  87. data/yard-0.9.44/lib/yard/handlers/c/struct_handler.rb +13 -0
  88. data/yard-0.9.44/lib/yard/handlers/c/symbol_handler.rb +8 -0
  89. data/yard-0.9.44/lib/yard/handlers/common/method_handler.rb +19 -0
  90. data/yard-0.9.44/lib/yard/handlers/processor.rb +200 -0
  91. data/yard-0.9.44/lib/yard/handlers/rbs/attribute_handler.rb +79 -0
  92. data/yard-0.9.44/lib/yard/handlers/rbs/base.rb +38 -0
  93. data/yard-0.9.44/lib/yard/handlers/rbs/constant_handler.rb +18 -0
  94. data/yard-0.9.44/lib/yard/handlers/rbs/method_handler.rb +327 -0
  95. data/yard-0.9.44/lib/yard/handlers/rbs/mixin_handler.rb +20 -0
  96. data/yard-0.9.44/lib/yard/handlers/rbs/namespace_handler.rb +26 -0
  97. data/yard-0.9.44/lib/yard/handlers/ruby/alias_handler.rb +45 -0
  98. data/yard-0.9.44/lib/yard/handlers/ruby/attribute_handler.rb +90 -0
  99. data/yard-0.9.44/lib/yard/handlers/ruby/base.rb +165 -0
  100. data/yard-0.9.44/lib/yard/handlers/ruby/class_condition_handler.rb +92 -0
  101. data/yard-0.9.44/lib/yard/handlers/ruby/class_handler.rb +119 -0
  102. data/yard-0.9.44/lib/yard/handlers/ruby/class_variable_handler.rb +17 -0
  103. data/yard-0.9.44/lib/yard/handlers/ruby/comment_handler.rb +10 -0
  104. data/yard-0.9.44/lib/yard/handlers/ruby/constant_handler.rb +73 -0
  105. data/yard-0.9.44/lib/yard/handlers/ruby/decorator_handler_methods.rb +123 -0
  106. data/yard-0.9.44/lib/yard/handlers/ruby/dsl_handler.rb +15 -0
  107. data/yard-0.9.44/lib/yard/handlers/ruby/dsl_handler_methods.rb +96 -0
  108. data/yard-0.9.44/lib/yard/handlers/ruby/exception_handler.rb +27 -0
  109. data/yard-0.9.44/lib/yard/handlers/ruby/extend_handler.rb +22 -0
  110. data/yard-0.9.44/lib/yard/handlers/ruby/legacy/alias_handler.rb +37 -0
  111. data/yard-0.9.44/lib/yard/handlers/ruby/legacy/attribute_handler.rb +65 -0
  112. data/yard-0.9.44/lib/yard/handlers/ruby/legacy/base.rb +245 -0
  113. data/yard-0.9.44/lib/yard/handlers/ruby/legacy/class_condition_handler.rb +83 -0
  114. data/yard-0.9.44/lib/yard/handlers/ruby/legacy/class_handler.rb +113 -0
  115. data/yard-0.9.44/lib/yard/handlers/ruby/legacy/class_variable_handler.rb +15 -0
  116. data/yard-0.9.44/lib/yard/handlers/ruby/legacy/comment_handler.rb +10 -0
  117. data/yard-0.9.44/lib/yard/handlers/ruby/legacy/constant_handler.rb +29 -0
  118. data/yard-0.9.44/lib/yard/handlers/ruby/legacy/dsl_handler.rb +17 -0
  119. data/yard-0.9.44/lib/yard/handlers/ruby/legacy/exception_handler.rb +13 -0
  120. data/yard-0.9.44/lib/yard/handlers/ruby/legacy/extend_handler.rb +21 -0
  121. data/yard-0.9.44/lib/yard/handlers/ruby/legacy/method_handler.rb +90 -0
  122. data/yard-0.9.44/lib/yard/handlers/ruby/legacy/mixin_handler.rb +39 -0
  123. data/yard-0.9.44/lib/yard/handlers/ruby/legacy/module_function_handler.rb +19 -0
  124. data/yard-0.9.44/lib/yard/handlers/ruby/legacy/module_handler.rb +12 -0
  125. data/yard-0.9.44/lib/yard/handlers/ruby/legacy/private_class_method_handler.rb +22 -0
  126. data/yard-0.9.44/lib/yard/handlers/ruby/legacy/private_constant_handler.rb +22 -0
  127. data/yard-0.9.44/lib/yard/handlers/ruby/legacy/visibility_handler.rb +18 -0
  128. data/yard-0.9.44/lib/yard/handlers/ruby/legacy/yield_handler.rb +29 -0
  129. data/yard-0.9.44/lib/yard/handlers/ruby/method_condition_handler.rb +9 -0
  130. data/yard-0.9.44/lib/yard/handlers/ruby/method_handler.rb +104 -0
  131. data/yard-0.9.44/lib/yard/handlers/ruby/mixin_handler.rb +61 -0
  132. data/yard-0.9.44/lib/yard/handlers/ruby/module_function_handler.rb +39 -0
  133. data/yard-0.9.44/lib/yard/handlers/ruby/module_handler.rb +12 -0
  134. data/yard-0.9.44/lib/yard/handlers/ruby/private_class_method_handler.rb +14 -0
  135. data/yard-0.9.44/lib/yard/handlers/ruby/private_constant_handler.rb +43 -0
  136. data/yard-0.9.44/lib/yard/handlers/ruby/public_class_method_handler.rb +14 -0
  137. data/yard-0.9.44/lib/yard/handlers/ruby/struct_handler_methods.rb +143 -0
  138. data/yard-0.9.44/lib/yard/handlers/ruby/visibility_handler.rb +35 -0
  139. data/yard-0.9.44/lib/yard/handlers/ruby/yield_handler.rb +31 -0
  140. data/yard-0.9.44/lib/yard/i18n/locale.rb +67 -0
  141. data/yard-0.9.44/lib/yard/i18n/message.rb +57 -0
  142. data/yard-0.9.44/lib/yard/i18n/messages.rb +56 -0
  143. data/yard-0.9.44/lib/yard/i18n/po_parser.rb +61 -0
  144. data/yard-0.9.44/lib/yard/i18n/pot_generator.rb +290 -0
  145. data/yard-0.9.44/lib/yard/i18n/text.rb +173 -0
  146. data/yard-0.9.44/lib/yard/logging.rb +260 -0
  147. data/yard-0.9.44/lib/yard/open_struct.rb +67 -0
  148. data/yard-0.9.44/lib/yard/options.rb +217 -0
  149. data/yard-0.9.44/lib/yard/parser/base.rb +57 -0
  150. data/yard-0.9.44/lib/yard/parser/c/c_parser.rb +235 -0
  151. data/yard-0.9.44/lib/yard/parser/c/comment_parser.rb +134 -0
  152. data/yard-0.9.44/lib/yard/parser/c/statement.rb +66 -0
  153. data/yard-0.9.44/lib/yard/parser/rbs/rbs_parser.rb +325 -0
  154. data/yard-0.9.44/lib/yard/parser/rbs/statement.rb +75 -0
  155. data/yard-0.9.44/lib/yard/parser/ruby/ast_node.rb +559 -0
  156. data/yard-0.9.44/lib/yard/parser/ruby/legacy/irb/slex.rb +294 -0
  157. data/yard-0.9.44/lib/yard/parser/ruby/legacy/ruby_lex.rb +1360 -0
  158. data/yard-0.9.44/lib/yard/parser/ruby/legacy/ruby_parser.rb +32 -0
  159. data/yard-0.9.44/lib/yard/parser/ruby/legacy/statement.rb +68 -0
  160. data/yard-0.9.44/lib/yard/parser/ruby/legacy/statement_list.rb +394 -0
  161. data/yard-0.9.44/lib/yard/parser/ruby/legacy/token_list.rb +74 -0
  162. data/yard-0.9.44/lib/yard/parser/ruby/ruby_parser.rb +792 -0
  163. data/yard-0.9.44/lib/yard/parser/ruby/token_resolver.rb +158 -0
  164. data/yard-0.9.44/lib/yard/parser/source_parser.rb +526 -0
  165. data/yard-0.9.44/lib/yard/rake/yardoc_task.rb +81 -0
  166. data/yard-0.9.44/lib/yard/registry.rb +439 -0
  167. data/yard-0.9.44/lib/yard/registry_resolver.rb +225 -0
  168. data/yard-0.9.44/lib/yard/registry_store.rb +342 -0
  169. data/yard-0.9.44/lib/yard/rubygems/backports/LICENSE.txt +57 -0
  170. data/yard-0.9.44/lib/yard/rubygems/backports/MIT.txt +20 -0
  171. data/yard-0.9.44/lib/yard/rubygems/backports/gem.rb +10 -0
  172. data/yard-0.9.44/lib/yard/rubygems/backports/source_index.rb +365 -0
  173. data/yard-0.9.44/lib/yard/rubygems/backports.rb +10 -0
  174. data/yard-0.9.44/lib/yard/rubygems/doc_manager.rb +90 -0
  175. data/yard-0.9.44/lib/yard/rubygems/hook.rb +197 -0
  176. data/yard-0.9.44/lib/yard/rubygems/specification.rb +50 -0
  177. data/yard-0.9.44/lib/yard/serializers/base.rb +83 -0
  178. data/yard-0.9.44/lib/yard/serializers/file_system_serializer.rb +123 -0
  179. data/yard-0.9.44/lib/yard/serializers/process_serializer.rb +24 -0
  180. data/yard-0.9.44/lib/yard/serializers/stdout_serializer.rb +34 -0
  181. data/yard-0.9.44/lib/yard/serializers/yardoc_serializer.rb +152 -0
  182. data/yard-0.9.44/lib/yard/server/adapter.rb +100 -0
  183. data/yard-0.9.44/lib/yard/server/commands/base.rb +203 -0
  184. data/yard-0.9.44/lib/yard/server/commands/display_file_command.rb +29 -0
  185. data/yard-0.9.44/lib/yard/server/commands/display_object_command.rb +65 -0
  186. data/yard-0.9.44/lib/yard/server/commands/frames_command.rb +16 -0
  187. data/yard-0.9.44/lib/yard/server/commands/library_command.rb +187 -0
  188. data/yard-0.9.44/lib/yard/server/commands/library_index_command.rb +28 -0
  189. data/yard-0.9.44/lib/yard/server/commands/list_command.rb +25 -0
  190. data/yard-0.9.44/lib/yard/server/commands/root_request_command.rb +15 -0
  191. data/yard-0.9.44/lib/yard/server/commands/search_command.rb +79 -0
  192. data/yard-0.9.44/lib/yard/server/commands/static_file_command.rb +23 -0
  193. data/yard-0.9.44/lib/yard/server/commands/static_file_helpers.rb +60 -0
  194. data/yard-0.9.44/lib/yard/server/doc_server_helper.rb +91 -0
  195. data/yard-0.9.44/lib/yard/server/doc_server_serializer.rb +39 -0
  196. data/yard-0.9.44/lib/yard/server/http_utils.rb +512 -0
  197. data/yard-0.9.44/lib/yard/server/library_version.rb +277 -0
  198. data/yard-0.9.44/lib/yard/server/rack_adapter.rb +97 -0
  199. data/yard-0.9.44/lib/yard/server/router.rb +187 -0
  200. data/yard-0.9.44/lib/yard/server/static_caching.rb +77 -0
  201. data/yard-0.9.44/lib/yard/server/templates/default/fulldoc/html/css/custom.css +207 -0
  202. data/yard-0.9.44/lib/yard/server/templates/default/fulldoc/html/images/processing.gif +0 -0
  203. data/yard-0.9.44/lib/yard/server/templates/default/fulldoc/html/js/autocomplete.js +203 -0
  204. data/yard-0.9.44/lib/yard/server/templates/default/layout/html/breadcrumb.erb +21 -0
  205. data/yard-0.9.44/lib/yard/server/templates/default/layout/html/script_setup.erb +7 -0
  206. data/yard-0.9.44/lib/yard/server/templates/default/layout/html/setup.rb +8 -0
  207. data/yard-0.9.44/lib/yard/server/templates/default/method_details/html/permalink.erb +6 -0
  208. data/yard-0.9.44/lib/yard/server/templates/default/method_details/html/setup.rb +5 -0
  209. data/yard-0.9.44/lib/yard/server/templates/doc_server/library_list/html/headers.erb +8 -0
  210. data/yard-0.9.44/lib/yard/server/templates/doc_server/library_list/html/library_list.erb +13 -0
  211. data/yard-0.9.44/lib/yard/server/templates/doc_server/library_list/html/listing.erb +13 -0
  212. data/yard-0.9.44/lib/yard/server/templates/doc_server/library_list/html/setup.rb +6 -0
  213. data/yard-0.9.44/lib/yard/server/templates/doc_server/library_list/html/title.erb +2 -0
  214. data/yard-0.9.44/lib/yard/server/templates/doc_server/processing/html/processing.erb +58 -0
  215. data/yard-0.9.44/lib/yard/server/templates/doc_server/processing/html/setup.rb +4 -0
  216. data/yard-0.9.44/lib/yard/server/templates/doc_server/search/html/search.erb +18 -0
  217. data/yard-0.9.44/lib/yard/server/templates/doc_server/search/html/setup.rb +9 -0
  218. data/yard-0.9.44/lib/yard/server/webrick_adapter.rb +45 -0
  219. data/yard-0.9.44/lib/yard/server.rb +13 -0
  220. data/yard-0.9.44/lib/yard/tags/default_factory.rb +192 -0
  221. data/yard-0.9.44/lib/yard/tags/default_tag.rb +13 -0
  222. data/yard-0.9.44/lib/yard/tags/directives.rb +631 -0
  223. data/yard-0.9.44/lib/yard/tags/library.rb +633 -0
  224. data/yard-0.9.44/lib/yard/tags/option_tag.rb +13 -0
  225. data/yard-0.9.44/lib/yard/tags/overload_tag.rb +72 -0
  226. data/yard-0.9.44/lib/yard/tags/ref_tag.rb +8 -0
  227. data/yard-0.9.44/lib/yard/tags/ref_tag_list.rb +28 -0
  228. data/yard-0.9.44/lib/yard/tags/tag.rb +72 -0
  229. data/yard-0.9.44/lib/yard/tags/tag_format_error.rb +7 -0
  230. data/yard-0.9.44/lib/yard/tags/types_explainer.rb +288 -0
  231. data/yard-0.9.44/lib/yard/templates/engine.rb +185 -0
  232. data/yard-0.9.44/lib/yard/templates/erb_cache.rb +23 -0
  233. data/yard-0.9.44/lib/yard/templates/helpers/base_helper.rb +215 -0
  234. data/yard-0.9.44/lib/yard/templates/helpers/filter_helper.rb +27 -0
  235. data/yard-0.9.44/lib/yard/templates/helpers/html_helper.rb +688 -0
  236. data/yard-0.9.44/lib/yard/templates/helpers/html_syntax_highlight_helper.rb +83 -0
  237. data/yard-0.9.44/lib/yard/templates/helpers/markup/html_entities.rb +2140 -0
  238. data/yard-0.9.44/lib/yard/templates/helpers/markup/hybrid_markdown.rb +2131 -0
  239. data/yard-0.9.44/lib/yard/templates/helpers/markup/rdoc_markdown.rb +23 -0
  240. data/yard-0.9.44/lib/yard/templates/helpers/markup/rdoc_markup.rb +112 -0
  241. data/yard-0.9.44/lib/yard/templates/helpers/markup_helper.rb +175 -0
  242. data/yard-0.9.44/lib/yard/templates/helpers/method_helper.rb +77 -0
  243. data/yard-0.9.44/lib/yard/templates/helpers/module_helper.rb +21 -0
  244. data/yard-0.9.44/lib/yard/templates/helpers/text_helper.rb +112 -0
  245. data/yard-0.9.44/lib/yard/templates/helpers/uml_helper.rb +47 -0
  246. data/yard-0.9.44/lib/yard/templates/section.rb +103 -0
  247. data/yard-0.9.44/lib/yard/templates/template.rb +420 -0
  248. data/yard-0.9.44/lib/yard/templates/template_options.rb +91 -0
  249. data/yard-0.9.44/lib/yard/verifier.rb +151 -0
  250. data/yard-0.9.44/lib/yard/version.rb +6 -0
  251. data/yard-0.9.44/lib/yard.rb +75 -0
  252. data/yard-0.9.44/po/ja.po +31108 -0
  253. data/yard-0.9.44/templates/default/class/dot/setup.rb +7 -0
  254. data/yard-0.9.44/templates/default/class/dot/superklass.erb +3 -0
  255. data/yard-0.9.44/templates/default/class/html/constructor_details.erb +8 -0
  256. data/yard-0.9.44/templates/default/class/html/setup.rb +2 -0
  257. data/yard-0.9.44/templates/default/class/html/subclasses.erb +4 -0
  258. data/yard-0.9.44/templates/default/class/setup.rb +36 -0
  259. data/yard-0.9.44/templates/default/class/text/setup.rb +12 -0
  260. data/yard-0.9.44/templates/default/class/text/subclasses.erb +5 -0
  261. data/yard-0.9.44/templates/default/constant/text/header.erb +11 -0
  262. data/yard-0.9.44/templates/default/constant/text/setup.rb +4 -0
  263. data/yard-0.9.44/templates/default/docstring/html/abstract.erb +4 -0
  264. data/yard-0.9.44/templates/default/docstring/html/deprecated.erb +1 -0
  265. data/yard-0.9.44/templates/default/docstring/html/index.erb +5 -0
  266. data/yard-0.9.44/templates/default/docstring/html/note.erb +6 -0
  267. data/yard-0.9.44/templates/default/docstring/html/private.erb +4 -0
  268. data/yard-0.9.44/templates/default/docstring/html/returns_void.erb +1 -0
  269. data/yard-0.9.44/templates/default/docstring/html/text.erb +1 -0
  270. data/yard-0.9.44/templates/default/docstring/html/todo.erb +6 -0
  271. data/yard-0.9.44/templates/default/docstring/setup.rb +52 -0
  272. data/yard-0.9.44/templates/default/docstring/text/abstract.erb +2 -0
  273. data/yard-0.9.44/templates/default/docstring/text/deprecated.erb +2 -0
  274. data/yard-0.9.44/templates/default/docstring/text/index.erb +2 -0
  275. data/yard-0.9.44/templates/default/docstring/text/note.erb +4 -0
  276. data/yard-0.9.44/templates/default/docstring/text/private.erb +2 -0
  277. data/yard-0.9.44/templates/default/docstring/text/returns_void.erb +1 -0
  278. data/yard-0.9.44/templates/default/docstring/text/text.erb +1 -0
  279. data/yard-0.9.44/templates/default/docstring/text/todo.erb +4 -0
  280. data/yard-0.9.44/templates/default/fulldoc/html/css/common.css +1 -0
  281. data/yard-0.9.44/templates/default/fulldoc/html/css/full_list.css +206 -0
  282. data/yard-0.9.44/templates/default/fulldoc/html/css/style.css +1089 -0
  283. data/yard-0.9.44/templates/default/fulldoc/html/frames.erb +22 -0
  284. data/yard-0.9.44/templates/default/fulldoc/html/full_list.erb +40 -0
  285. data/yard-0.9.44/templates/default/fulldoc/html/full_list_class.erb +2 -0
  286. data/yard-0.9.44/templates/default/fulldoc/html/full_list_file.erb +7 -0
  287. data/yard-0.9.44/templates/default/fulldoc/html/full_list_method.erb +10 -0
  288. data/yard-0.9.44/templates/default/fulldoc/html/js/app.js +801 -0
  289. data/yard-0.9.44/templates/default/fulldoc/html/js/full_list.js +334 -0
  290. data/yard-0.9.44/templates/default/fulldoc/html/js/jquery.js +4 -0
  291. data/yard-0.9.44/templates/default/fulldoc/html/setup.rb +249 -0
  292. data/yard-0.9.44/templates/default/layout/dot/header.erb +6 -0
  293. data/yard-0.9.44/templates/default/layout/dot/setup.rb +15 -0
  294. data/yard-0.9.44/templates/default/layout/html/breadcrumb.erb +11 -0
  295. data/yard-0.9.44/templates/default/layout/html/files.erb +11 -0
  296. data/yard-0.9.44/templates/default/layout/html/footer.erb +5 -0
  297. data/yard-0.9.44/templates/default/layout/html/headers.erb +15 -0
  298. data/yard-0.9.44/templates/default/layout/html/index.erb +2 -0
  299. data/yard-0.9.44/templates/default/layout/html/layout.erb +26 -0
  300. data/yard-0.9.44/templates/default/layout/html/listing.erb +4 -0
  301. data/yard-0.9.44/templates/default/layout/html/objects.erb +32 -0
  302. data/yard-0.9.44/templates/default/layout/html/script_setup.erb +4 -0
  303. data/yard-0.9.44/templates/default/layout/html/search.erb +13 -0
  304. data/yard-0.9.44/templates/default/layout/html/setup.rb +89 -0
  305. data/yard-0.9.44/templates/default/method/html/header.erb +17 -0
  306. data/yard-0.9.44/templates/default/method/setup.rb +4 -0
  307. data/yard-0.9.44/templates/default/method/text/header.erb +1 -0
  308. data/yard-0.9.44/templates/default/method_details/html/header.erb +3 -0
  309. data/yard-0.9.44/templates/default/method_details/html/method_signature.erb +25 -0
  310. data/yard-0.9.44/templates/default/method_details/html/source.erb +10 -0
  311. data/yard-0.9.44/templates/default/method_details/setup.rb +11 -0
  312. data/yard-0.9.44/templates/default/method_details/text/header.erb +10 -0
  313. data/yard-0.9.44/templates/default/method_details/text/method_signature.erb +12 -0
  314. data/yard-0.9.44/templates/default/method_details/text/setup.rb +11 -0
  315. data/yard-0.9.44/templates/default/module/dot/child.erb +1 -0
  316. data/yard-0.9.44/templates/default/module/dot/dependencies.erb +3 -0
  317. data/yard-0.9.44/templates/default/module/dot/header.erb +6 -0
  318. data/yard-0.9.44/templates/default/module/dot/info.erb +14 -0
  319. data/yard-0.9.44/templates/default/module/dot/setup.rb +15 -0
  320. data/yard-0.9.44/templates/default/module/html/attribute_details.erb +10 -0
  321. data/yard-0.9.44/templates/default/module/html/attribute_summary.erb +8 -0
  322. data/yard-0.9.44/templates/default/module/html/box_info.erb +43 -0
  323. data/yard-0.9.44/templates/default/module/html/children.erb +8 -0
  324. data/yard-0.9.44/templates/default/module/html/constant_summary.erb +17 -0
  325. data/yard-0.9.44/templates/default/module/html/defines.erb +3 -0
  326. data/yard-0.9.44/templates/default/module/html/header.erb +5 -0
  327. data/yard-0.9.44/templates/default/module/html/inherited_attributes.erb +14 -0
  328. data/yard-0.9.44/templates/default/module/html/inherited_constants.erb +8 -0
  329. data/yard-0.9.44/templates/default/module/html/inherited_methods.erb +20 -0
  330. data/yard-0.9.44/templates/default/module/html/item_summary.erb +40 -0
  331. data/yard-0.9.44/templates/default/module/html/method_details_list.erb +9 -0
  332. data/yard-0.9.44/templates/default/module/html/method_summary.erb +22 -0
  333. data/yard-0.9.44/templates/default/module/html/methodmissing.erb +12 -0
  334. data/yard-0.9.44/templates/default/module/html/pre_docstring.erb +1 -0
  335. data/yard-0.9.44/templates/default/module/setup.rb +187 -0
  336. data/yard-0.9.44/templates/default/module/text/children.erb +10 -0
  337. data/yard-0.9.44/templates/default/module/text/class_meths_list.erb +8 -0
  338. data/yard-0.9.44/templates/default/module/text/extends.erb +8 -0
  339. data/yard-0.9.44/templates/default/module/text/header.erb +7 -0
  340. data/yard-0.9.44/templates/default/module/text/includes.erb +8 -0
  341. data/yard-0.9.44/templates/default/module/text/instance_meths_list.erb +8 -0
  342. data/yard-0.9.44/templates/default/module/text/setup.rb +13 -0
  343. data/yard-0.9.44/templates/default/onefile/html/files.erb +5 -0
  344. data/yard-0.9.44/templates/default/onefile/html/headers.erb +8 -0
  345. data/yard-0.9.44/templates/default/onefile/html/layout.erb +16 -0
  346. data/yard-0.9.44/templates/default/onefile/html/readme.erb +3 -0
  347. data/yard-0.9.44/templates/default/onefile/html/setup.rb +62 -0
  348. data/yard-0.9.44/templates/default/root/dot/child.erb +3 -0
  349. data/yard-0.9.44/templates/default/root/dot/setup.rb +6 -0
  350. data/yard-0.9.44/templates/default/root/html/setup.rb +2 -0
  351. data/yard-0.9.44/templates/default/tags/html/example.erb +11 -0
  352. data/yard-0.9.44/templates/default/tags/html/index.erb +3 -0
  353. data/yard-0.9.44/templates/default/tags/html/option.erb +24 -0
  354. data/yard-0.9.44/templates/default/tags/html/overload.erb +14 -0
  355. data/yard-0.9.44/templates/default/tags/html/see.erb +8 -0
  356. data/yard-0.9.44/templates/default/tags/html/tag.erb +20 -0
  357. data/yard-0.9.44/templates/default/tags/setup.rb +57 -0
  358. data/yard-0.9.44/templates/default/tags/text/example.erb +12 -0
  359. data/yard-0.9.44/templates/default/tags/text/index.erb +1 -0
  360. data/yard-0.9.44/templates/default/tags/text/option.erb +20 -0
  361. data/yard-0.9.44/templates/default/tags/text/overload.erb +19 -0
  362. data/yard-0.9.44/templates/default/tags/text/see.erb +11 -0
  363. data/yard-0.9.44/templates/default/tags/text/tag.erb +13 -0
  364. data/yard-0.9.44/templates/guide/class/html/setup.rb +2 -0
  365. data/yard-0.9.44/templates/guide/docstring/html/setup.rb +2 -0
  366. data/yard-0.9.44/templates/guide/fulldoc/html/css/style.css +358 -0
  367. data/yard-0.9.44/templates/guide/fulldoc/html/js/app.js +61 -0
  368. data/yard-0.9.44/templates/guide/fulldoc/html/setup.rb +74 -0
  369. data/yard-0.9.44/templates/guide/layout/html/layout.erb +78 -0
  370. data/yard-0.9.44/templates/guide/layout/html/setup.rb +25 -0
  371. data/yard-0.9.44/templates/guide/method/html/header.erb +18 -0
  372. data/yard-0.9.44/templates/guide/method/html/setup.rb +22 -0
  373. data/yard-0.9.44/templates/guide/module/html/header.erb +7 -0
  374. data/yard-0.9.44/templates/guide/module/html/method_list.erb +5 -0
  375. data/yard-0.9.44/templates/guide/module/html/setup.rb +27 -0
  376. data/yard-0.9.44/templates/guide/onefile/html/files.erb +4 -0
  377. data/yard-0.9.44/templates/guide/onefile/html/setup.rb +6 -0
  378. data/yard-0.9.44/templates/guide/onefile/html/toc.erb +3 -0
  379. data/yard-0.9.44/templates/guide/tags/html/setup.rb +9 -0
  380. metadata +419 -0
@@ -0,0 +1,2131 @@
1
+ # frozen_string_literal: true
2
+ if RUBY_VERSION < '3.5'
3
+ require 'cgi/util'
4
+ else
5
+ require 'cgi/escape'
6
+ end
7
+
8
+ module YARD
9
+ module Templates
10
+ module Helpers
11
+ module Markup
12
+ # A built-in formatter that implements a practical subset of GitHub
13
+ # flavored Markdown plus common RDoc markup forms.
14
+ class HybridMarkdown
15
+ attr_accessor :from_path
16
+
17
+ ATX_HEADING_RE = /^\s{0,3}#{Regexp.escape('#')}{1,6}(?=[ \t]|$)/.freeze
18
+ RDOC_HEADING_RE = /^\s*(=+)[ \t]+(.+?)\s*$/.freeze
19
+ SETEXT_HEADING_RE = /^\s{0,3}(=+|-+)\s*$/.freeze
20
+ FENCE_RE = /^(\s{0,3})(`{3,}|~{3,})([^\n]*)$/.freeze
21
+ THEMATIC_BREAK_RE = /^\s{0,3}(?:(?:-\s*){3,}|(?:\*\s*){3,}|(?:_\s*){3,})\s*$/.freeze
22
+ TABLE_SEPARATOR_RE = /^\s*\|?(?:\s*:?-+:?\s*\|)+(?:\s*:?-+:?\s*)\|?\s*$/.freeze
23
+ UNORDERED_LIST_RE = /^\s{0,3}([*+-])[ \t]+(.+?)\s*$/.freeze
24
+ ORDERED_LIST_RE = /^\s{0,3}(\d+)([.)])[ \t]+(.+?)\s*$/.freeze
25
+ RDOC_ORDERED_LIST_RE = /^\s{0,3}([A-Za-z])\.[ \t]+(.+?)\s*$/.freeze
26
+ LABEL_LIST_BRACKET_RE = /^\s*\[([^\]]+)\](?:[ \t]+(.+))?\s*$/.freeze
27
+ LABEL_LIST_COLON_RE = /^\s*([^\s:][^:]*)::(?:[ \t]+(.*))?\s*$/.freeze
28
+ BLOCKQUOTE_RE = /^\s{0,3}>\s?(.*)$/.freeze
29
+ HTML_BLOCK_RE = %r{
30
+ ^\s*(?:
31
+ <!--|
32
+ <\?|
33
+ <![A-Z]|
34
+ <!\[CDATA\[|
35
+ </?(?:address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul)\b|
36
+ <(?:script|pre|style|textarea)\b|
37
+ </(?:script|pre|style|textarea)\b|
38
+ </?[A-Za-z][A-Za-z0-9-]*(?:\s+[A-Za-z_:][\w:.-]*(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'=<>`]+))?)*\s*/?>\s*$
39
+ )
40
+ }mx.freeze
41
+ HTML_BLOCK_TAGS = %w[
42
+ address article aside base basefont blockquote body caption center col
43
+ colgroup dd details dialog dir div dl dt fieldset figcaption figure
44
+ footer form frame frameset h1 h2 h3 h4 h5 h6 head header hr html iframe
45
+ legend li link main menu menuitem nav noframes ol optgroup option p param
46
+ search section summary table tbody td tfoot th thead title tr track ul
47
+ ].freeze
48
+ HTML_TAG_RE = %r{
49
+ <!--(?:>|->)|
50
+ <!--(?:.*?)-->|
51
+ <\?.*?\?>|
52
+ <![A-Z][^>]*>|
53
+ <!\[CDATA\[.*?\]\]>|
54
+ </[A-Za-z][A-Za-z0-9-]*\s*>|
55
+ <[A-Za-z][A-Za-z0-9-]*
56
+ (?:\s+[A-Za-z_:][\w:.-]*
57
+ (?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'=<>`]+))?
58
+ )*
59
+ \s*/?>
60
+ }mx.freeze
61
+ ENTITY_RE = /&(?:[A-Za-z][A-Za-z0-9]+|#\d+|#[xX][0-9A-Fa-f]+);/.freeze
62
+ YARD_LINK_RE = /(?<!\\)\{(?!\})(\S+?)(?:\s([^\}]*?\S))?\}(?=\W|.+<\/|$)/m.freeze
63
+ CODE_LANG_RE = /\A(?:[ \t]*\n)?[ \t]*!!!([\w.+-]+)[ \t]*\n/.freeze
64
+ REFERENCE_DEF_START_RE = /^\s{0,3}\[([^\]]+)\]:\s*(.*)$/.freeze
65
+ PLACEHOLDER_RE = /\0(\d+)\0/.freeze
66
+ ESCAPABLE_CHARS_RE = /\\([!"#$%&'()*+,\-.\/:;<=>?@\[\\\]^_`{|}~])/.freeze
67
+ RDOC_ESCAPED_CAPITALIZED_CROSSREF_RE = /\\((?:::)?(?:[A-Z]\w+|[A-Z]\w*::\w+)(?:::\w+)*)/.freeze
68
+ AUTOLINK_RE = /<([A-Za-z][A-Za-z0-9.+-]{1,31}:[^<>\s]*|[A-Za-z0-9.!#$%&'*+\/=?^_`{|}~-]+@[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?(?:\.[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?)+)>/.freeze
69
+ TAB_WIDTH = 4
70
+
71
+ # @param text [String] the Markdown text to format.
72
+ # @param options [Hash] options for the formatter.
73
+ # @option options [Boolean] :heading_ids whether to generate id attributes for headings.
74
+ def initialize(text, options = {})
75
+ @heading_ids = options.fetch(:heading_ids, true)
76
+ @references = {}
77
+ @text = extract_reference_definitions(text.to_s.gsub(/\r\n?/, "\n"))
78
+ end
79
+
80
+ # @return [String] the formatted HTML.
81
+ def to_html
82
+ parse_blocks(split_lines(@text), 0).join("\n")
83
+ end
84
+
85
+ private
86
+
87
+ def parse_blocks(lines, index)
88
+ blocks = []
89
+ previous_block_type = nil
90
+
91
+ while index < lines.length
92
+ line = lines[index]
93
+
94
+ if blank_line?(line)
95
+ index += 1
96
+ elsif yard_indented_code_start?(lines, index)
97
+ block, index = parse_yard_indented_code(lines, index)
98
+ blocks << block
99
+ previous_block_type = :code
100
+ elsif indented_code_block_start?(lines, index, previous_block_type)
101
+ block, index = parse_indented_code(lines, index)
102
+ blocks << block
103
+ previous_block_type = :code
104
+ elsif thematic_break?(line)
105
+ blocks << '<hr />'
106
+ index += 1
107
+ previous_block_type = :hr
108
+ elsif (heading = parse_setext_heading(lines, index))
109
+ blocks << heading[0]
110
+ index = heading[1]
111
+ previous_block_type = :heading
112
+ elsif (heading = parse_heading(line))
113
+ blocks << heading
114
+ index += 1
115
+ previous_block_type = :heading
116
+ elsif fenced_code_start?(line)
117
+ block, index = parse_fenced_code(lines, index)
118
+ blocks << block
119
+ previous_block_type = :code
120
+ elsif table_start?(lines, index)
121
+ block, index = parse_table(lines, index)
122
+ blocks << block
123
+ previous_block_type = :table
124
+ elsif labeled_list_start?(lines, index)
125
+ block, index = parse_labeled_list(lines, index)
126
+ blocks << block
127
+ previous_block_type = :list
128
+ elsif blockquote_start?(line)
129
+ block, index = parse_blockquote(lines, index)
130
+ blocks << block
131
+ previous_block_type = :blockquote
132
+ elsif list_start?(line)
133
+ block, index = parse_list(lines, index)
134
+ blocks << block
135
+ previous_block_type = :list
136
+ elsif html_block_start?(line)
137
+ block, index = parse_html_block(lines, index)
138
+ blocks << block
139
+ previous_block_type = :html
140
+ else
141
+ block, index = parse_paragraph(lines, index)
142
+ blocks << block unless block.empty?
143
+ previous_block_type = :paragraph unless block.empty?
144
+ end
145
+ end
146
+
147
+ blocks
148
+ end
149
+
150
+ def parse_heading(line)
151
+ if (heading = parse_atx_heading(line))
152
+ return heading
153
+ end
154
+
155
+ match = RDOC_HEADING_RE.match(line)
156
+ return unless match
157
+
158
+ heading_marks = match[1]
159
+ heading_text = match[2].strip
160
+ return nil if heading_text =~ /\A[=\-]+\z/
161
+
162
+ level = [heading_marks.length, 6].min
163
+ "<h#{level}#{heading_id(heading_text)}>#{format_inline(heading_text)}</h#{level}>"
164
+ end
165
+
166
+ def parse_setext_heading(lines, index)
167
+ return nil if index + 1 >= lines.length
168
+ return nil if lines[index].strip.empty?
169
+ return nil if lines[index] =~ /^\s{0,3}>/
170
+ return nil if parse_list_marker(lines[index])
171
+ return nil if lines[index] =~ /^(?: {4,}|\t)/
172
+ return nil if parse_heading(lines[index])
173
+ return nil if fenced_code_start?(lines[index])
174
+
175
+ content_lines = []
176
+ current_index = index
177
+
178
+ while current_index < lines.length
179
+ line = lines[current_index]
180
+ return nil if blank_line?(line)
181
+
182
+ if line =~ SETEXT_HEADING_RE
183
+ return nil if content_lines.empty?
184
+
185
+ level = $1.start_with?('=') ? 1 : 2
186
+ text = content_lines.join("\n")
187
+ return ["<h#{level}#{heading_id(text)}>#{format_inline(text)}</h#{level}>", current_index + 1]
188
+ end
189
+
190
+ if current_index > index && block_boundary?(line)
191
+ return nil
192
+ end
193
+
194
+ content_lines << normalize_heading_line(line)
195
+ current_index += 1
196
+ end
197
+
198
+ nil
199
+ end
200
+
201
+ def parse_fenced_code(lines, index)
202
+ opener = parse_fence_opener(lines[index])
203
+ fence_char = opener[:char]
204
+ fence_length = opener[:length]
205
+ indent = opener[:indent]
206
+ lang = opener[:lang]
207
+ index += 1
208
+ body = []
209
+
210
+ while index < lines.length
211
+ break if fence_closer?(lines[index], fence_char, fence_length)
212
+
213
+ body << strip_fenced_indent(lines[index], indent)
214
+ index += 1
215
+ end
216
+
217
+ index += 1 if index < lines.length
218
+ [code_block(body.join, lang), index]
219
+ end
220
+
221
+ def parse_indented_code(lines, index)
222
+ body = []
223
+ previous_blank = false
224
+
225
+ while index < lines.length
226
+ line = lines[index]
227
+ break if previous_blank && html_block_start?(line)
228
+ break unless blank_line?(line) || indented_code_start?(line)
229
+ body << line
230
+ previous_blank = blank_line?(line)
231
+ index += 1
232
+ end
233
+
234
+ body.pop while body.any? && blank_line?(body.last)
235
+ [code_block(unindent_indented_code(body)), index]
236
+ end
237
+
238
+ def parse_yard_indented_code(lines, index)
239
+ body = []
240
+
241
+ while index < lines.length
242
+ line = lines[index]
243
+ break unless blank_line?(line) || indented_code_start?(line)
244
+ body << line
245
+ index += 1
246
+ end
247
+
248
+ body.pop while body.any? && blank_line?(body.last)
249
+ [code_block(unindent(body)), index]
250
+ end
251
+
252
+ def parse_table(lines, index)
253
+ header = split_table_row(lines[index])
254
+ alignments = split_table_row(lines[index + 1]).map { |cell| table_alignment(cell) }
255
+ rows = []
256
+ index += 2
257
+
258
+ while index < lines.length && table_row?(lines[index])
259
+ rows << split_table_row(lines[index])
260
+ index += 1
261
+ end
262
+
263
+ html = "<table>\n<thead>\n<tr>\n".dup
264
+ header.each_with_index do |cell, i|
265
+ attrs = alignments[i] ? %( align="#{alignments[i]}") : ""
266
+ html << "<th#{attrs}>#{format_inline(cell)}</th>\n"
267
+ end
268
+ html << "</tr>\n</thead>\n<tbody>\n"
269
+ rows.each do |row|
270
+ html << "<tr>\n"
271
+ row.each_with_index do |cell, i|
272
+ attrs = alignments[i] ? %( align="#{alignments[i]}") : ""
273
+ html << "<td#{attrs}>#{format_inline(cell)}</td>\n"
274
+ end
275
+ html << "</tr>\n"
276
+ end
277
+ html << "</tbody>\n</table>"
278
+ [html, index]
279
+ end
280
+
281
+ def parse_list(lines, index)
282
+ marker = parse_list_marker(lines[index])
283
+ ordered = marker[:ordered]
284
+ tag = ordered ? 'ol' : 'ul'
285
+ start_attr = ordered && marker[:start] != 1 ? %( start="#{marker[:start]}") : ''
286
+ items = []
287
+ tight = true
288
+ list_indent = marker[:indent]
289
+
290
+ while index < lines.length
291
+ break if items.any? && thematic_break?(lines[index]) && leading_columns(lines[index]) <= list_indent + 3
292
+
293
+ item_marker = parse_list_marker(lines[index])
294
+ break unless item_marker && same_list_type?(marker, item_marker)
295
+
296
+ effective_padding = list_item_padding(item_marker)
297
+ content_indent = item_marker[:indent] + item_marker[:marker_length] + effective_padding
298
+ lazy_indent = item_marker[:indent] + effective_padding
299
+ item_lines = []
300
+ first_line = item_marker[:content]
301
+ unless first_line.empty?
302
+ leading = [item_marker[:padding] - effective_padding, 0].max
303
+ item_lines << "#{' ' * leading}#{first_line}\n"
304
+ end
305
+ index += 1
306
+ blank_seen = false
307
+ item_loose = false
308
+
309
+ while index < lines.length
310
+ line = lines[index]
311
+ break if thematic_break?(line) && !indented_to?(line, content_indent)
312
+ break if setext_underline_line?(line) && !indented_to?(line, content_indent)
313
+
314
+ next_marker = parse_list_marker(line)
315
+ if next_marker && same_list_type?(marker, next_marker) &&
316
+ (next_marker[:indent] == item_marker[:indent] || (blank_seen && next_marker[:indent] <= list_indent + 3))
317
+ if blank_seen
318
+ tight = false
319
+ end
320
+ break
321
+ end
322
+ break if next_marker && next_marker[:indent] < content_indent
323
+ break if !blank_seen && !indented_to?(line, content_indent) && block_boundary?(line)
324
+
325
+ if blank_line?(line)
326
+ item_lines << "\n"
327
+ blank_seen = true
328
+ elsif blank_seen && indented_to?(line, content_indent)
329
+ break if first_line.empty? && item_lines.all? { |item_line| item_line == "\n" } &&
330
+ leading_columns(line) == content_indent
331
+ item_loose = true if loose_list_item_continuation?(item_lines)
332
+ stripped = strip_list_item_indent(line, content_indent)
333
+ item_lines << stripped
334
+ blank_seen = false
335
+ elsif !blank_seen && indented_to?(line, content_indent)
336
+ stripped = strip_list_item_indent(line, content_indent)
337
+ item_lines << stripped
338
+ blank_seen = false
339
+ elsif !blank_seen
340
+ stripped = strip_list_item_indent(line, lazy_indent)
341
+ stripped = escape_list_marker_text(stripped) if parse_list_marker(stripped)
342
+ item_lines << stripped
343
+ blank_seen = false
344
+ else
345
+ break
346
+ end
347
+
348
+ index += 1
349
+ end
350
+
351
+ item_blocks = parse_blocks(item_lines, 0)
352
+ item_html = item_blocks.join("\n")
353
+ item_html = format_inline(first_line) if item_html.empty? && !first_line.empty?
354
+
355
+ simple_item = !item_loose &&
356
+ item_blocks.length == 1 &&
357
+ item_html =~ /\A<p>(.*?)<\/p>\z/m &&
358
+ item_html !~ /<(?:pre|blockquote|ul|ol|dl|table|h\d|hr)/m
359
+
360
+ if item_html.empty?
361
+ item_html = ''
362
+ else
363
+ item_loose ||= item_blocks.count { |block| block.start_with?('<p>') } > 1
364
+ end
365
+
366
+ tight &&= !item_loose
367
+ items << {:html => item_html, :simple => simple_item}
368
+ end
369
+
370
+ items.map! do |item|
371
+ item_html = item[:html]
372
+ item_html = "<p>#{item_html}</p>" if !tight && !item_html.empty? && item_html !~ /\A</m
373
+ item_html = item_html.sub(/\A<p>(.*?)<\/p>(?=\n<(?:ul|ol|blockquote|pre|h\d|table|hr))/m, '\1') if tight
374
+ item_html = item_html.sub(/\n<p>(.*?)<\/p>\z/m, "\n\\1") if tight
375
+ item_html = item_html.sub(/\A<p>(.*?)<\/p>\z/m, '\1') if item[:simple] && tight
376
+
377
+ if item_html.empty?
378
+ '<li></li>'
379
+ elsif item[:simple] && tight
380
+ "<li>#{item_html}</li>"
381
+ elsif item_html !~ /\A</m
382
+ suffix = item_html.include?("\n") ? "\n" : ''
383
+ "<li>#{item_html}#{suffix}</li>"
384
+ else
385
+ suffix = item_html =~ /(?:<\/(?:p|pre|blockquote|ul|ol|dl|table|h\d)>|<hr \/>|<[A-Za-z][A-Za-z0-9-]*>)\z/m ? "\n" : ''
386
+ "<li>\n#{item_html}#{suffix}</li>"
387
+ end
388
+ end
389
+
390
+ ["<#{tag}#{start_attr}>\n#{items.join("\n")}\n</#{tag}>", index]
391
+ end
392
+
393
+ def parse_labeled_list(lines, index)
394
+ items = []
395
+
396
+ while index < lines.length
397
+ label, body = parse_labeled_list_line(lines[index])
398
+ break unless label
399
+
400
+ index += 1
401
+ body_lines = []
402
+ body_lines << body if body && !body.empty?
403
+
404
+ while index < lines.length
405
+ line = lines[index]
406
+ break if blank_line?(line)
407
+ break if parse_labeled_list_line(line)
408
+ break if !line.strip.empty? && !line.match(/^(?: {2,}|\t)/)
409
+
410
+ body_lines << line.sub(/^(?: {2,}|\t)/, '').chomp
411
+ index += 1
412
+ end
413
+
414
+ body_html =
415
+ if body_lines.empty?
416
+ ''
417
+ else
418
+ parse_blocks(body_lines.map { |l| "#{l}\n" }, 0).join("\n")
419
+ end
420
+
421
+ items << "<dt>#{format_inline(label)}</dt>\n<dd>#{body_html}</dd>"
422
+ index += 1 while index < lines.length && blank_line?(lines[index])
423
+ end
424
+
425
+ ["<dl>\n#{items.join("\n")}\n</dl>", index]
426
+ end
427
+
428
+ def parse_blockquote(lines, index)
429
+ quoted_lines = []
430
+ saw_quote = false
431
+ previous_blank = false
432
+
433
+ while index < lines.length
434
+ line = lines[index]
435
+ break if saw_quote && quoted_lines.last == "\n" && !blockquote_start?(line)
436
+ break if saw_quote && blank_line?(line) && blockquote_open_fence?(quoted_lines)
437
+ break if saw_quote && previous_blank
438
+ break if saw_quote && !blank_line?(line) && !blockquote_start?(line) &&
439
+ !lazy_blockquote_continuation?(quoted_lines, line)
440
+ break unless blank_line?(line) || blockquote_start?(line) || saw_quote
441
+
442
+ if blank_line?(line)
443
+ quoted_lines << "\n"
444
+ previous_blank = true
445
+ elsif (stripped = strip_blockquote_marker(line))
446
+ quoted_lines << stripped
447
+ saw_quote = true
448
+ previous_blank = false
449
+ else
450
+ if setext_underline_line?(line)
451
+ quoted_lines << " #{line.lstrip}"
452
+ else
453
+ quoted_lines << line
454
+ end
455
+ previous_blank = false
456
+ end
457
+ index += 1
458
+ end
459
+
460
+ inner_html = parse_blocks(quoted_lines, 0).join("\n")
461
+ [inner_html.empty? ? "<blockquote>\n</blockquote>" : "<blockquote>\n#{inner_html}\n</blockquote>", index]
462
+ end
463
+
464
+ def parse_html_block(lines, index)
465
+ html = []
466
+ type = html_block_type(lines[index])
467
+ return ['', index] unless type
468
+
469
+ while index < lines.length
470
+ line = lines[index]
471
+ break if html.any? && [6, 7].include?(type) && html_block_end?(type, line)
472
+ break unless html.any? || html_block_type(line)
473
+
474
+ html << line.chomp
475
+ if html_block_end?(type, line)
476
+ index += 1
477
+ break
478
+ end
479
+ index += 1
480
+ end
481
+
482
+ [html.join("\n"), index]
483
+ end
484
+
485
+ def parse_paragraph(lines, index)
486
+ buffer = []
487
+
488
+ while index < lines.length
489
+ line = lines[index]
490
+ break if blank_line?(line)
491
+ break if !buffer.empty? && colon_indented_code_block_start?(lines, index)
492
+ break if thematic_break?(line)
493
+ break if parse_setext_heading(lines, index)
494
+ break if parse_heading(line)
495
+ break if fenced_code_start?(line)
496
+ break if table_start?(lines, index)
497
+ break if labeled_list_start?(lines, index)
498
+ break if blockquote_start?(line)
499
+ break if list_start?(line, true)
500
+ break if html_block_start?(line, true)
501
+
502
+ buffer << line.chomp
503
+ index += 1
504
+ end
505
+
506
+ text = buffer.map { |line| normalize_paragraph_line(line) }.join("\n").strip
507
+ [text.empty? ? '' : "<p>#{format_inline(text)}</p>", index]
508
+ end
509
+
510
+ def format_inline(text)
511
+ placeholders = []
512
+ text = protect_yard_links(text, placeholders)
513
+ text = protect_raw_html(text, placeholders)
514
+ text = protect_code_spans(text, placeholders)
515
+ text = protect_autolinks(text, placeholders)
516
+ text = protect_hard_breaks(text, placeholders)
517
+ text = protect_rdoc_images(text, placeholders)
518
+ text = protect_inline_images(text, placeholders)
519
+ text = protect_inline_links(text, placeholders)
520
+ text = protect_braced_text_links(text, placeholders)
521
+ text = protect_single_word_text_links(text, placeholders)
522
+ text = protect_reference_images(text, placeholders)
523
+ text = protect_reference_links(text, placeholders)
524
+ text = protect_escaped_characters(text, placeholders)
525
+ text = protect_entities(text, placeholders)
526
+ text = text.gsub(/[ \t]+\n/, "\n")
527
+ text = h(text)
528
+ text = format_emphasis(text)
529
+ text = format_strikethrough(text)
530
+ restore_placeholders(autolink_urls(text), placeholders)
531
+ end
532
+
533
+ def protect_code_spans(text, placeholders)
534
+ output = String.new
535
+ index = 0
536
+
537
+ while index < text.length
538
+ if text[index, 1] == '`' && (index.zero? || text[index - 1, 1] != '\\') && !inside_angle_autolink_candidate?(text, index)
539
+ opener_length = 1
540
+ opener_length += 1 while index + opener_length < text.length && text[index + opener_length, 1] == '`'
541
+ closer_index = find_matching_backtick_run(text, index + opener_length, opener_length)
542
+ if closer_index
543
+ code = normalize_code_span(restore_placeholders(text[(index + opener_length)...closer_index], placeholders))
544
+ output << store_placeholder(placeholders, "<code>#{h(code)}</code>")
545
+ index = closer_index + opener_length
546
+ next
547
+ end
548
+
549
+ output << ('`' * opener_length)
550
+ index += opener_length
551
+ next
552
+ end
553
+
554
+ output << text[index, 1]
555
+ index += 1
556
+ end
557
+
558
+ output.gsub(/(^|[\s>])\+([^\s+\n](?:[^+\n]*?[^\s+\n])?)\+(?=$|[\s<.,;:!?)]|\z)/) do
559
+ prefix = $1
560
+ prefix + store_placeholder(placeholders, "<code>#{h(restore_placeholders($2, placeholders))}</code>")
561
+ end
562
+ end
563
+
564
+ def inside_angle_autolink_candidate?(text, index)
565
+ opening = text.rindex('<', index)
566
+ return false unless opening
567
+
568
+ closing = text.rindex('>', index)
569
+ return false if closing && closing > opening
570
+
571
+ candidate = text[opening...index]
572
+ return false if candidate =~ /\s/
573
+
574
+ candidate =~ /\A<(?:[A-Za-z][A-Za-z0-9.+-]{1,31}:|[A-Za-z0-9.!#$%&'*+\/=?^_`{|}~-]+@)/
575
+ end
576
+
577
+ def protect_yard_links(text, placeholders)
578
+ text.gsub(YARD_LINK_RE) do
579
+ match = Regexp.last_match
580
+ if text[match.end(0), 1] == '['
581
+ match[0]
582
+ else
583
+ store_placeholder(placeholders, match[0])
584
+ end
585
+ end
586
+ end
587
+
588
+ def protect_autolinks(text, placeholders)
589
+ text.gsub(AUTOLINK_RE) do
590
+ href = $1
591
+ link_href = href.include?('@') && href !~ /\A[A-Za-z][A-Za-z0-9.+-]{1,31}:/ ? "mailto:#{href}" : escape_autolink_url(href)
592
+ store_placeholder(placeholders, %(<a href="#{h(link_href)}">#{h(href)}</a>))
593
+ end
594
+ end
595
+
596
+ def protect_raw_html(text, placeholders)
597
+ text.gsub(/(?<!\\)#{HTML_TAG_RE}/m) do
598
+ match = $&
599
+ match_start = Regexp.last_match.begin(0)
600
+ if match_start > 0 && text[match_start - 1, 1] == '`'
601
+ match
602
+ else
603
+ store_placeholder(placeholders, match)
604
+ end
605
+ end
606
+ end
607
+
608
+ def protect_escaped_characters(text, placeholders)
609
+ text = text.gsub(RDOC_ESCAPED_CAPITALIZED_CROSSREF_RE) do
610
+ store_placeholder(placeholders, h($1))
611
+ end
612
+
613
+ text.gsub(ESCAPABLE_CHARS_RE) { store_placeholder(placeholders, h($1)) }
614
+ end
615
+
616
+ def protect_entities(text, placeholders)
617
+ text.gsub(ENTITY_RE) { store_placeholder(placeholders, h(decode_entity($&))) }
618
+ end
619
+
620
+ def protect_hard_breaks(text, placeholders)
621
+ text.gsub(/(?:\\|\s{2,})\n/) { store_placeholder(placeholders, "<br />\n") }
622
+ end
623
+
624
+ def protect_rdoc_images(text, placeholders)
625
+ text.gsub(/(^|[ \t\n])rdoc-image:([A-Za-z][A-Za-z0-9+.-]*:\/\/\S+)(?=$|[ \t\n])/) do
626
+ prefix = Regexp.last_match(1)
627
+ dest = Regexp.last_match(2)
628
+ prefix + store_placeholder(placeholders, image_html('', dest))
629
+ end
630
+ end
631
+
632
+ def protect_inline_images(text, placeholders)
633
+ replace_inline_constructs(text, placeholders, '!') do |label, dest, title|
634
+ store_placeholder(placeholders, image_html(
635
+ restore_placeholders(label, placeholders),
636
+ restore_placeholders(dest, placeholders),
637
+ title && restore_placeholders(title, placeholders)
638
+ ))
639
+ end
640
+ end
641
+
642
+ def protect_inline_links(text, placeholders)
643
+ replace_inline_constructs(text, placeholders, nil) do |label, dest, title|
644
+ store_placeholder(placeholders, link_html(
645
+ restore_placeholders(label, placeholders),
646
+ restore_placeholders(dest, placeholders),
647
+ title && restore_placeholders(title, placeholders)
648
+ ))
649
+ end
650
+ end
651
+
652
+ def protect_reference_images(text, placeholders)
653
+ scan_reference_constructs(text, placeholders, :image)
654
+ end
655
+
656
+ def protect_reference_links(text, placeholders)
657
+ scan_reference_constructs(text, placeholders, :link)
658
+ end
659
+
660
+ def protect_single_word_text_links(text, placeholders)
661
+ output = String.new
662
+ index = 0
663
+ bracket_depth = 0
664
+
665
+ while index < text.length
666
+ char = text[index, 1]
667
+
668
+ if char == '\\' && index + 1 < text.length
669
+ output << text[index, 2]
670
+ index += 2
671
+ next
672
+ elsif char == '['
673
+ bracket_depth += 1
674
+ elsif char == ']' && bracket_depth > 0
675
+ bracket_depth -= 1
676
+ end
677
+
678
+ if bracket_depth.zero? && (match = text[index..-1].match(/\A([A-Za-z0-9]+)(?=\[)/))
679
+ label = match[1]
680
+ dest, consumed = parse_text_link_destination(text, index + label.length)
681
+
682
+ if dest
683
+ output << store_placeholder(placeholders, link_html(label, dest))
684
+ index += label.length + consumed
685
+ next
686
+ end
687
+ end
688
+
689
+ output << char
690
+ index += 1
691
+ end
692
+
693
+ output
694
+ end
695
+
696
+ def protect_braced_text_links(text, placeholders)
697
+ output = String.new
698
+ index = 0
699
+
700
+ while index < text.length
701
+ if text[index, 1] == '\\' && index + 1 < text.length
702
+ output << text[index, 2]
703
+ index += 2
704
+ next
705
+ end
706
+
707
+ if text[index, 1] == '{'
708
+ label_end = find_braced_text_link_label_end(text, index)
709
+ if label_end
710
+ label = text[(index + 1)...label_end]
711
+ dest, consumed = parse_text_link_destination(text, label_end + 1)
712
+
713
+ if dest
714
+ output << store_placeholder(placeholders, link_html(label, dest))
715
+ index = label_end + 1 + consumed
716
+ next
717
+ end
718
+ end
719
+ end
720
+
721
+ output << text[index, 1]
722
+ index += 1
723
+ end
724
+
725
+ output
726
+ end
727
+
728
+ def format_emphasis(text)
729
+ delimiters = []
730
+ output = []
731
+ index = 0
732
+
733
+ while index < text.length
734
+ char = text[index, 1]
735
+ if char == '*' || char == '_'
736
+ run_end = index
737
+ run_end += 1 while run_end < text.length && text[run_end, 1] == char
738
+ run_length = run_end - index
739
+ can_open, can_close = delimiter_flags(text, index, run_end, char)
740
+ token = {
741
+ :char => char,
742
+ :length => run_length,
743
+ :position => output.length,
744
+ :left_consumed => 0,
745
+ :right_consumed => 0,
746
+ :opening_html => String.new,
747
+ :closing_html => String.new,
748
+ :can_open => can_open,
749
+ :can_close => can_close
750
+ }
751
+ output << token
752
+
753
+ if can_close
754
+ delimiter_index = delimiters.length - 1
755
+ while delimiter_index >= 0 && available_delimiter_length(token) > 0
756
+ opener = delimiters[delimiter_index]
757
+ if opener[:char] == char && available_delimiter_length(opener) > 0 &&
758
+ !odd_match_disallowed?(opener, token)
759
+ use = available_delimiter_length(opener) >= 2 &&
760
+ available_delimiter_length(token) >= 2 ? 2 : 1
761
+ opener[:right_consumed] += use
762
+ opener[:opening_html] = (use == 2 ? '<strong>' : '<em>') + opener[:opening_html]
763
+ token[:left_consumed] += use
764
+ token[:closing_html] << (use == 2 ? '</strong>' : '</em>')
765
+ delimiters.reject! do |candidate|
766
+ candidate[:position] > opener[:position] &&
767
+ candidate[:position] < token[:position] &&
768
+ available_delimiter_length(candidate) > 0
769
+ end
770
+ delimiters.delete_at(delimiter_index) if available_delimiter_length(opener).zero?
771
+ delimiter_index = delimiters.length - 1
772
+ else
773
+ delimiter_index -= 1
774
+ end
775
+ end
776
+ end
777
+
778
+ delimiters << token if can_open && available_delimiter_length(token) > 0
779
+ index = run_end
780
+ else
781
+ output << char
782
+ index += 1
783
+ end
784
+ end
785
+
786
+ output.map do |piece|
787
+ next piece if piece.is_a?(String)
788
+
789
+ piece[:closing_html] +
790
+ (piece[:char] * available_delimiter_length(piece)) +
791
+ piece[:opening_html]
792
+ end.join
793
+ end
794
+
795
+ def format_strikethrough(text)
796
+ text.gsub(/~~([^\n~](?:.*?[^\n~])?)~~/, '<del>\1</del>')
797
+ end
798
+
799
+ def autolink_urls(text)
800
+ text.gsub(/(^|[^\w\/{"'=])((?:https?:\/\/|mailto:)[^\s<]+)/) do
801
+ match = Regexp.last_match
802
+ prefix = $1
803
+ before_url = text[0...match.begin(2)]
804
+ if before_url.end_with?('&lt;') || before_url.end_with?('&lt; ')
805
+ match[0]
806
+ else
807
+ url, trailer = strip_trailing_punctuation($2)
808
+ %(#{prefix}<a href="#{h(url)}">#{h(url)}</a>#{h(trailer)})
809
+ end
810
+ end
811
+ end
812
+
813
+ def restore_placeholders(text, placeholders)
814
+ text.gsub(PLACEHOLDER_RE) { placeholders[$1.to_i] }
815
+ end
816
+
817
+ def store_placeholder(placeholders, html)
818
+ placeholders << html
819
+ "\0#{placeholders.length - 1}\0"
820
+ end
821
+
822
+ def parse_labeled_list_line(line)
823
+ return [$1, $2] if line =~ LABEL_LIST_COLON_RE
824
+
825
+ nil
826
+ end
827
+
828
+ def extract_reference_definitions(text)
829
+ lines = split_lines(text)
830
+ kept_lines = []
831
+ index = 0
832
+ in_fenced_code = false
833
+ previous_line = nil
834
+
835
+ while index < lines.length
836
+ line = lines[index]
837
+ if fenced_code_start?(line)
838
+ in_fenced_code = !in_fenced_code
839
+ kept_lines << line
840
+ index += 1
841
+ previous_line = line
842
+ next
843
+ end
844
+
845
+ if in_fenced_code
846
+ kept_lines << line
847
+ index += 1
848
+ previous_line = line
849
+ next
850
+ end
851
+
852
+ parsed = parse_reference_definition_block(lines, index, previous_line)
853
+ if parsed
854
+ normalized = normalize_reference_label(parsed[:label])
855
+ @references[normalized] ||= parsed[:reference] unless normalized.empty?
856
+ kept_lines.concat(parsed[:replacement_lines])
857
+ index = parsed[:next_index]
858
+ previous_line = kept_lines.last
859
+ next
860
+ end
861
+
862
+ kept_lines << line
863
+ index += 1
864
+ previous_line = line
865
+ end
866
+
867
+ kept_lines.join
868
+ end
869
+
870
+ def normalize_reference_label(label)
871
+ normalized = label.to_s.gsub(/\\([\[\]])/, '\1').gsub(/\s+/, ' ').strip
872
+ unicode_casefold_compat(normalized)
873
+ end
874
+
875
+ def reference_link_html(label, ref)
876
+ reference = @references[normalize_reference_label(ref)]
877
+ return nil unless reference
878
+
879
+ attrs = %( href="#{h(reference[:url])}")
880
+ attrs += %( title="#{h(reference[:title])}") if reference[:title]
881
+ %(<a#{attrs}>#{format_inline(unescape_markdown_punctuation(label))}</a>)
882
+ end
883
+
884
+ def reference_image_html(alt, ref)
885
+ reference = @references[normalize_reference_label(ref)]
886
+ return nil unless reference
887
+
888
+ attrs = %( src="#{h(reference[:url])}" alt="#{h(plain_text(alt))}")
889
+ attrs += %( title="#{h(reference[:title])}") if reference[:title]
890
+ "<img#{attrs} />"
891
+ end
892
+
893
+ def blank_line?(line)
894
+ line.strip.empty?
895
+ end
896
+
897
+ def thematic_break?(line)
898
+ line =~ THEMATIC_BREAK_RE
899
+ end
900
+
901
+ def setext_underline_line?(line)
902
+ line =~ SETEXT_HEADING_RE
903
+ end
904
+
905
+ def fenced_code_start?(line)
906
+ !!parse_fence_opener(line)
907
+ end
908
+
909
+ def indented_code_start?(line)
910
+ leading_columns(line) >= 2
911
+ end
912
+
913
+ def indented_code_block_start?(lines, index, previous_block_type = nil)
914
+ return false unless indented_code_start?(lines[index])
915
+ return true if leading_columns(lines[index]) >= 4
916
+ return true if colon_indented_code_block_start?(lines, index)
917
+ return false if previous_block_type == :list
918
+ return false if html_block_start?(lines[index])
919
+ return false if parse_setext_heading(lines, index)
920
+
921
+ !index.zero? && blank_line?(lines[index - 1])
922
+ end
923
+
924
+ def colon_indented_code_block_start?(lines, index)
925
+ return false if index.zero?
926
+ return false unless leading_columns(lines[index]) >= 2
927
+ return false if leading_columns(lines[index]) >= 4
928
+
929
+ previous_line = lines[index - 1]
930
+ return false if blank_line?(previous_line)
931
+
932
+ previous_line.rstrip.end_with?(':')
933
+ end
934
+
935
+ def yard_indented_code_start?(lines, index)
936
+ return false unless leading_columns(lines[index]) >= 2
937
+ return false unless consume_columns(lines[index], 2) =~ /^!!!([\w.+-]+)[ \t]*$/
938
+ return false if index + 1 >= lines.length
939
+
940
+ indented_code_block_start?(lines, index) && indented_code_start?(lines[index + 1])
941
+ end
942
+
943
+ def list_start?(line, interrupt_paragraph = false)
944
+ return false unless (marker = parse_list_marker(line))
945
+ return true unless interrupt_paragraph
946
+
947
+ return false if marker[:content].empty?
948
+
949
+ !marker[:ordered] || marker[:start] == 1
950
+ end
951
+
952
+ def labeled_list_start?(lines, index)
953
+ line = lines[index]
954
+ return true if line =~ LABEL_LIST_COLON_RE
955
+ false
956
+ end
957
+
958
+ def blockquote_start?(line)
959
+ !strip_blockquote_marker(line).nil?
960
+ end
961
+
962
+ def html_block_start?(line, interrupt_paragraph = false)
963
+ !html_block_type(line, interrupt_paragraph).nil?
964
+ end
965
+
966
+ def table_start?(lines, index)
967
+ return false if index + 1 >= lines.length
968
+ table_row?(lines[index]) && lines[index + 1] =~ TABLE_SEPARATOR_RE
969
+ end
970
+
971
+ def table_row?(line)
972
+ stripped = line.strip
973
+ stripped.include?('|') && stripped !~ /\A[|:\-\s]+\z/
974
+ end
975
+
976
+ def split_table_row(line)
977
+ line.strip.sub(/\A\|/, '').sub(/\|\z/, '').split('|').map(&:strip)
978
+ end
979
+
980
+ def table_alignment(cell)
981
+ stripped = cell.strip
982
+ return 'center' if stripped.start_with?(':') && stripped.end_with?(':')
983
+ return 'left' if stripped.start_with?(':')
984
+ return 'right' if stripped.end_with?(':')
985
+
986
+ nil
987
+ end
988
+
989
+ def unindent(lines)
990
+ indent = lines.reject { |line| blank_line?(line) }.map do |line|
991
+ leading_columns(line)
992
+ end.min || 4
993
+
994
+ lines.map { |line| consume_columns(line, indent) }.join
995
+ end
996
+
997
+ def unindent_indented_code(lines)
998
+ lines.map { |line| consume_columns(line, 4) }.join
999
+ end
1000
+
1001
+ def code_block(text, lang = nil)
1002
+ lang, text = extract_codeblock_language(text, lang)
1003
+ attrs = lang ? %( class="#{h(lang)}") : ''
1004
+ "<pre><code#{attrs}>#{h(text)}</code></pre>"
1005
+ end
1006
+
1007
+ def extract_codeblock_language(text, lang = nil)
1008
+ return [lang, text] unless text =~ CODE_LANG_RE
1009
+
1010
+ lang ||= unescape_markdown_punctuation(decode_entities($1))
1011
+ [lang, $']
1012
+ end
1013
+
1014
+ def strip_trailing_punctuation(url)
1015
+ trailer = ''
1016
+ while url =~ /[),.;:!?]\z/
1017
+ trailer = url[-1, 1] + trailer
1018
+ url = url[0...-1]
1019
+ end
1020
+ [url, trailer]
1021
+ end
1022
+
1023
+ def heading_id(text)
1024
+ return '' unless @heading_ids
1025
+
1026
+ " id=\"#{text.gsub(/\W/, '_')}\""
1027
+ end
1028
+
1029
+ def parse_atx_heading(line)
1030
+ stripped = line.chomp.sub(/^\s{0,3}/, '')
1031
+ match = stripped.match(/\A(#{'#' * 6}|#{'#' * 5}|#{'#' * 4}|#{'#' * 3}|#{'#' * 2}|#)(?=[ \t]|$)(.*)\z/)
1032
+ return nil unless match
1033
+
1034
+ level = match[1].length
1035
+ content = match[2]
1036
+ content = content.sub(/\A[ \t]+/, '')
1037
+ content = content.sub(/[ \t]+#+[ \t]*\z/, '')
1038
+ content = '' if content =~ /\A#+\z/
1039
+ content = content.rstrip
1040
+ "<h#{level}#{heading_id(content)}>#{format_inline(content)}</h#{level}>"
1041
+ end
1042
+
1043
+ def parse_fence_opener(line)
1044
+ match = line.match(FENCE_RE)
1045
+ return nil unless match
1046
+
1047
+ indent = match[1].length
1048
+ fence = match[2]
1049
+ info = match[3].to_s.strip
1050
+ return nil if fence.start_with?('`') && info.include?('`')
1051
+
1052
+ lang = info.empty? ? nil : unescape_markdown_punctuation(decode_entities(info.split(/[ \t]/, 2).first))
1053
+ {:char => fence[0, 1], :length => fence.length, :indent => indent, :lang => lang}
1054
+ end
1055
+
1056
+ def fence_closer?(line, char, min_length)
1057
+ stripped = line.sub(/^\s{0,3}/, '')
1058
+ return false unless stripped.start_with?(char)
1059
+
1060
+ run = stripped[/\A#{Regexp.escape(char)}+/]
1061
+ run && run.length >= min_length && stripped.sub(/\A#{Regexp.escape(run)}/, '').strip.empty?
1062
+ end
1063
+
1064
+ def strip_fenced_indent(line, indent)
1065
+ return line.sub(/^\t/, '') if line.start_with?("\t")
1066
+
1067
+ line.sub(/\A {0,#{indent}}/, '')
1068
+ end
1069
+
1070
+ def parse_list_marker(line)
1071
+ source = line.to_s.sub(/\n\z/, '')
1072
+ indent, index = scan_leading_columns(source)
1073
+ return nil if indent > 3
1074
+ return nil if index >= source.length
1075
+
1076
+ char = source[index, 1]
1077
+ current_column = indent
1078
+
1079
+ if '*+-'.include?(char)
1080
+ marker_length = 1
1081
+ marker_end = index + 1
1082
+ current_column += 1
1083
+ padding, marker_end = scan_padding_columns(source, marker_end, current_column)
1084
+ content = source[marker_end..-1].to_s
1085
+ return nil if padding.zero? && !content.empty?
1086
+
1087
+ return {:ordered => false, :bullet => char, :indent => indent,
1088
+ :marker_length => marker_length, :padding => padding, :content => content}
1089
+ end
1090
+
1091
+ number = source[index..-1][/^\d{1,9}/]
1092
+ if number
1093
+ marker_end = index + number.length
1094
+ delimiter = source[marker_end, 1]
1095
+ if delimiter == '.' || delimiter == ')'
1096
+ marker_length = number.length + 1
1097
+ current_column += marker_length
1098
+ marker_end += 1
1099
+ padding, marker_end = scan_padding_columns(source, marker_end, current_column)
1100
+ content = source[marker_end..-1].to_s
1101
+ return nil if padding.zero? && !content.empty?
1102
+
1103
+ return {:ordered => true, :delimiter => delimiter, :start => number.to_i,
1104
+ :indent => indent, :marker_length => marker_length,
1105
+ :padding => padding, :content => content}
1106
+ end
1107
+ end
1108
+
1109
+ if source[index, 2] =~ /\A[A-Za-z]\.\z/
1110
+ marker_length = 2
1111
+ marker_end = index + marker_length
1112
+ current_column += marker_length
1113
+ padding, marker_end = scan_padding_columns(source, marker_end, current_column)
1114
+ content = source[marker_end..-1].to_s
1115
+ return nil if padding.zero? && !content.empty?
1116
+
1117
+ return {:ordered => true, :delimiter => '.', :start => 1,
1118
+ :indent => indent, :marker_length => marker_length,
1119
+ :padding => padding, :content => content}
1120
+ end
1121
+
1122
+ nil
1123
+ end
1124
+
1125
+ def list_item_padding(marker)
1126
+ (1..4).include?(marker[:padding]) ? marker[:padding] : 1
1127
+ end
1128
+
1129
+ def same_list_type?(base, other)
1130
+ return false unless other
1131
+ return base[:bullet] == other[:bullet] if !base[:ordered] && !other[:ordered]
1132
+
1133
+ base[:ordered] && other[:ordered] && base[:delimiter] == other[:delimiter]
1134
+ end
1135
+
1136
+ def block_boundary?(line)
1137
+ thematic_break?(line) || parse_heading(line) || fenced_code_start?(line) ||
1138
+ table_row?(line) || labeled_list_start?([line, ''], 0) || blockquote_start?(line) ||
1139
+ html_block_start?(line) || parse_list_marker(line)
1140
+ end
1141
+
1142
+ def parse_reference_definition(label, definition)
1143
+ definition = definition.to_s
1144
+ return nil if normalize_reference_label(label).empty?
1145
+
1146
+ index = 0
1147
+ index += 1 while index < definition.length && definition[index, 1] =~ /[ \t\n]/
1148
+ return nil if index >= definition.length
1149
+
1150
+ if definition[index, 1] == '<'
1151
+ close = definition.index('>', index + 1)
1152
+ return nil unless close
1153
+ url = definition[(index + 1)...close]
1154
+ return nil if url.include?("\n")
1155
+ index = close + 1
1156
+ return nil if index < definition.length && definition[index, 1] !~ /[ \t\n]/
1157
+ else
1158
+ start = index
1159
+ while index < definition.length && definition[index, 1] !~ /[ \t\n]/
1160
+ index += 1
1161
+ end
1162
+ url = definition[start...index]
1163
+ end
1164
+
1165
+ return nil if url.nil? || url.include?('<') || url.include?('>')
1166
+
1167
+ index += 1 while index < definition.length && definition[index, 1] =~ /[ \t\n]/
1168
+ title = nil
1169
+
1170
+ if index < definition.length
1171
+ delimiter = definition[index, 1]
1172
+ close_delimiter = delimiter == '(' ? ')' : delimiter
1173
+ if delimiter == '"' || delimiter == "'" || delimiter == '('
1174
+ index += 1
1175
+ start = index
1176
+ buffer = String.new
1177
+ while index < definition.length
1178
+ char = definition[index, 1]
1179
+ if char == '\\' && index + 1 < definition.length
1180
+ buffer << definition[index, 2]
1181
+ index += 2
1182
+ next
1183
+ end
1184
+ break if char == close_delimiter
1185
+ buffer << char
1186
+ index += 1
1187
+ end
1188
+ return nil if index >= definition.length || definition[index, 1] != close_delimiter
1189
+ title = buffer
1190
+ index += 1
1191
+ index += 1 while index < definition.length && definition[index, 1] =~ /[ \t\n]/
1192
+ return nil unless index == definition.length
1193
+ else
1194
+ return nil
1195
+ end
1196
+ end
1197
+
1198
+ {
1199
+ :url => escape_url(unescape_markdown_punctuation(decode_entities(url))),
1200
+ :title => title && unescape_markdown_punctuation(decode_entities(title))
1201
+ }
1202
+ end
1203
+
1204
+ def replace_inline_constructs(text, placeholders, prefix)
1205
+ output = String.new
1206
+ index = 0
1207
+
1208
+ while index < text.length
1209
+ if prefix
1210
+ if text[index, 2] != '![' || (index > 0 && text[index - 1, 1] == '\\')
1211
+ output << text[index, 1]
1212
+ index += 1
1213
+ next
1214
+ end
1215
+ label_start = index + 2
1216
+ else
1217
+ if text[index, 1] != '[' || (index > 0 && text[index - 1, 1] == '\\')
1218
+ output << text[index, 1]
1219
+ index += 1
1220
+ next
1221
+ end
1222
+ label_start = index + 1
1223
+ end
1224
+
1225
+ label_end = find_closing_bracket(text, label_start - 1)
1226
+ unless label_end && text[label_end + 1, 1] == '('
1227
+ output << text[index, 1]
1228
+ index += 1
1229
+ next
1230
+ end
1231
+
1232
+ dest, title, consumed = parse_inline_destination(text, label_end + 2, placeholders)
1233
+ unless consumed
1234
+ output << text[index, 1]
1235
+ index += 1
1236
+ next
1237
+ end
1238
+
1239
+ label = text[label_start...label_end]
1240
+ if !prefix && contains_nested_link?(label, placeholders)
1241
+ output << text[index, 1]
1242
+ index += 1
1243
+ next
1244
+ end
1245
+ output << yield(label, dest, title)
1246
+ index = consumed
1247
+ end
1248
+
1249
+ output
1250
+ end
1251
+
1252
+ def scan_reference_constructs(text, placeholders, kind)
1253
+ output = String.new
1254
+ index = 0
1255
+
1256
+ while index < text.length
1257
+ image = kind == :image
1258
+ if image
1259
+ if text[index, 2] != '![' || (index > 0 && text[index - 1, 1] == '\\')
1260
+ output << text[index, 1]
1261
+ index += 1
1262
+ next
1263
+ end
1264
+ label_open = index + 1
1265
+ else
1266
+ if text[index, 1] != '[' || (index > 0 && text[index - 1, 1] == '\\')
1267
+ output << text[index, 1]
1268
+ index += 1
1269
+ next
1270
+ end
1271
+ label_open = index
1272
+ end
1273
+
1274
+ label_close = find_closing_bracket(text, label_open)
1275
+ unless label_close
1276
+ output << text[index, 1]
1277
+ index += 1
1278
+ next
1279
+ end
1280
+
1281
+ next_char = text[label_close + 1, 1]
1282
+ label = restore_placeholders(text[(label_open + 1)...label_close], placeholders)
1283
+ html = nil
1284
+ consumed = nil
1285
+
1286
+ if next_char == '['
1287
+ ref_close = find_closing_bracket(text, label_close + 1)
1288
+ if ref_close
1289
+ ref = restore_placeholders(text[(label_close + 2)...ref_close], placeholders)
1290
+ ref = label if ref.empty?
1291
+ if kind == :link && contains_nested_link?(label, placeholders)
1292
+ output << text[index]
1293
+ index += 1
1294
+ next
1295
+ end
1296
+ html = kind == :image ? reference_image_html(label, ref) : reference_link_html(label, ref)
1297
+ consumed = ref_close + 1 if html
1298
+ end
1299
+ else
1300
+ if kind == :link && contains_nested_link?(label, placeholders)
1301
+ output << text[index, 1]
1302
+ index += 1
1303
+ next
1304
+ end
1305
+ html = kind == :image ? reference_image_html(label, label) : reference_link_html(label, label)
1306
+ consumed = label_close + 1 if html
1307
+ end
1308
+
1309
+ if html
1310
+ output << store_placeholder(placeholders, html)
1311
+ index = consumed
1312
+ else
1313
+ output << text[index, 1]
1314
+ index += 1
1315
+ end
1316
+ end
1317
+
1318
+ output
1319
+ end
1320
+
1321
+ def find_closing_bracket(text, open_index)
1322
+ depth = 0
1323
+ index = open_index
1324
+ while index < text.length
1325
+ char = text[index, 1]
1326
+ if char == '['
1327
+ depth += 1
1328
+ elsif char == ']'
1329
+ depth -= 1
1330
+ return index if depth.zero?
1331
+ elsif char == '\\'
1332
+ index += 1
1333
+ end
1334
+ index += 1
1335
+ end
1336
+ nil
1337
+ end
1338
+
1339
+ def find_matching_backtick_run(text, index, length)
1340
+ while index < text.length
1341
+ if text[index, 1] == '`'
1342
+ run_length = 1
1343
+ run_length += 1 while index + run_length < text.length && text[index + run_length, 1] == '`'
1344
+ return index if run_length == length
1345
+
1346
+ index += run_length
1347
+ next
1348
+ end
1349
+ index += 1
1350
+ end
1351
+
1352
+ nil
1353
+ end
1354
+
1355
+ def parse_inline_destination(text, index, placeholders = nil)
1356
+ while index < text.length && text[index, 1] =~ /[ \t\n]/
1357
+ index += 1
1358
+ end
1359
+
1360
+ if text[index, 1] == '<'
1361
+ close = text.index('>', index + 1)
1362
+ return [nil, nil, nil] unless close
1363
+ dest = text[(index + 1)...close]
1364
+ return [nil, nil, nil] if dest.include?("\n") || dest.include?('\\')
1365
+ dest = dest.gsub(' ', '%20')
1366
+ index = close + 1
1367
+ else
1368
+ close = index
1369
+ parens = 0
1370
+ while close < text.length
1371
+ char = text[close, 1]
1372
+ if char == '\\' && close + 1 < text.length
1373
+ close += 2
1374
+ next
1375
+ end
1376
+ break if parens.zero? && (char == ')' || char =~ /\s/)
1377
+ parens += 1 if char == '('
1378
+ parens -= 1 if char == ')'
1379
+ close += 1
1380
+ end
1381
+ dest = text[index...close]
1382
+ index = close
1383
+ end
1384
+
1385
+ if placeholders
1386
+ restored_dest = restore_placeholders(dest.to_s, placeholders)
1387
+ if restored_dest.start_with?('<')
1388
+ return [nil, nil, nil] if restored_dest.include?("\n") || restored_dest.include?('\\')
1389
+ return [nil, nil, nil] unless restored_dest.end_with?('>') && restored_dest.index('>') == restored_dest.length - 1
1390
+
1391
+ dest = restored_dest[1...-1]
1392
+ end
1393
+ end
1394
+
1395
+ while index < text.length && text[index, 1] =~ /[ \t\n]/
1396
+ index += 1
1397
+ end
1398
+
1399
+ title = nil
1400
+ if text[index, 1] == '"' || text[index, 1] == "'"
1401
+ delimiter = text[index, 1]
1402
+ index += 1
1403
+ buffer = String.new
1404
+ while index < text.length
1405
+ char = text[index, 1]
1406
+ if char == '\\' && index + 1 < text.length
1407
+ buffer << text[index, 2]
1408
+ index += 2
1409
+ next
1410
+ end
1411
+ break if char == delimiter
1412
+ buffer << char
1413
+ index += 1
1414
+ end
1415
+ return [nil, nil, nil] unless index < text.length && text[index, 1] == delimiter
1416
+ title = buffer
1417
+ index += 1
1418
+ elsif text[index, 1] == '('
1419
+ index += 1
1420
+ buffer = String.new
1421
+ depth = 1
1422
+ while index < text.length
1423
+ char = text[index, 1]
1424
+ if char == '\\' && index + 1 < text.length
1425
+ buffer << text[index, 2]
1426
+ index += 2
1427
+ next
1428
+ end
1429
+ if char == '('
1430
+ depth += 1
1431
+ elsif char == ')'
1432
+ depth -= 1
1433
+ break if depth.zero?
1434
+ end
1435
+ buffer << char
1436
+ index += 1
1437
+ end
1438
+ return [nil, nil, nil] unless index < text.length && text[index, 1] == ')'
1439
+ title = buffer
1440
+ index += 1
1441
+ end
1442
+
1443
+ while index < text.length && text[index, 1] =~ /[ \t\n]/
1444
+ index += 1
1445
+ end
1446
+ return [nil, nil, nil] unless text[index, 1] == ')'
1447
+
1448
+ [dest.to_s, title, index + 1]
1449
+ end
1450
+
1451
+ def plain_text(text)
1452
+ text = text.to_s.gsub(/!\[([^\]]*)\]\([^)]+\)/, '\1')
1453
+ text = text.gsub(/\[([^\]]+)\]\([^)]+\)/, '\1')
1454
+ text = text.gsub(/[*_~`]/, '')
1455
+ decode_entities(unescape_markdown_punctuation(text))
1456
+ end
1457
+
1458
+ def parse_text_link_destination(text, index)
1459
+ return [nil, 0] unless text[index, 1] == '['
1460
+
1461
+ dest = String.new
1462
+ cursor = index + 1
1463
+
1464
+ while cursor < text.length
1465
+ char = text[cursor, 1]
1466
+
1467
+ if char == '\\'
1468
+ escaped = text[cursor + 1, 1]
1469
+ return [nil, 0] unless escaped && "[]\\*+<_".include?(escaped)
1470
+
1471
+ dest << escaped
1472
+ cursor += 2
1473
+ next
1474
+ end
1475
+
1476
+ return [nil, 0] if char =~ /\s/
1477
+ return [dest, cursor - index + 1] if char == ']'
1478
+ return [nil, 0] if char == '['
1479
+
1480
+ dest << char
1481
+ cursor += 1
1482
+ end
1483
+
1484
+ [nil, 0]
1485
+ end
1486
+
1487
+ def find_braced_text_link_label_end(text, index)
1488
+ cursor = index + 1
1489
+
1490
+ while cursor < text.length
1491
+ char = text[cursor, 1]
1492
+
1493
+ if char == '\\'
1494
+ cursor += 2
1495
+ next
1496
+ end
1497
+
1498
+ return cursor if char == '}'
1499
+ cursor += 1
1500
+ end
1501
+
1502
+ nil
1503
+ end
1504
+
1505
+ def link_html(label, dest, title = nil)
1506
+ href = escape_url(unescape_markdown_punctuation(decode_entities(dest.to_s)))
1507
+ normalized_title = title && unescape_markdown_punctuation(decode_entities(title))
1508
+ attrs = %( href="#{h(href)}")
1509
+ attrs += %( title="#{h(normalized_title)}") if normalized_title
1510
+ %(<a#{attrs}>#{format_inline(label)}</a>)
1511
+ end
1512
+
1513
+ def image_html(label, dest, title = nil)
1514
+ src = escape_url(unescape_markdown_punctuation(decode_entities(dest.to_s)))
1515
+ normalized_title = title && unescape_markdown_punctuation(decode_entities(title))
1516
+ attrs = %( src="#{h(src)}" alt="#{h(plain_text(label))}")
1517
+ attrs += %( title="#{h(normalized_title)}") if normalized_title
1518
+ "<img#{attrs} />"
1519
+ end
1520
+
1521
+ def decode_entities(text)
1522
+ text.gsub(ENTITY_RE) do |entity|
1523
+ decode_entity(entity)
1524
+ end
1525
+ end
1526
+
1527
+ def reference_definition_continuation?(line)
1528
+ return true if line =~ /^(?: {1,3}|\t)(.*)$/
1529
+ return true if line =~ /\A<(?:[^>\n]*)>\s*\z/
1530
+ return true if line =~ /\A(?:"[^"]*"|'[^']*'|\([^)]*\))\s*\z/
1531
+
1532
+ false
1533
+ end
1534
+
1535
+ def normalize_code_span(code)
1536
+ code = code.gsub(/\n/, ' ')
1537
+ if code.length > 1 && code.start_with?(' ') && code.end_with?(' ') && code.strip != ''
1538
+ code[1...-1]
1539
+ else
1540
+ code
1541
+ end
1542
+ end
1543
+
1544
+ def available_delimiter_length(token)
1545
+ token[:length] - token[:left_consumed] - token[:right_consumed]
1546
+ end
1547
+
1548
+ def odd_match_disallowed?(opener, closer)
1549
+ return false unless opener[:can_close] || closer[:can_open]
1550
+
1551
+ opener_len = available_delimiter_length(opener)
1552
+ closer_len = available_delimiter_length(closer)
1553
+ ((opener_len + closer_len) % 3).zero? &&
1554
+ (opener_len % 3 != 0 || closer_len % 3 != 0)
1555
+ end
1556
+
1557
+ def delimiter_flags(text, run_start, run_end, char)
1558
+ before = run_start.zero? ? nil : text[run_start - 1, 1]
1559
+ after = run_end >= text.length ? nil : text[run_end, 1]
1560
+ before_whitespace = whitespace_char?(before)
1561
+ after_whitespace = whitespace_char?(after)
1562
+ before_punctuation = punctuation_char?(before)
1563
+ after_punctuation = punctuation_char?(after)
1564
+
1565
+ left_flanking = !after_whitespace && (!after_punctuation || before_whitespace || before_punctuation)
1566
+ right_flanking = !before_whitespace && (!before_punctuation || after_whitespace || after_punctuation)
1567
+
1568
+ if char == '_'
1569
+ [
1570
+ left_flanking && (!right_flanking || before_punctuation),
1571
+ right_flanking && (!left_flanking || after_punctuation)
1572
+ ]
1573
+ else
1574
+ [left_flanking, right_flanking]
1575
+ end
1576
+ end
1577
+
1578
+ def whitespace_char?(char)
1579
+ char.nil? || char =~ /\s/ || char == HtmlEntities::ENTITIES['nbsp']
1580
+ end
1581
+
1582
+ def punctuation_char?(char)
1583
+ return false if char.nil?
1584
+
1585
+ ascii_punctuation_char?(char) || unicode_symbol_char?(char)
1586
+ end
1587
+
1588
+ def unicode_symbol_char?(char)
1589
+ codepoint = char.to_s.unpack('U*').first
1590
+ return false unless codepoint
1591
+
1592
+ (0x00A2..0x00A9).include?(codepoint) ||
1593
+ (0x00AC..0x00AE).include?(codepoint) ||
1594
+ (0x00B0..0x00B4).include?(codepoint) ||
1595
+ codepoint == 0x00B6 ||
1596
+ codepoint == 0x00B7 ||
1597
+ codepoint == 0x00D7 ||
1598
+ codepoint == 0x00F7 ||
1599
+ (0x20A0..0x20CF).include?(codepoint)
1600
+ end
1601
+
1602
+ def ascii_punctuation_char?(char)
1603
+ return false unless ascii_only_compat?(char)
1604
+
1605
+ byte = char.to_s.unpack('C').first
1606
+ return false unless byte
1607
+
1608
+ (0x21..0x2F).include?(byte) ||
1609
+ (0x3A..0x40).include?(byte) ||
1610
+ (0x5B..0x60).include?(byte) ||
1611
+ (0x7B..0x7E).include?(byte)
1612
+ end
1613
+
1614
+ def leading_columns(line)
1615
+ scan_leading_columns(line.to_s).first
1616
+ end
1617
+
1618
+ def indented_to?(line, indent)
1619
+ leading_columns(line) >= indent
1620
+ end
1621
+
1622
+ def strip_list_item_indent(line, content_indent)
1623
+ consume_columns(line, content_indent, 0, true)
1624
+ end
1625
+
1626
+ def escape_list_marker_text(line)
1627
+ source = line.to_s.sub(/\n\z/, '')
1628
+ newline = source.length == line.to_s.length ? '' : "\n"
1629
+
1630
+ if source =~ /\A([*+-])([ \t].*)\z/
1631
+ "\\#{$1}#{$2}#{newline}"
1632
+ elsif source =~ /\A(\d{1,9}[.)])([ \t].*)\z/
1633
+ "\\#{$1}#{$2}#{newline}"
1634
+ elsif source =~ /\A([A-Za-z]\.)([ \t].*)\z/
1635
+ "\\#{$1}#{$2}#{newline}"
1636
+ else
1637
+ source + newline
1638
+ end
1639
+ end
1640
+
1641
+ def escape_url(url)
1642
+ percent_encode_url(url.to_s, /[A-Za-z0-9\-._~:\/?#\[\]@!$&'()*+,;=%]/)
1643
+ end
1644
+
1645
+ def escape_autolink_url(url)
1646
+ percent_encode_url(url.to_s, /[A-Za-z0-9\-._~:\/?#@!$&'()*+,;=%]/)
1647
+ end
1648
+
1649
+ def parse_reference_definition_block(lines, index, previous_line)
1650
+ line = lines[index]
1651
+ return nil unless reference_definition_context?(previous_line)
1652
+
1653
+ prefix, content = split_reference_container_prefix(line)
1654
+ return nil unless content =~ /^\s{0,3}\[/
1655
+
1656
+ label_buffer = content.sub(/^\s{0,3}/, '')
1657
+ consumed_lines = [line]
1658
+ label_end = find_reference_label_end(label_buffer)
1659
+ current_index = index
1660
+
1661
+ while label_end.nil?
1662
+ current_index += 1
1663
+ return nil if current_index >= lines.length
1664
+
1665
+ next_prefix, next_content = split_reference_container_prefix(lines[current_index])
1666
+ return nil unless next_prefix == prefix
1667
+
1668
+ label_buffer << next_content
1669
+ consumed_lines << lines[current_index]
1670
+ label_end = find_reference_label_end(label_buffer)
1671
+ end
1672
+
1673
+ label = label_buffer[1...label_end]
1674
+ remainder = label_buffer[(label_end + 2)..-1].to_s
1675
+ current_index += 1
1676
+
1677
+ while remainder.strip.empty? && current_index < lines.length
1678
+ next_prefix, next_content = split_reference_container_prefix(lines[current_index])
1679
+ break unless next_prefix == prefix
1680
+ break if blank_line?(next_content)
1681
+
1682
+ remainder << (remainder.empty? ? next_content : "\n#{next_content}")
1683
+ consumed_lines << lines[current_index]
1684
+ current_index += 1
1685
+ end
1686
+
1687
+ while unclosed_reference_title?(remainder) && current_index < lines.length
1688
+ next_prefix, next_content = split_reference_container_prefix(lines[current_index])
1689
+ break unless next_prefix == prefix
1690
+ break if blank_line?(next_content)
1691
+
1692
+ remainder << "\n#{next_content}"
1693
+ consumed_lines << lines[current_index]
1694
+ current_index += 1
1695
+ end
1696
+
1697
+ while current_index < lines.length
1698
+ next_prefix, next_content = split_reference_container_prefix(lines[current_index])
1699
+ break unless next_prefix == prefix
1700
+ break unless reference_definition_continuation?(next_content)
1701
+
1702
+ remainder << "\n#{next_content.strip}"
1703
+ consumed_lines << lines[current_index]
1704
+ current_index += 1
1705
+ end
1706
+
1707
+ reference = parse_reference_definition(label, remainder)
1708
+ return nil unless reference
1709
+
1710
+ {
1711
+ :label => label,
1712
+ :reference => reference,
1713
+ :replacement_lines => consumed_lines.map { |consumed| reference_definition_replacement_line(consumed, prefix) },
1714
+ :next_index => current_index
1715
+ }
1716
+ end
1717
+
1718
+ def reference_definition_context?(previous_line)
1719
+ return true if previous_line.nil?
1720
+ return true if blank_line?(previous_line)
1721
+
1722
+ stripped = split_reference_container_prefix(previous_line).last
1723
+ block_boundary?(stripped)
1724
+ end
1725
+
1726
+ def split_reference_container_prefix(line)
1727
+ prefix = String.new
1728
+ content = line.chomp
1729
+
1730
+ while (split = split_blockquote_prefix(content))
1731
+ prefix << split[0]
1732
+ content = split[1].chomp
1733
+ end
1734
+
1735
+ [prefix, content]
1736
+ end
1737
+
1738
+ def reference_definition_replacement_line(line, prefix)
1739
+ return '' if prefix.empty?
1740
+
1741
+ prefix.rstrip + "\n"
1742
+ end
1743
+
1744
+ def find_reference_label_end(text)
1745
+ return nil unless text.start_with?('[')
1746
+
1747
+ index = 1
1748
+ while index < text.length
1749
+ char = text[index, 1]
1750
+ if char == '\\'
1751
+ index += 2
1752
+ next
1753
+ end
1754
+ return nil if char == '['
1755
+ return index if char == ']' && text[index + 1, 1] == ':'
1756
+
1757
+ index += 1
1758
+ end
1759
+
1760
+ nil
1761
+ end
1762
+
1763
+ def contains_nested_link?(label, placeholders)
1764
+ text = restore_placeholders(label.to_s, placeholders)
1765
+ return true if text.include?('<a ')
1766
+
1767
+ index = 0
1768
+
1769
+ while index < text.length
1770
+ if text[index, 2] == '![' && (index.zero? || text[index - 1, 1] != '\\')
1771
+ label_open = index + 1
1772
+ elsif text[index, 1] == '[' && (index.zero? || text[index - 1, 1] != '\\')
1773
+ label_open = index
1774
+ else
1775
+ index += 1
1776
+ next
1777
+ end
1778
+
1779
+ label_close = find_closing_bracket(text, label_open)
1780
+ if label_close
1781
+ next_char = text[label_close + 1, 1]
1782
+ return true if next_char == '(' || next_char == '['
1783
+ end
1784
+
1785
+ index += 1
1786
+ end
1787
+
1788
+ false
1789
+ end
1790
+
1791
+ def unclosed_reference_title?(text)
1792
+ stripped = text.to_s.rstrip
1793
+ return false if stripped.empty?
1794
+
1795
+ single_quotes = stripped.count("'")
1796
+ double_quotes = stripped.count('"')
1797
+ open_parens = stripped.count('(')
1798
+ close_parens = stripped.count(')')
1799
+
1800
+ single_quotes.odd? || double_quotes.odd? || open_parens > close_parens
1801
+ end
1802
+
1803
+ def percent_encode_url(text, allowed_re)
1804
+ encoded = String.new
1805
+
1806
+ each_char_compat(text.to_s) do |char|
1807
+ if ascii_only_compat?(char) && char =~ /\A#{allowed_re.source}\z/
1808
+ encoded << char
1809
+ else
1810
+ utf8_bytes(char).each do |byte|
1811
+ encoded << sprintf('%%%02X', byte)
1812
+ end
1813
+ end
1814
+ end
1815
+
1816
+ encoded
1817
+ end
1818
+
1819
+ def html_block_type(line, interrupt_paragraph = false)
1820
+ stripped = line.chomp
1821
+ return nil unless stripped =~ /^\s{0,3}</ || stripped =~ /^\s{0,3}<(?!!--)/
1822
+
1823
+ return 1 if stripped =~ /^\s{0,3}<(?:script|pre|style|textarea)(?:\s|>|$)/i
1824
+ return 2 if stripped =~ /^\s{0,3}<!--/
1825
+ return 3 if stripped =~ /^\s{0,3}<\?/
1826
+ return 4 if stripped =~ /^\s{0,3}<![A-Z]/
1827
+ return 5 if stripped =~ /^\s{0,3}<!\[CDATA\[/
1828
+ return 6 if stripped =~ /^\s{0,3}<\/?(?:#{HTML_BLOCK_TAGS.join('|')})(?:\s|\/?>|$)/i
1829
+ return nil if interrupt_paragraph
1830
+
1831
+ return 7 if stripped =~ /^\s{0,3}(?:<[A-Za-z][A-Za-z0-9-]*(?:\s+[A-Za-z_:][\w:.-]*(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'=<>`]+))?)*\s*\/?>|<\/[A-Za-z][A-Za-z0-9-]*\s*>)\s*$/
1832
+
1833
+ nil
1834
+ end
1835
+
1836
+ def html_block_end?(type, line)
1837
+ case type
1838
+ when 1
1839
+ line =~ %r{</(?:script|pre|style|textarea)\s*>}i
1840
+ when 2
1841
+ line.include?('-->')
1842
+ when 3
1843
+ line.include?('?>')
1844
+ when 4
1845
+ line.include?('>')
1846
+ when 5
1847
+ line.include?(']]>')
1848
+ when 6, 7
1849
+ blank_line?(line)
1850
+ else
1851
+ false
1852
+ end
1853
+ end
1854
+
1855
+ def decode_entity(entity)
1856
+ case entity
1857
+ when /\A&#(\d+);\z/
1858
+ codepoint = $1.to_i
1859
+ when /\A&#[xX]([0-9A-Fa-f]+);\z/
1860
+ codepoint = $1.to_i(16)
1861
+ else
1862
+ name = entity[1..-2]
1863
+ return HtmlEntities::ENTITIES[name] || CGI.unescapeHTML(entity)
1864
+ end
1865
+
1866
+ return [0xFFFD].pack('U') if codepoint.zero?
1867
+ return entity if codepoint > 0x10FFFF
1868
+ [codepoint].pack('U')
1869
+ rescue RangeError
1870
+ entity
1871
+ end
1872
+
1873
+ def h(text)
1874
+ text.to_s.gsub('&', '&amp;').gsub('<', '&lt;').gsub('>', '&gt;').gsub('"', '&quot;')
1875
+ end
1876
+
1877
+ def unescape_markdown_punctuation(text)
1878
+ text.to_s.gsub(ESCAPABLE_CHARS_RE, '\1')
1879
+ end
1880
+
1881
+ def split_lines(text)
1882
+ text.to_s.split(/^/, -1)
1883
+ end
1884
+
1885
+ def scan_leading_columns(text)
1886
+ index = 0
1887
+ column = 0
1888
+ source = text.to_s
1889
+
1890
+ while index < source.length
1891
+ char = source[index, 1]
1892
+ if char == ' '
1893
+ column += 1
1894
+ elsif char == "\t"
1895
+ column += TAB_WIDTH - (column % TAB_WIDTH)
1896
+ else
1897
+ break
1898
+ end
1899
+ index += 1
1900
+ end
1901
+
1902
+ [column, index]
1903
+ end
1904
+
1905
+ def scan_padding_columns(text, index, start_column)
1906
+ column = start_column
1907
+ padding = 0
1908
+ source = text.to_s
1909
+
1910
+ while index < source.length
1911
+ char = source[index, 1]
1912
+ if char == ' '
1913
+ column += 1
1914
+ padding += 1
1915
+ elsif char == "\t"
1916
+ advance = TAB_WIDTH - (column % TAB_WIDTH)
1917
+ column += advance
1918
+ padding += advance
1919
+ else
1920
+ break
1921
+ end
1922
+ index += 1
1923
+ end
1924
+
1925
+ [padding, index]
1926
+ end
1927
+
1928
+ def consume_columns(text, columns, start_column = 0, normalize_remaining = false)
1929
+ index = 0
1930
+ column = start_column
1931
+ remaining = columns
1932
+ prefix_width = 0
1933
+ source = text.to_s
1934
+
1935
+ while index < source.length && remaining > 0
1936
+ char = source[index, 1]
1937
+ if char == ' '
1938
+ column += 1
1939
+ remaining -= 1
1940
+ index += 1
1941
+ elsif char == "\t"
1942
+ advance = TAB_WIDTH - (column % TAB_WIDTH)
1943
+ if advance <= remaining
1944
+ column += advance
1945
+ remaining -= advance
1946
+ index += 1
1947
+ else
1948
+ prefix_width += advance - remaining if normalize_remaining
1949
+ column += advance
1950
+ remaining = 0
1951
+ index += 1
1952
+ end
1953
+ else
1954
+ break
1955
+ end
1956
+ end
1957
+
1958
+ if normalize_remaining
1959
+ while index < source.length
1960
+ char = source[index, 1]
1961
+ if char == ' '
1962
+ prefix_width += 1
1963
+ column += 1
1964
+ index += 1
1965
+ elsif char == "\t"
1966
+ advance = TAB_WIDTH - (column % TAB_WIDTH)
1967
+ prefix_width += advance
1968
+ column += advance
1969
+ index += 1
1970
+ else
1971
+ break
1972
+ end
1973
+ end
1974
+
1975
+ (' ' * prefix_width) + source[index..-1].to_s
1976
+ else
1977
+ source[index..-1].to_s
1978
+ end
1979
+ end
1980
+
1981
+ def lazy_blockquote_continuation?(quoted_lines, line)
1982
+ return false if block_boundary?(line)
1983
+ return false if indented_code_start?(line) && !blockquote_paragraph_context?(quoted_lines)
1984
+
1985
+ last_content = quoted_lines.reverse.find { |quoted| !blank_line?(quoted) }
1986
+ return false if last_content && fenced_code_start?(last_content)
1987
+ return false if last_content && indented_code_start?(last_content)
1988
+
1989
+ true
1990
+ end
1991
+
1992
+ def blockquote_open_fence?(quoted_lines)
1993
+ opener = nil
1994
+
1995
+ quoted_lines.each do |quoted|
1996
+ next if blank_line?(quoted)
1997
+
1998
+ if opener
1999
+ opener = nil if fence_closer?(quoted, opener[:char], opener[:length])
2000
+ else
2001
+ opener = parse_fence_opener(quoted)
2002
+ end
2003
+ end
2004
+
2005
+ !opener.nil?
2006
+ end
2007
+
2008
+ def blockquote_paragraph_context?(quoted_lines)
2009
+ last_content = quoted_lines.reverse.find { |quoted| !blank_line?(quoted) }
2010
+ return false unless last_content
2011
+ return false if fenced_code_start?(last_content)
2012
+ return false if parse_heading(last_content)
2013
+ return false if thematic_break?(last_content)
2014
+
2015
+ true
2016
+ end
2017
+
2018
+ def normalize_paragraph_line(line)
2019
+ line.to_s.chomp.sub(/^\s+/, '')
2020
+ end
2021
+
2022
+ def normalize_heading_line(line)
2023
+ normalize_paragraph_line(line).rstrip
2024
+ end
2025
+
2026
+ def split_blockquote_prefix(line)
2027
+ source = line.to_s
2028
+ indent, index = scan_leading_columns(source)
2029
+ return nil if indent > 3
2030
+ return nil unless source[index, 1] == '>'
2031
+
2032
+ prefix = source[0..index]
2033
+ rest = source[(index + 1)..-1].to_s
2034
+ if rest.start_with?(' ') || rest.start_with?("\t")
2035
+ prefix << rest[0, 1]
2036
+ rest = consume_columns(rest, 1, indent + 1, true)
2037
+ end
2038
+
2039
+ [prefix, rest.end_with?("\n") ? rest : "#{rest}\n"]
2040
+ end
2041
+
2042
+ def strip_blockquote_marker(line)
2043
+ split = split_blockquote_prefix(line)
2044
+ split && split[1]
2045
+ end
2046
+
2047
+ def loose_list_item_continuation?(item_lines)
2048
+ return false if open_fence_in_lines?(item_lines)
2049
+
2050
+ previous = item_lines.reverse.find { |item_line| item_line != "\n" }
2051
+ return true unless previous
2052
+
2053
+ !parse_list_marker(previous.chomp)
2054
+ end
2055
+
2056
+ def open_fence_in_lines?(lines)
2057
+ opener = nil
2058
+
2059
+ lines.each do |line|
2060
+ next if blank_line?(line)
2061
+
2062
+ if opener
2063
+ opener = nil if fence_closer?(line, opener[:char], opener[:length])
2064
+ else
2065
+ opener = parse_fence_opener(line)
2066
+ end
2067
+ end
2068
+
2069
+ !opener.nil?
2070
+ end
2071
+
2072
+ def each_char_compat(text)
2073
+ if text.respond_to?(:each_char)
2074
+ text.each_char { |char| yield char }
2075
+ else
2076
+ text.scan(/./m) { |char| yield char }
2077
+ end
2078
+ end
2079
+
2080
+ def ascii_only_compat?(text)
2081
+ if text.respond_to?(:ascii_only?)
2082
+ text.ascii_only?
2083
+ else
2084
+ text.to_s.unpack('C*').all? { |byte| byte < 128 }
2085
+ end
2086
+ end
2087
+
2088
+ def utf8_bytes(char)
2089
+ if defined?(Encoding)
2090
+ char.encode(Encoding::UTF_8).unpack('C*')
2091
+ else
2092
+ [char[0]].pack('U').unpack('C*')
2093
+ end
2094
+ end
2095
+
2096
+ def unicode_casefold_compat(text)
2097
+ codepoints = text.to_s.unpack('U*')
2098
+ folded = String.new
2099
+
2100
+ codepoints.each do |codepoint|
2101
+ append_folded_codepoint(folded, codepoint)
2102
+ end
2103
+
2104
+ folded
2105
+ end
2106
+
2107
+ def append_folded_codepoint(buffer, codepoint)
2108
+ case codepoint
2109
+ when 0x41..0x5A
2110
+ buffer << [codepoint + 32].pack('U')
2111
+ when 0x0391..0x03A1
2112
+ buffer << [codepoint + 32].pack('U')
2113
+ when 0x03A3..0x03AB
2114
+ buffer << [codepoint + 32].pack('U')
2115
+ when 0x03C2
2116
+ buffer << [0x03C3].pack('U')
2117
+ when 0x00DF, 0x1E9E
2118
+ buffer << 'ss'
2119
+ else
2120
+ begin
2121
+ buffer << [codepoint].pack('U').downcase
2122
+ rescue StandardError
2123
+ buffer << [codepoint].pack('U')
2124
+ end
2125
+ end
2126
+ end
2127
+ end
2128
+ end
2129
+ end
2130
+ end
2131
+ end