kramdown 2.1.0 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/CONTRIBUTERS +19 -3
  3. data/README.md +8 -2
  4. data/VERSION +1 -1
  5. data/lib/kramdown/converter/base.rb +2 -1
  6. data/lib/kramdown/converter/html.rb +37 -30
  7. data/lib/kramdown/converter/kramdown.rb +20 -10
  8. data/lib/kramdown/converter/latex.rb +2 -2
  9. data/lib/kramdown/converter/math_engine/mathjax.rb +7 -33
  10. data/lib/kramdown/converter/syntax_highlighter/rouge.rb +17 -9
  11. data/lib/kramdown/converter/syntax_highlighter.rb +1 -1
  12. data/lib/kramdown/element.rb +24 -0
  13. data/lib/kramdown/options.rb +62 -12
  14. data/lib/kramdown/parser/base.rb +3 -1
  15. data/lib/kramdown/parser/html.rb +16 -9
  16. data/lib/kramdown/parser/kramdown/abbreviation.rb +1 -1
  17. data/lib/kramdown/parser/kramdown/autolink.rb +2 -2
  18. data/lib/kramdown/parser/kramdown/codespan.rb +18 -4
  19. data/lib/kramdown/parser/kramdown/emphasis.rb +1 -1
  20. data/lib/kramdown/parser/kramdown/extensions.rb +6 -0
  21. data/lib/kramdown/parser/kramdown/header.rb +3 -2
  22. data/lib/kramdown/parser/kramdown/html.rb +4 -10
  23. data/lib/kramdown/parser/kramdown/list.rb +37 -9
  24. data/lib/kramdown/parser/kramdown/math.rb +1 -1
  25. data/lib/kramdown/parser/kramdown/table.rb +2 -2
  26. data/lib/kramdown/parser/kramdown.rb +8 -1
  27. data/lib/kramdown/utils/html.rb +9 -0
  28. data/lib/kramdown/version.rb +1 -1
  29. data/man/man1/kramdown.1 +23 -0
  30. data/test/test_files.rb +28 -18
  31. data/test/test_location.rb +2 -2
  32. data/test/test_string_scanner_kramdown.rb +1 -1
  33. data/test/testcases/block/03_paragraph/standalone_image.html +5 -0
  34. data/test/testcases/block/03_paragraph/standalone_image.text +3 -0
  35. data/test/testcases/block/03_paragraph/to_kramdown.kramdown +7 -0
  36. data/test/testcases/block/03_paragraph/to_kramdown.text +5 -0
  37. data/test/testcases/block/04_header/atx_header.html +6 -0
  38. data/test/testcases/block/04_header/atx_header.text +6 -0
  39. data/test/testcases/block/06_codeblock/rouge/multiple.html +1 -1
  40. data/test/testcases/block/06_codeblock/rouge/simple.html +1 -1
  41. data/test/testcases/block/09_html/processing_instruction.html +5 -6
  42. data/test/testcases/block/09_html/standalone_image_in_div.htmlinput +7 -0
  43. data/test/testcases/block/09_html/standalone_image_in_div.text +8 -0
  44. data/test/testcases/block/09_html/table.kramdown +8 -0
  45. data/test/testcases/block/09_html/table.text +7 -0
  46. data/test/testcases/block/12_extension/options.html +4 -4
  47. data/test/testcases/block/12_extension/options.text +2 -0
  48. data/test/testcases/block/12_extension/options2.html +4 -4
  49. data/test/testcases/block/14_table/table_with_footnote.html +4 -4
  50. data/test/testcases/block/15_math/gh_128.html +1 -2
  51. data/test/testcases/block/15_math/normal.html +16 -15
  52. data/test/testcases/block/16_toc/toc_with_footnotes.html +4 -4
  53. data/test/testcases/cjk-line-break.html +4 -0
  54. data/test/testcases/cjk-line-break.options +1 -0
  55. data/test/testcases/cjk-line-break.text +12 -0
  56. data/test/testcases/man/example.man +1 -1
  57. data/test/testcases/man/example.text +1 -1
  58. data/test/testcases/span/02_emphasis/normal.html +4 -0
  59. data/test/testcases/span/02_emphasis/normal.text +4 -0
  60. data/test/testcases/span/03_codespan/normal.html +4 -0
  61. data/test/testcases/span/03_codespan/normal.text +4 -0
  62. data/test/testcases/span/04_footnote/backlink_inline.html +21 -21
  63. data/test/testcases/span/04_footnote/backlink_text.html +4 -4
  64. data/test/testcases/span/04_footnote/footnote_nr.html +6 -6
  65. data/test/testcases/span/04_footnote/footnote_prefix.html +6 -6
  66. data/test/testcases/span/04_footnote/inside_footnote.html +9 -9
  67. data/test/testcases/span/04_footnote/markers.html +16 -16
  68. data/test/testcases/span/04_footnote/placement.html +4 -4
  69. data/test/testcases/span/04_footnote/regexp_problem.html +4 -4
  70. data/test/testcases/span/04_footnote/without_backlink.html +3 -3
  71. data/test/testcases/span/05_html/normal.html +1 -1
  72. data/test/testcases/span/abbreviations/abbrev_in_html.html +9 -0
  73. data/test/testcases/span/abbreviations/abbrev_in_html.text +10 -0
  74. data/test/testcases/span/abbreviations/in_footnote.html +4 -4
  75. data/test/testcases/span/math/normal.html +4 -4
  76. data/test/testcases/span/text_substitutions/entities.html +1 -1
  77. data/test/testcases/span/text_substitutions/entities.text +1 -1
  78. metadata +36 -15
  79. data/test/testcases/block/15_math/mathjax_preview.html +0 -4
  80. data/test/testcases/block/15_math/mathjax_preview.options +0 -2
  81. data/test/testcases/block/15_math/mathjax_preview.text +0 -5
  82. data/test/testcases/block/15_math/mathjax_preview_as_code.html +0 -4
  83. data/test/testcases/block/15_math/mathjax_preview_as_code.options +0 -3
  84. data/test/testcases/block/15_math/mathjax_preview_as_code.text +0 -5
  85. data/test/testcases/block/15_math/mathjax_preview_simple.html +0 -4
  86. data/test/testcases/block/15_math/mathjax_preview_simple.options +0 -2
  87. data/test/testcases/block/15_math/mathjax_preview_simple.text +0 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e43c55f31bbce7153d1d863ab7c90bb24aee7741e7df3a0f6d89064286fe2344
4
- data.tar.gz: c53d7af0318896bae6bdcb9123eb714f9572e4c9918f04d093c7d228c2431b0e
3
+ metadata.gz: 3c41b216fbd6f50c68b10864bc3a98040f79641db9af8ced05eb43f7817335ad
4
+ data.tar.gz: ef3adb19cbfbe4586bf0ef4f14362ca843a7fe3331ff6dc990c270ff65cff000
5
5
  SHA512:
6
- metadata.gz: 351a0939690de3cf44107f5cad613d8d3dc945b6d47d0e78f8bf676081b7887f696e738fb9200779a6821a870161a3110a7e42911e7705cc7d79af59ff061da6
7
- data.tar.gz: dbd73556bc863c0d6a937705662909440fd1dea33415da9e98ac681e5cd33fbedde37a7311b1082c2bcd556aec2896c10ec1d203ca992783f05eae8e7572aea0
6
+ metadata.gz: 483e269f858aadd6bf8e7bf2f49ec90ddfdcd9628aec17822eb7eefcfc21897eb19d258a84b6291611a38a5958a79013c2c656d35f7740b116923bd8624a2329
7
+ data.tar.gz: 5ffeef35cdfb994411414d62bd8b5fcf09fcea7d926069896248148575b88b4abbfdc1e5aab6da8dbe6f97c3e0a4921b391802e391ea3c76a646be7f64fbbbda
data/CONTRIBUTERS CHANGED
@@ -1,18 +1,20 @@
1
1
  Count Name
2
2
  ======= ====
3
- 913 Thomas Leitner <t_leitner@gmx.at>
3
+ 964 Thomas Leitner <t_leitner@gmx.at>
4
+ 18 Ashwin Maroli <ashmaroli@gmail.com>
4
5
  7 Christian Cornelssen <ccorn@1tein.de>
5
6
  6 Gioele Barabucci <gioele@svario.it>
7
+ 5 Gleb Mazovetskiy <glex.spb@gmail.com>
6
8
  4 Ted Pak <powerpak006@gmail.com>
7
9
  4 Shuanglei Tao <tsl0922@gmail.com>
8
- 4 Gleb Mazovetskiy <glex.spb@gmail.com>
9
10
  4 Dan Allen <dan.j.allen@gmail.com>
10
- 4 Ashwin Maroli <ashmaroli@gmail.com>
11
11
  4 Arne Brasseur <arne@arnebrasseur.net>
12
12
  3 Henning Perl <perl@fast-sicher.de>
13
13
  3 gettalong <t_leitner@gmx.at>
14
+ 3 Carsten Bormann <cabo@tzi.org>
14
15
  3 Brandur <brandur@mutelight.org>
15
16
  3 Ben Armston <ben.armston@googlemail.com>
17
+ 3 Ashwin Maroli <ashmaroli@users.noreply.github.com>
16
18
  3 Alex Marandon <contact@alexmarandon.com>
17
19
  2 Tom Thorogood <me+github@tomthorogood.co.uk>
18
20
  2 Parker Moore <parkrmoore@gmail.com>
@@ -26,20 +28,28 @@
26
28
  1 utenmiki <utenmiki@gmail.com>
27
29
  1 Trevor Wennblom <trevor@well.com>
28
30
  1 tomykaira <tomykaira@gmail.com>
31
+ 1 tom93 <tomlevy93@gmail.com>
32
+ 1 Tobin Yehle <tobinyehle@gmail.com>
33
+ 1 timcraft <mail@timcraft.com>
29
34
  1 Tim Blair <tim@bla.ir>
30
35
  1 Tim Besard <tim.besard@gmail.com>
31
36
  1 Tim Bates <tim@rumpuslabs.com>
32
37
  1 Sun Yaozhu <yzyzsun@gmail.com>
38
+ 1 Stephen <stephengroat@users.noreply.github.com>
33
39
  1 Stephen Crosby <stevecrozz@gmail.com>
40
+ 1 Stan Hu <stanhu@gmail.com>
34
41
  1 Simon Lydell <simon.lydell@gmail.com>
42
+ 1 Simon Coffey <simon.coffey@futurelearn.com>
35
43
  1 Shusaku NAKAZATO <cu393uc@gmail.com>
36
44
  1 Sebastian Boehm <sebastian@sometimesfood.org>
37
45
  1 scherr <maximilianscherr@gmail.com>
38
46
  1 Postmodern <postmodern.mod3@gmail.com>
39
47
  1 Pete Michaud <michaudp@gmail.com>
48
+ 1 Noah Doersing <doersino@gmail.com>
40
49
  1 myqlarson <myqlarson@gmail.com>
41
50
  1 milo.simpson <milo.simpson@bazaarvoice.com>
42
51
  1 Michal Till <michal.till@gmail.com>
52
+ 1 Maxime Kjaer <maxime.kjaer@gmail.com>
43
53
  1 Matt Hickford <matt.hickford@gmail.com>
44
54
  1 Martyn Chamberlin <martyn@perfectioncoding.com>
45
55
  1 Marek Tuchowski <marek@tuchowski.com.pl>
@@ -56,13 +66,19 @@
56
66
  1 Hector Correa <hector@hectorcorrea.com>
57
67
  1 Florian Klampfer <f.klampfer@gmail.com>
58
68
  1 Floreal Morandat florealm@gmail.com <florealm@gmail.com>
69
+ 1 Fangyi Zhou <me@fangyi.io>
59
70
  1 Diego Galeota <diegobg123@gmail.com>
71
+ 1 David Rodríguez <deivid.rodriguez@riseup.net>
72
+ 1 Daniel Bair <daniel@danielbair.com>
60
73
  1 Damien Pollet <damien.pollet@gmail.com>
61
74
  1 Christopher Jefferson <caj21@st-andrews.ac.uk>
62
75
  1 Cédric Boutillier <cedric.boutillier@gmail.com>
76
+ 1 Bob Lail <lail@squareup.com>
63
77
  1 Ashe Connor <ashe@kivikakk.ee>
78
+ 1 aschmitz <29508+aschmitz@users.noreply.github.com>
64
79
  1 Antoine Cotten <hello@acotten.com>
65
80
  1 Andrew <andrew.dale.wylie@gmail.com>
66
81
  1 Alpha Chen <alpha.chen@gmail.com>
67
82
  1 Alex Tomlins <alex.tomlins@digital.cabinet-office.gov.uk>
68
83
  1 Alexey Vasiliev <le0pard@users.noreply.github.com>
84
+ 1 284km <k.furuhashi10@gmail.com>
data/README.md CHANGED
@@ -7,8 +7,14 @@ requests it is now released under the MIT license and therefore can easily be us
7
7
  projects, too.
8
8
 
9
9
  However, if you use kramdown in a commercial setting, please consider **contributing back any
10
- changes** for the benefit of the community and/or
11
- [**becoming a patron**](https://www.patreon.com/gettalong) - thanks!
10
+ changes** for the benefit of the community and/or [**becoming a
11
+ sponsor**](https://github.com/sponsors/gettalong/) or [**a
12
+ patron**](https://www.patreon.com/gettalong) - thanks!
13
+
14
+ Sponsors:
15
+
16
+ * **GROSSWEBER** provides <a href="http://grossweber.com/b/kramdown" target="_blank">software
17
+ development consulting and training services</a>.
12
18
 
13
19
 
14
20
  ## Introduction
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.1.0
1
+ 2.4.0
@@ -106,7 +106,8 @@ module Kramdown
106
106
  end
107
107
  result = converter.convert(tree)
108
108
  if result.respond_to?(:encode!) && result.encoding != Encoding::BINARY
109
- result.encode!(tree.options[:encoding])
109
+ result.encode!(tree.options[:encoding] ||
110
+ (raise ::Kramdown::Error, "Missing encoding option on root element"))
110
111
  end
111
112
  if !converter.options[:template].empty? && converter.apply_template_after?
112
113
  result = apply_template(converter, result)
@@ -46,15 +46,16 @@ module Kramdown
46
46
  @toc_code = nil
47
47
  @indent = 2
48
48
  @stack = []
49
- end
50
49
 
51
- # The mapping of element type to conversion method.
52
- DISPATCHER = Hash.new {|h, k| h[k] = "convert_#{k}" }
50
+ # stash string representation of symbol to avoid allocations from multiple interpolations.
51
+ @highlighter_class = " highlighter-#{options[:syntax_highlighter]}"
52
+ @dispatcher = Hash.new {|h, k| h[k] = :"convert_#{k}" }
53
+ end
53
54
 
54
55
  # Dispatch the conversion of the element +el+ to a +convert_TYPE+ method using the +type+ of
55
56
  # the element.
56
57
  def convert(el, indent = -@indent)
57
- send(DISPATCHER[el.type], el, indent)
58
+ send(@dispatcher[el.type], el, indent)
58
59
  end
59
60
 
60
61
  # Return the converted content of the children of +el+ as a string. The parameter +indent+ has
@@ -67,7 +68,7 @@ module Kramdown
67
68
  indent += @indent
68
69
  @stack.push(el)
69
70
  el.children.each do |inner_el|
70
- result << send(DISPATCHER[inner_el.type], inner_el, indent)
71
+ result << send(@dispatcher[inner_el.type], inner_el, indent)
71
72
  end
72
73
  @stack.pop
73
74
  result
@@ -78,7 +79,8 @@ module Kramdown
78
79
  end
79
80
 
80
81
  def convert_text(el, _indent)
81
- escape_html(el.value, :text)
82
+ escaped = escape_html(el.value, :text)
83
+ @options[:remove_line_breaks_for_cjk] ? fix_cjk_line_break(escaped) : escaped
82
84
  end
83
85
 
84
86
  def convert_p(el, indent)
@@ -86,21 +88,23 @@ module Kramdown
86
88
  inner(el, indent)
87
89
  elsif el.children.size == 1 && el.children.first.type == :img &&
88
90
  el.children.first.options[:ial]&.[](:refs)&.include?('standalone')
89
- convert_standalone_image(el.children.first, indent)
91
+ convert_standalone_image(el, indent)
90
92
  else
91
- format_as_block_html(el.type, el.attr, inner(el, indent), indent)
93
+ format_as_block_html("p", el.attr, inner(el, indent), indent)
92
94
  end
93
95
  end
94
96
 
95
97
  # Helper method used by +convert_p+ to convert a paragraph that only contains a single :img
96
98
  # element.
97
99
  def convert_standalone_image(el, indent)
98
- attr = el.attr.dup
99
- figure_attr = {}
100
- figure_attr['class'] = attr.delete('class') if attr.key?('class')
101
- figure_attr['id'] = attr.delete('id') if attr.key?('id')
102
- body = "#{' ' * (indent + @indent)}<img#{html_attributes(attr)} />\n" \
103
- "#{' ' * (indent + @indent)}<figcaption>#{attr['alt']}</figcaption>\n"
100
+ figure_attr = el.attr.dup
101
+ image_attr = el.children.first.attr.dup
102
+
103
+ figure_attr['class'] = image_attr.delete('class') if image_attr.key?('class') and not figure_attr.key?('class')
104
+ figure_attr['id'] = image_attr.delete('id') if image_attr.key?('id') and not figure_attr.key?('id')
105
+
106
+ body = "#{' ' * (indent + @indent)}<img#{html_attributes(image_attr)} />\n" \
107
+ "#{' ' * (indent + @indent)}<figcaption>#{image_attr['alt']}</figcaption>\n"
104
108
  format_as_indented_block_html("figure", figure_attr, body, indent)
105
109
  end
106
110
 
@@ -135,7 +139,7 @@ module Kramdown
135
139
  end
136
140
 
137
141
  def convert_blockquote(el, indent)
138
- format_as_indented_block_html(el.type, el.attr, inner(el, indent), indent)
142
+ format_as_indented_block_html("blockquote", el.attr, inner(el, indent), indent)
139
143
  end
140
144
 
141
145
  def convert_header(el, indent)
@@ -152,12 +156,15 @@ module Kramdown
152
156
  "#{' ' * indent}<hr#{html_attributes(el.attr)} />\n"
153
157
  end
154
158
 
159
+ ZERO_TO_ONETWENTYEIGHT = (0..128).to_a.freeze
160
+ private_constant :ZERO_TO_ONETWENTYEIGHT
161
+
155
162
  def convert_ul(el, indent)
156
- if !@toc_code && (el.options[:ial][:refs].include?('toc') rescue nil)
157
- @toc_code = [el.type, el.attr, (0..128).to_a.map { rand(36).to_s(36) }.join]
163
+ if !@toc_code && el.options.dig(:ial, :refs)&.include?('toc')
164
+ @toc_code = [el.type, el.attr, ZERO_TO_ONETWENTYEIGHT.map { rand(36).to_s(36) }.join]
158
165
  @toc_code.last
159
- elsif !@footnote_location && el.options[:ial] && (el.options[:ial][:refs] || []).include?('footnotes')
160
- @footnote_location = (0..128).to_a.map { rand(36).to_s(36) }.join
166
+ elsif !@footnote_location && el.options.dig(:ial, :refs)&.include?('footnotes')
167
+ @footnote_location = ZERO_TO_ONETWENTYEIGHT.map { rand(36).to_s(36) }.join
161
168
  else
162
169
  format_as_indented_block_html(el.type, el.attr, inner(el, indent), indent)
163
170
  end
@@ -165,7 +172,7 @@ module Kramdown
165
172
  alias convert_ol convert_ul
166
173
 
167
174
  def convert_dl(el, indent)
168
- format_as_indented_block_html(el.type, el.attr, inner(el, indent), indent)
175
+ format_as_indented_block_html("dl", el.attr, inner(el, indent), indent)
169
176
  end
170
177
 
171
178
  def convert_li(el, indent)
@@ -188,7 +195,7 @@ module Kramdown
188
195
  break
189
196
  end
190
197
  end if !attr['id'] && @stack.last.options[:ial] && @stack.last.options[:ial][:refs]
191
- format_as_block_html(el.type, attr, inner(el, indent), indent)
198
+ format_as_block_html("dt", attr, inner(el, indent), indent)
192
199
  end
193
200
 
194
201
  def convert_html_element(el, indent)
@@ -263,7 +270,7 @@ module Kramdown
263
270
  end
264
271
 
265
272
  def convert_a(el, indent)
266
- format_as_span_html(el.type, el.attr, inner(el, indent))
273
+ format_as_span_html("a", el.attr, inner(el, indent))
267
274
  end
268
275
 
269
276
  def convert_img(el, _indent)
@@ -276,7 +283,7 @@ module Kramdown
276
283
  hl_opts = {}
277
284
  result = highlight_code(el.value, lang, :span, hl_opts)
278
285
  if result
279
- add_syntax_highlighter_to_class_attr(attr, hl_opts[:default_lang])
286
+ add_syntax_highlighter_to_class_attr(attr, lang || hl_opts[:default_lang])
280
287
  else
281
288
  result = escape_html(el.value)
282
289
  end
@@ -296,8 +303,8 @@ module Kramdown
296
303
  @footnotes << [name, el.value, number, 0]
297
304
  @footnotes_by_name[name] = @footnotes.last
298
305
  end
299
- "<sup id=\"fnref:#{name}#{repeat}\">" \
300
- "<a href=\"#fn:#{name}\" class=\"footnote\">" \
306
+ "<sup id=\"fnref:#{name}#{repeat}\" role=\"doc-noteref\">" \
307
+ "<a href=\"#fn:#{name}\" class=\"footnote\" rel=\"footnote\">" \
301
308
  "#{number}</a></sup>"
302
309
  end
303
310
 
@@ -400,7 +407,7 @@ module Kramdown
400
407
  # Add the syntax highlighter name to the 'class' attribute of the given attribute hash. And
401
408
  # overwrites or add a "language-LANG" part using the +lang+ parameter if +lang+ is not nil.
402
409
  def add_syntax_highlighter_to_class_attr(attr, lang = nil)
403
- (attr['class'] = (attr['class'] || '') + " highlighter-#{@options[:syntax_highlighter]}").lstrip!
410
+ (attr['class'] = (attr['class'] || '') + @highlighter_class).lstrip!
404
411
  attr['class'].sub!(/\blanguage-\S+|(^)/) { "language-#{lang}#{$1 ? ' ' : ''}" } if lang
405
412
  end
406
413
 
@@ -475,9 +482,9 @@ module Kramdown
475
482
  result
476
483
  end
477
484
 
478
- FOOTNOTE_BACKLINK_FMT = "%s<a href=\"#fnref:%s\" class=\"reversefootnote\">%s</a>"
485
+ FOOTNOTE_BACKLINK_FMT = "%s<a href=\"#fnref:%s\" class=\"reversefootnote\" role=\"doc-backlink\">%s</a>"
479
486
 
480
- # Return a HTML ordered list with the footnote content for the used footnotes.
487
+ # Return an HTML ordered list with the footnote content for the used footnotes.
481
488
  def footnote_content
482
489
  ol = Element.new(:ol)
483
490
  ol.attr['start'] = @footnote_start if @footnote_start != 1
@@ -485,7 +492,7 @@ module Kramdown
485
492
  backlink_text = escape_html(@options[:footnote_backlink], :text)
486
493
  while i < @footnotes.length
487
494
  name, data, _, repeat = *@footnotes[i]
488
- li = Element.new(:li, nil, 'id' => "fn:#{name}")
495
+ li = Element.new(:li, nil, 'id' => "fn:#{name}", 'role' => 'doc-endnote')
489
496
  li.children = Marshal.load(Marshal.dump(data.children))
490
497
 
491
498
  para = nil
@@ -520,7 +527,7 @@ module Kramdown
520
527
  if ol.children.empty?
521
528
  ''
522
529
  else
523
- format_as_indented_block_html('div', {class: "footnotes"}, convert(ol, 2), 0)
530
+ format_as_indented_block_html('div', {class: "footnotes", role: "doc-endnotes"}, convert(ol, 2), 0)
524
531
  end
525
532
  end
526
533
 
@@ -27,6 +27,8 @@ module Kramdown
27
27
  @footnotes = []
28
28
  @abbrevs = []
29
29
  @stack = []
30
+ @list_indent = @options[:list_indent]
31
+ @list_spacing = ' ' * (@list_indent - 2)
30
32
  end
31
33
 
32
34
  def convert(el, opts = {indent: 0})
@@ -34,13 +36,13 @@ module Kramdown
34
36
  res = res.dup if res.frozen?
35
37
  if ![:html_element, :li, :dt, :dd, :td].include?(el.type) && (ial = ial_for_element(el))
36
38
  res << ial
37
- res << "\n\n" if Element.category(el) == :block
39
+ res << "\n\n" if el.block?
38
40
  elsif [:ul, :dl, :ol, :codeblock].include?(el.type) && opts[:next] &&
39
41
  ([el.type, :codeblock].include?(opts[:next].type) ||
40
42
  (opts[:next].type == :blank && opts[:nnext] &&
41
43
  [el.type, :codeblock].include?(opts[:nnext].type)))
42
44
  res << "^\n\n"
43
- elsif Element.category(el) == :block &&
45
+ elsif el.block? &&
44
46
  ![:li, :dd, :dt, :td, :th, :tr, :thead, :tbody, :tfoot, :blank].include?(el.type) &&
45
47
  (el.type != :html_element || @stack.last.type != :html_element) &&
46
48
  (el.type != :p || !el.options[:transparent])
@@ -77,7 +79,9 @@ module Kramdown
77
79
  else
78
80
  el.value.gsub(/\A\n/) do
79
81
  opts[:prev] && opts[:prev].type == :br ? '' : "\n"
80
- end.gsub(/\s+/, ' ').gsub(ESCAPED_CHAR_RE) { "\\#{$1 || $2}" }
82
+ end.gsub(/\s+/, ' ').gsub(ESCAPED_CHAR_RE) do
83
+ $1 || !opts[:prev] || opts[:prev].type == :br ? "\\#{$1 || $2}" : $&
84
+ end
81
85
  end
82
86
  end
83
87
 
@@ -87,6 +91,7 @@ module Kramdown
87
91
  first&.gsub!(/^(?:(#|>)|(\d+)\.|([+-]\s))/) { $1 || $3 ? "\\#{$1 || $3}" : "#{$2}\\." }
88
92
  second&.gsub!(/^([=-]+\s*?)$/, "\\\1")
89
93
  res = [first, second, *rest].compact.join("\n") + "\n"
94
+ res.gsub!(/^[ ]{0,3}:/, "\\:")
90
95
  if el.children.length == 1 && el.children.first.type == :math
91
96
  res = "\\#{res}"
92
97
  elsif res.start_with?('\$$') && res.end_with?("\\$$\n")
@@ -124,7 +129,7 @@ module Kramdown
124
129
 
125
130
  def convert_li(el, opts)
126
131
  sym, width = if @stack.last.type == :ul
127
- [+'* ', el.children.first && el.children.first.type == :codeblock ? 4 : 2]
132
+ ['* ' + @list_spacing, el.children.first && el.children.first.type == :codeblock ? 4 : @list_indent]
128
133
  else
129
134
  ["#{opts[:index] + 1}.".ljust(4), 4]
130
135
  end
@@ -151,7 +156,7 @@ module Kramdown
151
156
  end
152
157
 
153
158
  def convert_dd(el, opts)
154
- sym, width = +": ", (el.children.first && el.children.first.type == :codeblock ? 4 : 2)
159
+ sym, width = ": " + @list_spacing, (el.children.first && el.children.first.type == :codeblock ? 4 : @list_indent)
155
160
  if (ial = ial_for_element(el))
156
161
  sym << ial << " "
157
162
  end
@@ -182,12 +187,17 @@ module Kramdown
182
187
  result << inner(el, opts) << "\n"
183
188
  end
184
189
 
185
- HTML_TAGS_WITH_BODY = ['div', 'script', 'iframe', 'textarea']
190
+ HTML_TAGS_WITH_BODY = ['div', 'script', 'iframe', 'textarea', 'th', 'td']
191
+
192
+ HTML_ELEMENT_TYPES = [:entity, :text, :html_element].freeze
193
+ private_constant :HTML_ELEMENT_TYPES
186
194
 
187
195
  def convert_html_element(el, opts)
188
196
  markdown_attr = el.options[:category] == :block && el.children.any? do |c|
189
- c.type != :html_element && (c.type != :p || !c.options[:transparent]) &&
190
- Element.category(c) == :block
197
+ c.type != :html_element &&
198
+ (c.type != :p || !c.options[:transparent] ||
199
+ c.children.any? {|t| !HTML_ELEMENT_TYPES.member?(t.type) }) &&
200
+ c.block?
191
201
  end
192
202
  opts[:force_raw_text] = true if %w[script pre code].include?(el.value)
193
203
  opts[:raw_text] = opts[:force_raw_text] || opts[:block_raw_text] || \
@@ -421,9 +431,9 @@ module Kramdown
421
431
  end
422
432
  end.compact.join('')
423
433
  res = "toc" + (res.strip.empty? ? '' : " #{res}") if (el.type == :ul || el.type == :ol) &&
424
- (el.options[:ial][:refs].include?('toc') rescue nil)
434
+ el.options.dig(:ial, :refs)&.include?('toc')
425
435
  res = "footnotes" + (res.strip.empty? ? '' : " #{res}") if (el.type == :ul || el.type == :ol) &&
426
- (el.options[:ial][:refs].include?('footnotes') rescue nil)
436
+ el.options.dig(:ial, :refs)&.include?('footnotes')
427
437
  if el.type == :dl && el.options[:ial] && el.options[:ial][:refs]
428
438
  auto_ids = el.options[:ial][:refs].select {|ref| ref.start_with?('auto_ids') }.join(" ")
429
439
  res = auto_ids << (res.strip.empty? ? '' : " #{res}") unless auto_ids.empty?
@@ -127,7 +127,7 @@ module Kramdown
127
127
  end
128
128
 
129
129
  def convert_ul(el, opts)
130
- if !@data[:has_toc] && (el.options[:ial][:refs].include?('toc') rescue nil)
130
+ if !@data[:has_toc] && el.options.dig(:ial, :refs)&.include?('toc')
131
131
  @data[:has_toc] = true
132
132
  '\tableofcontents'
133
133
  else
@@ -517,7 +517,7 @@ module Kramdown
517
517
  8194 => ['\hskip .5em\relax'],
518
518
  8195 => ['\quad'],
519
519
  } # :nodoc:
520
- ENTITY_CONV_TABLE.each_value {|v| v[0] = "{}#{v[0]}" }
520
+ ENTITY_CONV_TABLE.each_value {|v| v[0] = "#{v[0]}{}" }
521
521
 
522
522
  def entity_to_latex(entity)
523
523
  text, package = ENTITY_CONV_TABLE[entity.code_point]
@@ -16,40 +16,14 @@ module Kramdown::Converter::MathEngine
16
16
  module Mathjax
17
17
 
18
18
  def self.call(converter, el, opts)
19
- type = el.options[:category]
20
- text = (el.value =~ /<|&/ ? "% <![CDATA[\n#{el.value} %]]>" : el.value).dup
21
- text.gsub!(/<\/?script>?/, '')
22
-
23
- preview = preview_string(converter, el, opts).dup
24
-
25
- attr = {type: "math/tex#{type == :block ? '; mode=display' : ''}"}
26
- preview << if type == :block
27
- converter.format_as_block_html('script', attr, text, opts[:indent])
28
- else
29
- converter.format_as_span_html('script', attr, text)
30
- end
31
- end
32
-
33
- def self.preview_string(converter, el, opts)
34
- preview = converter.options[:math_engine_opts][:preview]
35
- return '' unless preview
36
-
37
- preview = (preview == true ? converter.escape_html(el.value) : preview.to_s)
38
-
39
- preview_as_code = converter.options[:math_engine_opts][:preview_as_code]
40
-
41
- if el.options[:category] == :block
42
- if preview_as_code
43
- converter.format_as_block_html('pre', {'class' => 'MathJax_Preview'},
44
- converter.format_as_span_html('code', {}, preview),
45
- opts[:indent])
46
- else
47
- converter.format_as_block_html('div', {'class' => 'MathJax_Preview'}, preview,
48
- opts[:indent])
49
- end
19
+ value = converter.escape_html(el.value)
20
+ result = el.options[:category] == :block ? "\\[#{value}\\]\n" : "\\(#{value}\\)"
21
+ if el.attr.empty?
22
+ result
23
+ elsif el.options[:category] == :block
24
+ converter.format_as_block_html('div', el.attr, result, opts[:indent])
50
25
  else
51
- converter.format_as_span_html(preview_as_code ? 'code' : 'span',
52
- {'class' => 'MathJax_Preview'}, preview)
26
+ converter.format_as_span_html('span', el.attr, "$#{el.value}$")
53
27
  end
54
28
  end
55
29
 
@@ -45,24 +45,32 @@ module Kramdown::Converter::SyntaxHighlighter
45
45
  cache = converter.data[:syntax_highlighter_rouge] = {}
46
46
 
47
47
  opts = converter.options[:syntax_highlighter_opts].dup
48
- span_opts = (opts.delete(:span) || {}).dup
49
- block_opts = (opts.delete(:block) || {}).dup
50
- [span_opts, block_opts].each do |hash|
51
- hash.keys.each do |k|
52
- hash[k.kind_of?(String) ? Kramdown::Options.str_to_sym(k) : k] = hash.delete(k)
53
- end
54
- end
55
48
 
56
- cache[:span] = opts.merge(span_opts).update(wrap: false)
49
+ span_opts = opts.delete(:span)&.dup || {}
50
+ block_opts = opts.delete(:block)&.dup || {}
51
+ normalize_keys(span_opts)
52
+ normalize_keys(block_opts)
53
+
54
+ cache[:span] = opts.merge(span_opts)
55
+ cache[:span][:wrap] = false
56
+
57
57
  cache[:block] = opts.merge(block_opts)
58
58
  end
59
59
 
60
+ def self.normalize_keys(hash)
61
+ return if hash.empty?
62
+
63
+ hash.keys.each do |k|
64
+ hash[k.kind_of?(String) ? Kramdown::Options.str_to_sym(k) : k] = hash.delete(k)
65
+ end
66
+ end
67
+
60
68
  def self.formatter_class(opts = {})
61
69
  case formatter = opts[:formatter]
62
70
  when Class
63
71
  formatter
64
72
  when /\A[[:upper:]][[:alnum:]_]*\z/
65
- ::Rouge::Formatters.const_get(formatter)
73
+ ::Rouge::Formatters.const_get(formatter, false)
66
74
  else
67
75
  # Available in Rouge 2.0 or later
68
76
  ::Rouge::Formatters::HTMLLegacy
@@ -42,7 +42,7 @@ module Kramdown
42
42
  #
43
43
  # == Special Implementation Details
44
44
  #
45
- # HTML converter:: If the syntax highlighter is used with a HTML converter, it should return
45
+ # HTML converter:: If the syntax highlighter is used with an HTML converter, it should return
46
46
  # :block type text correctly wrapped (i.e. normally inside a pre-tag, but may
47
47
  # also be a table-tag or just a div-tag) but :span type text *without* a
48
48
  # code-tag!
@@ -14,6 +14,14 @@ module Kramdown
14
14
  # kramdown only uses this one class for representing all available elements in an element tree
15
15
  # (paragraphs, headers, emphasis, ...). The type of element can be set via the #type accessor.
16
16
  #
17
+ # The root of a kramdown element tree has to be an element of type :root. It needs to have certain
18
+ # option keys set so that conversions work correctly. If only a part of a tree should be
19
+ # converted, duplicate the root node and assign the #children appropriately, e.g:
20
+ #
21
+ # root = doc.root
22
+ # new_root = root.dup
23
+ # new_root.children = [root.children[0]] # assign new array with elements to convert
24
+ #
17
25
  # Following is a description of all supported element types.
18
26
  #
19
27
  # Note that the option :location may contain the start line number of an element in the source
@@ -522,6 +530,22 @@ module Kramdown
522
530
  CATEGORY[el.type] || el.options[:category]
523
531
  end
524
532
 
533
+ # syntactic sugar to simplify calls such as +Kramdown::Element.category(el) == :block+ with
534
+ # +el.block?+.
535
+ #
536
+ # Returns boolean true or false.
537
+ def block?
538
+ (CATEGORY[type] || options[:category]) == :block
539
+ end
540
+
541
+ # syntactic sugar to simplify calls such as +Kramdown::Element.category(el) == :span+ with
542
+ # +el.span?+.
543
+ #
544
+ # Returns boolean true or false.
545
+ def span?
546
+ (CATEGORY[type] || options[:category]) == :span
547
+ end
548
+
525
549
  end
526
550
 
527
551
  end