kramdown 0.14.2 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of kramdown might be problematic. Click here for more details.

Files changed (323) hide show
  1. checksums.yaml +7 -0
  2. data/CONTRIBUTERS +63 -1
  3. data/COPYING +17 -11
  4. data/README.md +35 -14
  5. data/VERSION +1 -1
  6. data/bin/kramdown +92 -40
  7. data/data/kramdown/document.html +4 -0
  8. data/data/kramdown/document.latex +7 -0
  9. data/lib/kramdown.rb +3 -16
  10. data/lib/kramdown/converter.rb +42 -16
  11. data/lib/kramdown/converter/base.rb +102 -38
  12. data/lib/kramdown/converter/hash_ast.rb +38 -0
  13. data/lib/kramdown/converter/html.rb +232 -141
  14. data/lib/kramdown/converter/kramdown.rb +122 -104
  15. data/lib/kramdown/converter/latex.rb +95 -78
  16. data/lib/kramdown/converter/man.rb +300 -0
  17. data/lib/kramdown/converter/math_engine/mathjax.rb +32 -0
  18. data/lib/kramdown/converter/remove_html_tags.rb +8 -17
  19. data/lib/kramdown/converter/syntax_highlighter.rb +56 -0
  20. data/lib/kramdown/converter/syntax_highlighter/minted.rb +35 -0
  21. data/lib/kramdown/converter/syntax_highlighter/rouge.rb +85 -0
  22. data/lib/kramdown/converter/toc.rb +7 -20
  23. data/lib/kramdown/document.rb +30 -37
  24. data/lib/kramdown/element.rb +54 -27
  25. data/lib/kramdown/error.rb +3 -16
  26. data/lib/kramdown/options.rb +392 -247
  27. data/lib/kramdown/parser.rb +3 -16
  28. data/lib/kramdown/parser/base.rb +28 -33
  29. data/lib/kramdown/parser/html.rb +151 -119
  30. data/lib/kramdown/parser/kramdown.rb +87 -50
  31. data/lib/kramdown/parser/kramdown/abbreviation.rb +33 -27
  32. data/lib/kramdown/parser/kramdown/autolink.rb +7 -25
  33. data/lib/kramdown/parser/kramdown/blank_line.rb +6 -19
  34. data/lib/kramdown/parser/kramdown/block_boundary.rb +6 -18
  35. data/lib/kramdown/parser/kramdown/blockquote.rb +6 -19
  36. data/lib/kramdown/parser/kramdown/codeblock.rb +15 -24
  37. data/lib/kramdown/parser/kramdown/codespan.rb +20 -22
  38. data/lib/kramdown/parser/kramdown/emphasis.rb +15 -24
  39. data/lib/kramdown/parser/kramdown/eob.rb +3 -16
  40. data/lib/kramdown/parser/kramdown/escaped_chars.rb +3 -16
  41. data/lib/kramdown/parser/kramdown/extensions.rb +66 -56
  42. data/lib/kramdown/parser/kramdown/footnote.rb +21 -31
  43. data/lib/kramdown/parser/kramdown/header.rb +37 -37
  44. data/lib/kramdown/parser/kramdown/horizontal_rule.rb +5 -17
  45. data/lib/kramdown/parser/kramdown/html.rb +47 -56
  46. data/lib/kramdown/parser/kramdown/html_entity.rb +9 -19
  47. data/lib/kramdown/parser/kramdown/line_break.rb +4 -17
  48. data/lib/kramdown/parser/kramdown/link.rb +39 -38
  49. data/lib/kramdown/parser/kramdown/list.rb +124 -82
  50. data/lib/kramdown/parser/kramdown/math.rb +12 -24
  51. data/lib/kramdown/parser/kramdown/paragraph.rb +23 -24
  52. data/lib/kramdown/parser/kramdown/smart_quotes.rb +26 -66
  53. data/lib/kramdown/parser/kramdown/table.rb +41 -48
  54. data/lib/kramdown/parser/kramdown/typographic_symbol.rb +14 -22
  55. data/lib/kramdown/parser/markdown.rb +11 -23
  56. data/lib/kramdown/utils.rb +21 -18
  57. data/lib/kramdown/utils/configurable.rb +45 -0
  58. data/lib/kramdown/utils/entities.rb +287 -292
  59. data/lib/kramdown/utils/html.rb +27 -30
  60. data/lib/kramdown/utils/lru_cache.rb +41 -0
  61. data/lib/kramdown/utils/string_scanner.rb +81 -0
  62. data/lib/kramdown/utils/unidecoder.rb +50 -0
  63. data/lib/kramdown/version.rb +4 -17
  64. data/man/man1/kramdown.1 +340 -347
  65. data/test/run_tests.rb +7 -20
  66. data/test/test_files.rb +188 -100
  67. data/test/test_location.rb +216 -0
  68. data/test/test_string_scanner_kramdown.rb +27 -0
  69. data/test/testcases/block/03_paragraph/indented.html.gfm +18 -0
  70. data/test/testcases/block/03_paragraph/line_break_last_line.html +9 -0
  71. data/test/testcases/block/03_paragraph/line_break_last_line.text +9 -0
  72. data/test/testcases/block/03_paragraph/standalone_image.html +8 -0
  73. data/test/testcases/block/03_paragraph/standalone_image.text +6 -0
  74. data/test/testcases/block/03_paragraph/with_html_to_native.html +1 -0
  75. data/test/testcases/block/03_paragraph/with_html_to_native.options +1 -0
  76. data/test/testcases/block/03_paragraph/with_html_to_native.text +1 -0
  77. data/test/testcases/block/04_header/atx_header.html +15 -1
  78. data/test/testcases/block/04_header/atx_header.text +14 -1
  79. data/test/testcases/block/04_header/setext_header.html +3 -1
  80. data/test/testcases/block/04_header/setext_header.text +4 -1
  81. data/test/testcases/block/04_header/with_auto_id_stripping.html +1 -0
  82. data/test/testcases/block/04_header/with_auto_id_stripping.options +1 -0
  83. data/test/testcases/block/04_header/with_auto_id_stripping.text +1 -0
  84. data/test/testcases/block/04_header/with_auto_ids.html +2 -0
  85. data/test/testcases/block/04_header/with_auto_ids.options +1 -0
  86. data/test/testcases/block/04_header/with_auto_ids.text +2 -0
  87. data/test/testcases/block/06_codeblock/guess_lang_css_class.html +15 -0
  88. data/test/testcases/block/06_codeblock/guess_lang_css_class.options +2 -0
  89. data/test/testcases/block/06_codeblock/guess_lang_css_class.text +13 -0
  90. data/test/testcases/block/06_codeblock/highlighting-minted-with-opts.latex +9 -0
  91. data/test/testcases/block/06_codeblock/highlighting-minted-with-opts.options +4 -0
  92. data/test/testcases/block/06_codeblock/highlighting-minted-with-opts.text +5 -0
  93. data/test/testcases/block/06_codeblock/highlighting-minted.latex +8 -0
  94. data/test/testcases/block/06_codeblock/highlighting-minted.options +3 -0
  95. data/test/testcases/block/06_codeblock/highlighting-minted.text +4 -0
  96. data/test/testcases/block/06_codeblock/highlighting-opts.html +6 -0
  97. data/test/testcases/block/06_codeblock/highlighting-opts.options +7 -0
  98. data/test/testcases/block/06_codeblock/highlighting-opts.text +4 -0
  99. data/test/testcases/block/06_codeblock/highlighting.html +5 -6
  100. data/test/testcases/block/06_codeblock/issue_gh45.html +164 -0
  101. data/test/testcases/block/06_codeblock/issue_gh45.test +188 -0
  102. data/test/testcases/block/06_codeblock/rouge/disabled.html +2 -0
  103. data/test/testcases/block/06_codeblock/rouge/disabled.options +4 -0
  104. data/test/testcases/block/06_codeblock/rouge/disabled.text +1 -0
  105. data/test/testcases/block/06_codeblock/rouge/multiple.html +11 -0
  106. data/test/testcases/block/06_codeblock/rouge/multiple.options +4 -0
  107. data/test/testcases/block/06_codeblock/rouge/multiple.text +11 -0
  108. data/test/testcases/block/06_codeblock/rouge/simple.html +10 -0
  109. data/test/testcases/block/06_codeblock/rouge/simple.options +3 -0
  110. data/test/testcases/block/06_codeblock/rouge/simple.text +9 -0
  111. data/test/testcases/block/06_codeblock/with_lang_in_fenced_block.options +1 -1
  112. data/test/testcases/block/06_codeblock/with_lang_in_fenced_block_any_char.html +8 -0
  113. data/test/testcases/block/06_codeblock/with_lang_in_fenced_block_any_char.options +2 -0
  114. data/test/testcases/block/06_codeblock/with_lang_in_fenced_block_any_char.text +11 -0
  115. data/test/testcases/block/06_codeblock/with_lang_in_fenced_block_name_with_dash.html +3 -0
  116. data/test/testcases/block/06_codeblock/with_lang_in_fenced_block_name_with_dash.options +2 -0
  117. data/test/testcases/block/06_codeblock/with_lang_in_fenced_block_name_with_dash.text +4 -0
  118. data/test/testcases/block/07_horizontal_rule/error.html +2 -2
  119. data/test/testcases/block/07_horizontal_rule/normal.html +2 -0
  120. data/test/testcases/block/07_horizontal_rule/normal.text +3 -0
  121. data/test/testcases/block/08_list/brackets_in_item.latex +3 -0
  122. data/test/testcases/block/08_list/brackets_in_item.text +1 -0
  123. data/test/testcases/block/08_list/lazy_and_nested.html +9 -0
  124. data/test/testcases/block/08_list/lazy_and_nested.text +4 -0
  125. data/test/testcases/block/09_html/html5_attributes.html +2 -0
  126. data/test/testcases/block/09_html/html5_attributes.text +2 -0
  127. data/test/testcases/block/09_html/html_after_block.html +7 -0
  128. data/test/testcases/block/09_html/html_after_block.text +5 -0
  129. data/test/testcases/block/09_html/html_to_native/table_simple.html +13 -0
  130. data/test/testcases/block/09_html/html_to_native/table_simple.text +15 -0
  131. data/test/testcases/block/09_html/html_to_native/typography.html +1 -1
  132. data/test/testcases/block/09_html/not_parsed.html +1 -1
  133. data/test/testcases/block/09_html/processing_instruction.html +5 -6
  134. data/test/testcases/block/09_html/simple.html +1 -5
  135. data/test/testcases/block/09_html/simple.text +1 -5
  136. data/test/testcases/block/09_html/standalone_image_in_div.htmlinput +7 -0
  137. data/test/testcases/block/09_html/standalone_image_in_div.text +8 -0
  138. data/test/testcases/block/09_html/textarea.html +8 -0
  139. data/test/testcases/block/09_html/textarea.text +8 -0
  140. data/test/testcases/block/09_html/xml.html +8 -0
  141. data/test/testcases/block/09_html/xml.text +7 -0
  142. data/test/testcases/block/11_ial/simple.html +5 -1
  143. data/test/testcases/block/11_ial/simple.text +8 -1
  144. data/test/testcases/block/12_extension/options.html +4 -4
  145. data/test/testcases/block/12_extension/options.text +2 -0
  146. data/test/testcases/block/12_extension/options2.html +4 -4
  147. data/test/testcases/block/12_extension/options3.html +7 -6
  148. data/test/testcases/block/12_extension/options3.text +2 -2
  149. data/test/testcases/block/13_definition_list/auto_ids.html +15 -0
  150. data/test/testcases/block/13_definition_list/auto_ids.text +18 -0
  151. data/test/testcases/block/13_definition_list/item_ial.html +5 -0
  152. data/test/testcases/block/13_definition_list/item_ial.text +8 -0
  153. data/test/testcases/block/14_table/empty_tag_in_cell.html +8 -0
  154. data/test/testcases/block/14_table/empty_tag_in_cell.options +1 -0
  155. data/test/testcases/block/14_table/empty_tag_in_cell.text +1 -0
  156. data/test/testcases/block/14_table/errors.html +4 -0
  157. data/test/testcases/block/14_table/errors.text +4 -0
  158. data/test/testcases/block/14_table/header.html +21 -0
  159. data/test/testcases/block/14_table/header.text +7 -0
  160. data/test/testcases/block/14_table/simple.html +22 -7
  161. data/test/testcases/block/14_table/simple.text +4 -0
  162. data/test/testcases/block/14_table/table_with_footnote.html +4 -4
  163. data/test/testcases/block/15_math/gh_128.html +1 -0
  164. data/test/testcases/block/15_math/gh_128.text +1 -0
  165. data/test/testcases/block/15_math/no_engine.html +3 -0
  166. data/test/testcases/block/15_math/no_engine.options +1 -0
  167. data/test/testcases/block/15_math/no_engine.text +2 -0
  168. data/test/testcases/block/15_math/normal.html +17 -14
  169. data/test/testcases/block/15_math/normal.text +2 -0
  170. data/test/testcases/block/16_toc/toc_exclude.html +7 -7
  171. data/test/testcases/block/16_toc/toc_levels.html +5 -5
  172. data/test/testcases/block/16_toc/toc_levels.text +1 -1
  173. data/test/testcases/block/16_toc/toc_with_footnotes.html +5 -5
  174. data/test/testcases/block/16_toc/toc_with_links.html +8 -0
  175. data/test/testcases/block/16_toc/toc_with_links.options +2 -0
  176. data/test/testcases/block/16_toc/toc_with_links.text +8 -0
  177. data/test/testcases/cjk-line-break.html +4 -0
  178. data/test/testcases/cjk-line-break.options +1 -0
  179. data/test/testcases/cjk-line-break.text +12 -0
  180. data/test/testcases/man/example.man +123 -0
  181. data/test/testcases/man/example.text +85 -0
  182. data/test/testcases/man/heading-name-dash-description.man +4 -0
  183. data/test/testcases/man/heading-name-dash-description.text +1 -0
  184. data/test/testcases/man/heading-name-description.man +4 -0
  185. data/test/testcases/man/heading-name-description.text +2 -0
  186. data/test/testcases/man/heading-name-section-description.man +4 -0
  187. data/test/testcases/man/heading-name-section-description.text +1 -0
  188. data/test/testcases/man/heading-name-section.man +2 -0
  189. data/test/testcases/man/heading-name-section.text +1 -0
  190. data/test/testcases/man/heading-name.man +2 -0
  191. data/test/testcases/man/heading-name.text +1 -0
  192. data/test/testcases/man/sections.man +4 -0
  193. data/test/testcases/man/sections.text +11 -0
  194. data/test/testcases/man/text-escaping.man +8 -0
  195. data/test/testcases/man/text-escaping.text +7 -0
  196. data/test/testcases/span/01_link/empty.html +1 -1
  197. data/test/testcases/span/01_link/empty_title.htmlinput +3 -0
  198. data/test/testcases/span/01_link/empty_title.text +7 -0
  199. data/test/testcases/span/01_link/imagelinks.html +1 -0
  200. data/test/testcases/span/01_link/imagelinks.text +2 -0
  201. data/test/testcases/span/01_link/inline.html +1 -1
  202. data/test/testcases/span/01_link/latex_escaping.latex +6 -0
  203. data/test/testcases/span/01_link/latex_escaping.text +5 -0
  204. data/test/testcases/span/01_link/link_defs.html +1 -1
  205. data/test/testcases/span/01_link/link_defs.text +2 -1
  206. data/test/testcases/span/01_link/link_defs_with_ial.html +4 -0
  207. data/test/testcases/span/01_link/link_defs_with_ial.text +16 -0
  208. data/test/testcases/span/01_link/reference.html +3 -3
  209. data/test/testcases/span/02_emphasis/nesting.html +3 -0
  210. data/test/testcases/span/02_emphasis/nesting.text +4 -1
  211. data/test/testcases/span/02_emphasis/normal.html +19 -0
  212. data/test/testcases/span/02_emphasis/normal.options +1 -0
  213. data/test/testcases/span/02_emphasis/normal.text +17 -0
  214. data/test/testcases/span/03_codespan/highlighting-minted.latex +2 -0
  215. data/test/testcases/span/03_codespan/highlighting-minted.options +1 -0
  216. data/test/testcases/span/03_codespan/highlighting-minted.text +1 -0
  217. data/test/testcases/span/03_codespan/highlighting.html +1 -1
  218. data/test/testcases/span/03_codespan/normal-css-class.html +1 -0
  219. data/test/testcases/span/03_codespan/normal-css-class.options +2 -0
  220. data/test/testcases/span/03_codespan/normal-css-class.text +1 -0
  221. data/test/testcases/span/03_codespan/rouge/disabled.html +1 -0
  222. data/test/testcases/span/03_codespan/rouge/disabled.options +4 -0
  223. data/test/testcases/span/03_codespan/rouge/disabled.text +1 -0
  224. data/test/testcases/span/03_codespan/rouge/simple.html +1 -0
  225. data/test/testcases/span/03_codespan/rouge/simple.options +1 -0
  226. data/test/testcases/span/03_codespan/rouge/simple.text +1 -0
  227. data/test/testcases/span/04_footnote/backlink_inline.html +79 -0
  228. data/test/testcases/span/04_footnote/backlink_inline.options +1 -0
  229. data/test/testcases/span/04_footnote/backlink_inline.text +38 -0
  230. data/test/testcases/span/04_footnote/backlink_text.html +9 -0
  231. data/test/testcases/span/04_footnote/backlink_text.options +1 -0
  232. data/test/testcases/span/04_footnote/backlink_text.text +3 -0
  233. data/test/testcases/span/04_footnote/definitions.latex +2 -2
  234. data/test/testcases/span/04_footnote/footnote_nr.html +6 -6
  235. data/test/testcases/span/04_footnote/footnote_prefix.html +12 -0
  236. data/test/testcases/span/04_footnote/footnote_prefix.options +1 -0
  237. data/test/testcases/span/04_footnote/footnote_prefix.text +4 -0
  238. data/test/testcases/span/04_footnote/inside_footnote.html +17 -0
  239. data/test/testcases/span/04_footnote/inside_footnote.text +9 -0
  240. data/test/testcases/span/04_footnote/markers.html +16 -16
  241. data/test/testcases/span/04_footnote/markers.latex +3 -3
  242. data/test/testcases/span/04_footnote/markers.options +2 -0
  243. data/test/testcases/span/04_footnote/markers.text +2 -1
  244. data/test/testcases/span/04_footnote/placement.html +11 -0
  245. data/test/testcases/span/04_footnote/placement.options +1 -0
  246. data/test/testcases/span/04_footnote/placement.text +8 -0
  247. data/test/testcases/span/04_footnote/regexp_problem.html +14 -0
  248. data/test/testcases/span/04_footnote/regexp_problem.options +2 -0
  249. data/test/testcases/span/04_footnote/regexp_problem.text +52 -0
  250. data/test/testcases/span/04_footnote/without_backlink.html +9 -0
  251. data/test/testcases/span/04_footnote/without_backlink.options +1 -0
  252. data/test/testcases/span/04_footnote/without_backlink.text +3 -0
  253. data/test/testcases/span/05_html/button.html +7 -0
  254. data/test/testcases/span/05_html/button.text +7 -0
  255. data/test/testcases/span/05_html/mark_element.html +3 -0
  256. data/test/testcases/span/05_html/mark_element.text +3 -0
  257. data/test/testcases/span/05_html/normal.html +10 -1
  258. data/test/testcases/span/05_html/normal.text +9 -0
  259. data/test/testcases/span/05_html/raw_span_elements.html +2 -0
  260. data/test/testcases/span/05_html/raw_span_elements.text +2 -0
  261. data/test/testcases/span/05_html/xml.html +5 -0
  262. data/test/testcases/span/05_html/xml.text +5 -0
  263. data/test/testcases/span/abbreviations/abbrev.html +14 -1
  264. data/test/testcases/span/abbreviations/abbrev.text +18 -2
  265. data/test/testcases/span/abbreviations/in_footnote.html +9 -0
  266. data/test/testcases/span/abbreviations/in_footnote.text +5 -0
  267. data/test/testcases/span/autolinks/url_links.html +5 -4
  268. data/test/testcases/span/autolinks/url_links.text +1 -0
  269. data/test/testcases/span/line_breaks/normal.html +2 -2
  270. data/test/testcases/span/line_breaks/normal.latex +2 -2
  271. data/test/testcases/span/math/no_engine.html +1 -0
  272. data/test/testcases/span/math/no_engine.options +1 -0
  273. data/test/testcases/span/math/no_engine.text +1 -0
  274. data/test/testcases/span/math/normal.html +4 -3
  275. data/test/testcases/span/math/normal.text +2 -1
  276. data/test/testcases/span/text_substitutions/entities_as_char.html +1 -1
  277. data/test/testcases/span/text_substitutions/entities_as_char.options +1 -0
  278. data/test/testcases/span/text_substitutions/entities_as_char.text +1 -1
  279. data/test/testcases/span/text_substitutions/typography.html +22 -0
  280. data/test/testcases/span/text_substitutions/typography.text +22 -0
  281. data/test/testcases/span/text_substitutions/typography_subst.html +3 -0
  282. data/test/testcases/span/text_substitutions/typography_subst.latex +4 -0
  283. data/test/testcases/span/text_substitutions/typography_subst.options +8 -0
  284. data/test/testcases/span/text_substitutions/typography_subst.text +3 -0
  285. metadata +218 -67
  286. data/ChangeLog +0 -7436
  287. data/GPL +0 -674
  288. data/Rakefile +0 -306
  289. data/benchmark/benchmark.rb +0 -36
  290. data/benchmark/benchmark.sh +0 -74
  291. data/benchmark/generate_data.rb +0 -119
  292. data/benchmark/mdbasics.text +0 -306
  293. data/benchmark/mdsyntax.text +0 -888
  294. data/benchmark/testing.sh +0 -9
  295. data/benchmark/timing.sh +0 -10
  296. data/doc/bg.png +0 -0
  297. data/doc/default.scss.css +0 -181
  298. data/doc/default.template +0 -68
  299. data/doc/design.scss.css +0 -441
  300. data/doc/documentation.page +0 -84
  301. data/doc/documentation.template +0 -20
  302. data/doc/index.page +0 -94
  303. data/doc/installation.page +0 -88
  304. data/doc/links.markdown +0 -6
  305. data/doc/metainfo +0 -3
  306. data/doc/news.feed +0 -10
  307. data/doc/news.page +0 -29
  308. data/doc/options.page +0 -10
  309. data/doc/quickref.page +0 -598
  310. data/doc/sidebar.template +0 -21
  311. data/doc/syntax.page +0 -1700
  312. data/doc/tests.page +0 -91
  313. data/doc/virtual +0 -2
  314. data/lib/kramdown/compatibility.rb +0 -49
  315. data/lib/kramdown/utils/ordered_hash.rb +0 -100
  316. data/setup.rb +0 -1585
  317. data/test/testcases/block/07_horizontal_rule/error.html.19 +0 -7
  318. data/test/testcases/block/09_html/html_to_native/typography.html.19 +0 -1
  319. data/test/testcases/block/09_html/simple.html.19 +0 -64
  320. data/test/testcases/block/14_table/simple.html.19 +0 -177
  321. data/test/testcases/span/01_link/inline.html.19 +0 -46
  322. data/test/testcases/span/01_link/reference.html.19 +0 -37
  323. data/test/testcases/span/text_substitutions/entities_as_char.html.19 +0 -1
@@ -1,22 +1,9 @@
1
- # -*- coding: utf-8 -*-
1
+ # -*- coding: utf-8; frozen_string_literal: true -*-
2
2
  #
3
3
  #--
4
- # Copyright (C) 2009-2012 Thomas Leitner <t_leitner@gmx.at>
4
+ # Copyright (C) 2009-2019 Thomas Leitner <t_leitner@gmx.at>
5
5
  #
6
- # This file is part of kramdown.
7
- #
8
- # kramdown is free software: you can redistribute it and/or modify
9
- # it under the terms of the GNU General Public License as published by
10
- # the Free Software Foundation, either version 3 of the License, or
11
- # (at your option) any later version.
12
- #
13
- # This program is distributed in the hope that it will be useful,
14
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
- # GNU General Public License for more details.
17
- #
18
- # You should have received a copy of the GNU General Public License
19
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
6
+ # This file is part of kramdown which is licensed under the MIT.
20
7
  #++
21
8
  #
22
9
 
@@ -28,8 +15,8 @@ module Kramdown
28
15
 
29
16
  # Parse the line break at the current location.
30
17
  def parse_line_break
18
+ @tree.children << Element.new(:br, nil, nil, location: @src.current_line_number)
31
19
  @src.pos += @src.matched_size
32
- @tree.children << Element.new(:br)
33
20
  end
34
21
  define_parser(:line_break, LINE_BREAK, '( |\\\\)(?=\n)')
35
22
 
@@ -1,51 +1,44 @@
1
- # -*- coding: utf-8 -*-
1
+ # -*- coding: utf-8; frozen_string_literal: true -*-
2
2
  #
3
3
  #--
4
- # Copyright (C) 2009-2012 Thomas Leitner <t_leitner@gmx.at>
4
+ # Copyright (C) 2009-2019 Thomas Leitner <t_leitner@gmx.at>
5
5
  #
6
- # This file is part of kramdown.
7
- #
8
- # kramdown is free software: you can redistribute it and/or modify
9
- # it under the terms of the GNU General Public License as published by
10
- # the Free Software Foundation, either version 3 of the License, or
11
- # (at your option) any later version.
12
- #
13
- # This program is distributed in the hope that it will be useful,
14
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
- # GNU General Public License for more details.
17
- #
18
- # You should have received a copy of the GNU General Public License
19
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
6
+ # This file is part of kramdown which is licensed under the MIT.
20
7
  #++
21
8
  #
22
9
 
10
+ require 'kramdown/parser/kramdown/escaped_chars'
11
+
23
12
  module Kramdown
24
13
  module Parser
25
14
  class Kramdown
26
15
 
27
16
  # Normalize the link identifier.
28
17
  def normalize_link_id(id)
29
- id.gsub(/(\s|\n)+/, ' ').downcase
18
+ id.gsub(/[\s]+/, ' ').downcase
30
19
  end
31
20
 
32
- LINK_DEFINITION_START = /^#{OPT_SPACE}\[([^\n\]]+)\]:[ \t]*(?:<(.*?)>|([^'"\n]*?\S[^'"\n]*?))[ \t]*?(?:\n?[ \t]*?(["'])(.+?)\4[ \t]*?)?\n/
21
+ LINK_DEFINITION_START = /^#{OPT_SPACE}\[([^\n\]]+)\]:[ \t]*(?:<(.*?)>|([^\n]*?\S[^\n]*?))(?:(?:[ \t]*?\n|[ \t]+?)[ \t]*?(["'])(.+?)\4)?[ \t]*?\n/
33
22
 
34
23
  # Parse the link definition at the current location.
35
24
  def parse_link_definition
25
+ return false if @src[3].to_s =~ /[ \t]+["']/
36
26
  @src.pos += @src.matched_size
37
27
  link_id, link_url, link_title = normalize_link_id(@src[1]), @src[2] || @src[3], @src[5]
38
- warning("Duplicate link ID '#{link_id}' - overwriting") if @link_defs[link_id]
39
- @link_defs[link_id] = [link_url, link_title]
40
- @tree.children << Element.new(:eob, :link_def)
28
+ if @link_defs[link_id]
29
+ warning("Duplicate link ID '#{link_id}' on line #{@src.current_line_number} - overwriting")
30
+ end
31
+ @tree.children << new_block_el(:eob, :link_def)
32
+ @link_defs[link_id] = [link_url, link_title, @tree.children.last]
41
33
  true
42
34
  end
43
35
  define_parser(:link_definition, LINK_DEFINITION_START)
44
36
 
45
-
46
37
  # This helper methods adds the approriate attributes to the element +el+ of type +a+ or +img+
47
38
  # and the element itself to the @tree.
48
- def add_link(el, href, title, alt_text = nil)
39
+ def add_link(el, href, title, alt_text = nil, ial = nil)
40
+ el.options[:ial] = ial
41
+ update_attr_with_ial(el.attr, ial) if ial
49
42
  if el.type == :a
50
43
  el.attr['href'] = href
51
44
  else
@@ -66,39 +59,47 @@ module Kramdown
66
59
  # Parse the link at the current scanner position. This method is used to parse normal links as
67
60
  # well as image links.
68
61
  def parse_link
62
+ start_line_number = @src.current_line_number
69
63
  result = @src.scan(LINK_START)
70
- reset_pos = @src.pos
64
+ cur_pos = @src.pos
65
+ saved_pos = @src.save_pos
71
66
 
72
67
  link_type = (result =~ /^!/ ? :img : :a)
73
68
 
74
69
  # no nested links allowed
75
- if link_type == :a && (@tree.type == :img || @tree.type == :a || @stack.any? {|t,s| t && (t.type == :img || t.type == :a)})
70
+ if link_type == :a && (@tree.type == :img || @tree.type == :a ||
71
+ @stack.any? {|t, _| t && (t.type == :img || t.type == :a) })
76
72
  add_text(result)
77
73
  return
78
74
  end
79
- el = Element.new(link_type)
75
+ el = Element.new(link_type, nil, nil, location: start_line_number)
80
76
 
81
77
  count = 1
82
78
  found = parse_spans(el, LINK_BRACKET_STOP_RE) do
83
- count = count + (@src[1] ? -1 : 1)
84
- count - el.children.select {|c| c.type == :img}.size == 0
79
+ count += (@src[1] ? -1 : 1)
80
+ count - el.children.select {|c| c.type == :img }.size == 0
85
81
  end
86
- if !found || (link_type == :a && el.children.empty?)
87
- @src.pos = reset_pos
82
+ unless found
83
+ @src.revert_pos(saved_pos)
88
84
  add_text(result)
89
85
  return
90
86
  end
91
- alt_text = extract_string(reset_pos...@src.pos, @src)
87
+ alt_text = extract_string(cur_pos...@src.pos, @src).gsub(ESCAPED_CHARS, '\1')
92
88
  @src.scan(LINK_BRACKET_STOP_RE)
93
89
 
94
90
  # reference style link or no link url
95
91
  if @src.scan(LINK_INLINE_ID_RE) || !@src.check(/\(/)
92
+ emit_warning = !@src[1]
96
93
  link_id = normalize_link_id(@src[1] || alt_text)
97
- if @link_defs.has_key?(link_id)
98
- add_link(el, @link_defs[link_id].first, @link_defs[link_id].last, alt_text)
94
+ if @link_defs.key?(link_id)
95
+ link_def = @link_defs[link_id]
96
+ add_link(el, link_def[0], link_def[1], alt_text,
97
+ link_def[2] && link_def[2].options[:ial])
99
98
  else
100
- warning("No link definition for link ID '#{link_id}' found")
101
- @src.pos = reset_pos
99
+ if emit_warning
100
+ warning("No link definition for link ID '#{link_id}' found on line #{start_line_number}")
101
+ end
102
+ @src.revert_pos(saved_pos)
102
103
  add_text(result)
103
104
  end
104
105
  return
@@ -112,9 +113,9 @@ module Kramdown
112
113
  return
113
114
  end
114
115
  else
115
- link_url = ''
116
+ link_url = +''
116
117
  nr_of_brackets = 0
117
- while temp = @src.scan_until(LINK_PAREN_STOP_RE)
118
+ while (temp = @src.scan_until(LINK_PAREN_STOP_RE))
118
119
  link_url << temp
119
120
  if @src[2]
120
121
  nr_of_brackets -= 1
@@ -137,7 +138,7 @@ module Kramdown
137
138
  if @src.scan(LINK_INLINE_TITLE_RE)
138
139
  add_link(el, link_url, @src[2], alt_text)
139
140
  else
140
- @src.pos = reset_pos
141
+ @src.revert_pos(saved_pos)
141
142
  add_text(result)
142
143
  end
143
144
  end
@@ -1,22 +1,9 @@
1
- # -*- coding: utf-8 -*-
1
+ # -*- coding: utf-8; frozen_string_literal: true -*-
2
2
  #
3
3
  #--
4
- # Copyright (C) 2009-2012 Thomas Leitner <t_leitner@gmx.at>
4
+ # Copyright (C) 2009-2019 Thomas Leitner <t_leitner@gmx.at>
5
5
  #
6
- # This file is part of kramdown.
7
- #
8
- # kramdown is free software: you can redistribute it and/or modify
9
- # it under the terms of the GNU General Public License as published by
10
- # the Free Software Foundation, either version 3 of the License, or
11
- # (at your option) any later version.
12
- #
13
- # This program is distributed in the hope that it will be useful,
14
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
- # GNU General Public License for more details.
17
- #
18
- # You should have received a copy of the GNU General Public License
19
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
6
+ # This file is part of kramdown which is licensed under the MIT.
20
7
  #++
21
8
  #
22
9
 
@@ -32,6 +19,14 @@ module Kramdown
32
19
  LIST_ITEM_IAL = /^\s*(?:\{:(?!(?:#{ALD_ID_NAME})?:|\/)(#{ALD_ANY_CHARS}+)\})\s*/
33
20
  LIST_ITEM_IAL_CHECK = /^#{LIST_ITEM_IAL}?\s*\n/
34
21
 
22
+ PARSE_FIRST_LIST_LINE_REGEXP_CACHE = Hash.new do |h, indentation|
23
+ indent_re = /^ {#{indentation}}/
24
+ content_re = /^(?:(?:\t| {4}){#{indentation / 4}} {#{indentation % 4}}|(?:\t| {4}){#{indentation / 4 + 1}}).*\S.*\n/
25
+ lazy_re = /(?!^ {0,#{[indentation, 3].min}}(?:#{IAL_BLOCK}|#{LAZY_END_HTML_STOP}|#{LAZY_END_HTML_START})).*\S.*\n/
26
+
27
+ h[indentation] = [content_re, lazy_re, indent_re]
28
+ end
29
+
35
30
  # Used for parsing the first line of a list item or a definition, i.e. the line with list item
36
31
  # marker or the definition marker.
37
32
  def parse_first_list_line(indentation, content)
@@ -40,66 +35,69 @@ module Kramdown
40
35
  else
41
36
  while content =~ /^ *\t/
42
37
  temp = content.scan(/^ */).first.length + indentation
43
- content.sub!(/^( *)(\t+)/) {$1 << " "*(4 - (temp % 4) + ($2.length - 1)*4)}
38
+ content.sub!(/^( *)(\t+)/) { $1 << " " * (4 - (temp % 4) + ($2.length - 1) * 4) }
44
39
  end
45
- indentation += content.scan(/^ */).first.length
40
+ indentation += content[/^ */].length
46
41
  end
47
42
  content.sub!(/^\s*/, '')
48
43
 
49
- indent_re = /^ {#{indentation}}/
50
- content_re = /^(?:(?:\t| {4}){#{indentation / 4}} {#{indentation % 4}}|(?:\t| {4}){#{indentation / 4 + 1}}).*\S.*\n/
51
- lazy_re = /(?!^ {0,#{[indentation, 3].min}}(?:#{IAL_BLOCK}|#{LAZY_END_HTML_STOP}|#{LAZY_END_HTML_START})).*\S.*\n/
52
- [content, indentation, content_re, lazy_re, indent_re]
44
+ [content, indentation, *PARSE_FIRST_LIST_LINE_REGEXP_CACHE[indentation]]
53
45
  end
54
46
 
47
+ PATTERN_TAIL = /[\t| ].*?\n/
55
48
 
56
- LIST_START_UL = /^(#{OPT_SPACE}[+*-])([\t| ].*?\n)/
57
- LIST_START_OL = /^(#{OPT_SPACE}\d+\.)([\t| ].*?\n)/
49
+ LIST_START_UL = /^(#{OPT_SPACE}[+*-])(#{PATTERN_TAIL})/
50
+ LIST_START_OL = /^(#{OPT_SPACE}\d+\.)(#{PATTERN_TAIL})/
58
51
  LIST_START = /#{LIST_START_UL}|#{LIST_START_OL}/
59
52
 
60
53
  # Parse the ordered or unordered list at the current location.
61
54
  def parse_list
55
+ start_line_number = @src.current_line_number
62
56
  type, list_start_re = (@src.check(LIST_START_UL) ? [:ul, LIST_START_UL] : [:ol, LIST_START_OL])
63
- list = new_block_el(type)
57
+ list = new_block_el(type, nil, nil, location: start_line_number)
64
58
 
65
59
  item = nil
66
60
  content_re, lazy_re, indent_re = nil
67
61
  eob_found = false
68
62
  nested_list_found = false
69
63
  last_is_blank = false
70
- while !@src.eos?
64
+ until @src.eos?
65
+ start_line_number = @src.current_line_number
71
66
  if last_is_blank && @src.check(HR_START)
72
67
  break
73
68
  elsif @src.scan(EOB_MARKER)
74
69
  eob_found = true
75
70
  break
76
71
  elsif @src.scan(list_start_re)
77
- item = Element.new(:li)
78
- item.value, indentation, content_re, lazy_re, indent_re = parse_first_list_line(@src[1].length, @src[2])
72
+ item = Element.new(:li, nil, nil, location: start_line_number)
73
+ item.value, indentation, content_re, lazy_re, indent_re =
74
+ parse_first_list_line(@src[1].length, @src[2])
79
75
  list.children << item
80
76
 
81
- item.value.sub!(self.class::LIST_ITEM_IAL) do |match|
77
+ item.value.sub!(self.class::LIST_ITEM_IAL) do
82
78
  parse_attribute_list($1, item.options[:ial] ||= {})
83
79
  ''
84
80
  end
85
81
 
86
- list_start_re = (type == :ul ? /^( {0,#{[3, indentation - 1].min}}[+*-])([\t| ].*?\n)/ :
87
- /^( {0,#{[3, indentation - 1].min}}\d+\.)([\t| ].*?\n)/)
82
+ list_start_re = fetch_pattern(type, indentation)
88
83
  nested_list_found = (item.value =~ LIST_START)
89
84
  last_is_blank = false
85
+ item.value = [item.value]
90
86
  elsif (result = @src.scan(content_re)) || (!last_is_blank && (result = @src.scan(lazy_re)))
91
- result.sub!(/^(\t+)/) { " "*($1 ? 4*$1.length : 0) }
92
- result.sub!(indent_re, '')
93
- if !nested_list_found && result =~ LIST_START
94
- item.value << "^\n"
87
+ result.sub!(/^(\t+)/) { " " * 4 * $1.length }
88
+ indentation_found = result.sub!(indent_re, '')
89
+ if !nested_list_found && indentation_found && result =~ LIST_START
90
+ item.value << +''
95
91
  nested_list_found = true
92
+ elsif nested_list_found && !indentation_found && result =~ LIST_START
93
+ result = " " * (indentation + 4) << result
96
94
  end
97
- item.value << result
95
+ item.value.last << result
98
96
  last_is_blank = false
99
- elsif result = @src.scan(BLANK_LINE)
97
+ elsif (result = @src.scan(BLANK_LINE))
100
98
  nested_list_found = true
101
99
  last_is_blank = true
102
- item.value << result
100
+ item.value.last << result
103
101
  else
104
102
  break
105
103
  end
@@ -109,30 +107,37 @@ module Kramdown
109
107
 
110
108
  last = nil
111
109
  list.children.each do |it|
112
- temp = Element.new(:temp)
113
- parse_blocks(temp, it.value)
110
+ temp = Element.new(:temp, nil, nil, location: it.options[:location])
111
+
112
+ env = save_env
113
+ location = it.options[:location]
114
+ it.value.each do |val|
115
+ @src = ::Kramdown::Utils::StringScanner.new(val, location)
116
+ parse_blocks(temp)
117
+ location = @src.current_line_number
118
+ end
119
+ restore_env(env)
120
+
114
121
  it.children = temp.children
115
122
  it.value = nil
116
- next if it.children.size == 0
123
+
124
+ it_children = it.children
125
+ next if it_children.empty?
117
126
 
118
127
  # Handle the case where an EOB marker is inserted by a block IAL for the first paragraph
119
- it.children.delete_at(1) if it.children.first.type == :p &&
120
- it.children.length >= 2 && it.children[1].type == :eob && it.children.first.options[:ial]
128
+ it_children.delete_at(1) if it_children.first.type == :p &&
129
+ it_children.length >= 2 && it_children[1].type == :eob && it_children.first.options[:ial]
121
130
 
122
- if it.children.first.type == :p &&
123
- (it.children.length < 2 || it.children[1].type != :blank ||
124
- (it == list.children.last && it.children.length == 2 && !eob_found)) &&
131
+ if it_children.first.type == :p &&
132
+ (it_children.length < 2 || it_children[1].type != :blank ||
133
+ (it == list.children.last && it_children.length == 2 && !eob_found)) &&
125
134
  (list.children.last != it || list.children.size == 1 ||
126
- list.children[0..-2].any? {|cit| !cit.children.first || cit.children.first.type != :p || cit.children.first.options[:transparent]})
127
- it.children.first.children.first.value << "\n" if it.children.size > 1 && it.children[1].type != :blank
128
- it.children.first.options[:transparent] = true
135
+ list.children[0..-2].any? {|cit| !cit.children.first || cit.children.first.type != :p || cit.children.first.options[:transparent] })
136
+ it_children.first.children.first.value << "\n" if it_children.size > 1 && it_children[1].type != :blank
137
+ it_children.first.options[:transparent] = true
129
138
  end
130
139
 
131
- if it.children.last.type == :blank
132
- last = it.children.pop
133
- else
134
- last = nil
135
- end
140
+ last = (it_children.last.type == :blank ? it_children.pop : nil)
136
141
  end
137
142
 
138
143
  @tree.children << last if !last.nil? && !eob_found
@@ -141,14 +146,14 @@ module Kramdown
141
146
  end
142
147
  define_parser(:list, LIST_START)
143
148
 
144
-
145
- DEFINITION_LIST_START = /^(#{OPT_SPACE}:)([\t| ].*?\n)/
149
+ DEFINITION_LIST_START = /^(#{OPT_SPACE}:)(#{PATTERN_TAIL})/
146
150
 
147
151
  # Parse the ordered or unordered list at the current location.
148
152
  def parse_definition_list
149
153
  children = @tree.children
150
- if !children.last || (children.length == 1 && children.last.type != :p ) ||
151
- (children.length >= 2 && children[-1].type != :p && (children[-1].type != :blank || children[-1].value != "\n" || children[-2].type != :p))
154
+ if !children.last || (children.length == 1 && children.last.type != :p) ||
155
+ (children.length >= 2 && children[-1].type != :p &&
156
+ (children[-1].type != :blank || children[-1].value != "\n" || children[-2].type != :p))
152
157
  return false
153
158
  end
154
159
 
@@ -159,8 +164,15 @@ module Kramdown
159
164
  para = @tree.children.pop
160
165
  first_as_para = true
161
166
  end
167
+ # take location from preceding para which is the first definition term
168
+ deflist.options[:location] = para.options[:location]
162
169
  para.children.first.value.split(/\n/).each do |term|
163
- el = Element.new(:dt)
170
+ el = Element.new(:dt, nil, nil, location: @src.current_line_number)
171
+ term.sub!(self.class::LIST_ITEM_IAL) do
172
+ parse_attribute_list($1, el.options[:ial] ||= {})
173
+ ''
174
+ end
175
+ el.options[:raw_text] = term
164
176
  el.children << Element.new(:raw_text, term)
165
177
  deflist.children << el
166
178
  end
@@ -170,30 +182,32 @@ module Kramdown
170
182
  content_re, lazy_re, indent_re = nil
171
183
  def_start_re = DEFINITION_LIST_START
172
184
  last_is_blank = false
173
- while !@src.eos?
185
+ until @src.eos?
186
+ start_line_number = @src.current_line_number
174
187
  if @src.scan(def_start_re)
175
- item = Element.new(:dd)
188
+ item = Element.new(:dd, nil, nil, location: start_line_number)
176
189
  item.options[:first_as_para] = first_as_para
177
- item.value, indentation, content_re, lazy_re, indent_re = parse_first_list_line(@src[1].length, @src[2])
190
+ item.value, indentation, content_re, lazy_re, indent_re =
191
+ parse_first_list_line(@src[1].length, @src[2])
178
192
  deflist.children << item
179
193
 
180
- item.value.sub!(self.class::LIST_ITEM_IAL) do |match|
194
+ item.value.sub!(self.class::LIST_ITEM_IAL) do |_match|
181
195
  parse_attribute_list($1, item.options[:ial] ||= {})
182
196
  ''
183
197
  end
184
198
 
185
- def_start_re = /^( {0,#{[3, indentation - 1].min}}:)([\t| ].*?\n)/
199
+ def_start_re = fetch_pattern(:dl, indentation)
186
200
  first_as_para = false
187
201
  last_is_blank = false
188
202
  elsif @src.check(EOB_MARKER)
189
203
  break
190
204
  elsif (result = @src.scan(content_re)) || (!last_is_blank && (result = @src.scan(lazy_re)))
191
- result.sub!(/^(\t+)/) { " "*($1 ? 4*$1.length : 0) }
205
+ result.sub!(/^(\t+)/) { " " * ($1 ? 4 * $1.length : 0) }
192
206
  result.sub!(indent_re, '')
193
207
  item.value << result
194
208
  first_as_para = false
195
209
  last_is_blank = false
196
- elsif result = @src.scan(BLANK_LINE)
210
+ elsif (result = @src.scan(BLANK_LINE))
197
211
  first_as_para = true
198
212
  item.value << result
199
213
  last_is_blank = true
@@ -208,35 +222,63 @@ module Kramdown
208
222
 
209
223
  parse_blocks(it, it.value)
210
224
  it.value = nil
211
- next if it.children.size == 0
225
+ it_children = it.children
226
+ next if it_children.empty?
212
227
 
213
- if it.children.last.type == :blank
214
- last = it.children.pop
215
- else
216
- last = nil
217
- end
228
+ last = (it_children.last.type == :blank ? it_children.pop : nil)
218
229
 
219
- if it.children.first && it.children.first.type == :p && !it.options.delete(:first_as_para)
220
- it.children.first.children.first.value << "\n" if it.children.size > 1
221
- it.children.first.options[:transparent] = true
230
+ if it_children.first && it_children.first.type == :p && !it.options.delete(:first_as_para)
231
+ it_children.first.children.first.value << "\n" if it_children.size > 1
232
+ it_children.first.options[:transparent] = true
222
233
  end
223
234
  end
224
235
 
225
- if @tree.children.length >= 1 && @tree.children.last.type == :dl
226
- @tree.children[-1].children.concat(deflist.children)
227
- elsif @tree.children.length >= 2 && @tree.children[-1].type == :blank && @tree.children[-2].type == :dl
228
- @tree.children.pop
229
- @tree.children[-1].children.concat(deflist.children)
236
+ children = @tree.children
237
+ if children.length >= 1 && children.last.type == :dl
238
+ children[-1].children.concat(deflist.children)
239
+ elsif children.length >= 2 && children[-1].type == :blank &&
240
+ children[-2].type == :dl
241
+ children.pop
242
+ children[-1].children.concat(deflist.children)
230
243
  else
231
- @tree.children << deflist
244
+ children << deflist
232
245
  end
233
246
 
234
- @tree.children << last if !last.nil?
247
+ children << last if last
235
248
 
236
249
  true
237
250
  end
238
251
  define_parser(:definition_list, DEFINITION_LIST_START)
239
252
 
253
+ private
254
+
255
+ # precomputed patterns for indentations 1..4 and fallback expression
256
+ # to compute pattern when indentation is outside the 1..4 range.
257
+ def fetch_pattern(type, indentation)
258
+ if type == :ul
259
+ case indentation
260
+ when 1 then %r/^( {0}[+*-])(#{PATTERN_TAIL})/o
261
+ when 2 then %r/^( {0,1}[+*-])(#{PATTERN_TAIL})/o
262
+ when 3 then %r/^( {0,2}[+*-])(#{PATTERN_TAIL})/o
263
+ else %r/^( {0,3}[+*-])(#{PATTERN_TAIL})/o
264
+ end
265
+ elsif type == :ol
266
+ case indentation
267
+ when 1 then %r/^( {0}\d+\.)(#{PATTERN_TAIL})/o
268
+ when 2 then %r/^( {0,1}\d+\.)(#{PATTERN_TAIL})/o
269
+ when 3 then %r/^( {0,2}\d+\.)(#{PATTERN_TAIL})/o
270
+ else %r/^( {0,3}\d+\.)(#{PATTERN_TAIL})/o
271
+ end
272
+ elsif type == :dl
273
+ case indentation
274
+ when 1 then %r/^( {0}:)(#{PATTERN_TAIL})/o
275
+ when 2 then %r/^( {0,1}:)(#{PATTERN_TAIL})/o
276
+ when 3 then %r/^( {0,2}:)(#{PATTERN_TAIL})/o
277
+ else %r/^( {0,3}:)(#{PATTERN_TAIL})/o
278
+ end
279
+ end
280
+ end
281
+
240
282
  end
241
283
  end
242
284
  end