kramdown 2.1.0 → 2.4.0

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 (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