commonmarker 0.22.0 → 0.23.0

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

Potentially problematic release.


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

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f5356237deaf85df9914e0c7fa3f121fa0301579653b83b006df4dd8404c65f2
4
- data.tar.gz: 9d4a09830e1c107dec28e47a3c0ab96f2cc6bcd9786982e38607752022ec12f1
3
+ metadata.gz: c52b70b0c680aae2d46b089b673745cbe586f1e27589bd81b1f3f0f091a7659d
4
+ data.tar.gz: 400efb13ab3ba9c72d451be4cd4cac8ee17688f70733d6d57771a093e412b63f
5
5
  SHA512:
6
- metadata.gz: 7e43f365eb266fa8c0627572999a2102df7dd65e0404917c5fa6054b532c526a35ad5d0ef9423cf68033edc52fb0c20da79ca73d7345b04d4493f33bff35ed73
7
- data.tar.gz: 0e5da729c2247288a1bf60672f572f785def6e2a1726afeaf87aa1332d083e4834d52c403479a36437f9c0a9e99548569f7d5bc092a405304ccef81ac71096b1
6
+ metadata.gz: 99309c1ab3c30c524adac6be1f75b3e63fe166b9248f8a8e74bd228a2428b10c794a56a3d5390786e4b33a869531f5879461c73d1ae7aaea66be491000779761
7
+ data.tar.gz: 2c4ac9a297cfb0204eb4355f0aa3e8471ddd2f6bf9162e7db2a71f16e32f3470779f86082e8aa9473ddf0f45ee3df41d133a7f073209c3d1441da4468266297d
data/README.md CHANGED
@@ -180,6 +180,76 @@ The available extensions are:
180
180
  * `:autolink` - This provides support for automatically converting URLs to anchor tags.
181
181
  * `:tagfilter` - This escapes [several "unsafe" HTML tags](https://github.github.com/gfm/#disallowed-raw-html-extension-), causing them to not have any effect.
182
182
 
183
+ ## Output formats
184
+
185
+ Like CMark, CommonMarker can generate output in several formats: HTML, XML, plaintext, and commonmark are currently supported.
186
+
187
+ ### HTML
188
+
189
+ The default output format, HTML, will be generated when calling `to_html` or using `--to=html` on the command line.
190
+
191
+ ```ruby
192
+ doc = CommonMarker.render_doc('*Hello* world!', :DEFAULT)
193
+ puts(doc.to_html)
194
+
195
+ <p><em>Hello</em> world!</p>
196
+ ```
197
+
198
+ ### XML
199
+
200
+ XML will be generated when calling `to_xml` or using `--to=xml` on the command line.
201
+
202
+ ```ruby
203
+ doc = CommonMarker.render_doc('*Hello* world!', :DEFAULT)
204
+ puts(doc.to_xml)
205
+
206
+ <?xml version="1.0" encoding="UTF-8"?>
207
+ <!DOCTYPE document SYSTEM "CommonMark.dtd">
208
+ <document xmlns="http://commonmark.org/xml/1.0">
209
+ <paragraph>
210
+ <emph>
211
+ <text xml:space="preserve">Hello</text>
212
+ </emph>
213
+ <text xml:space="preserve"> world!</text>
214
+ </paragraph>
215
+ </document>
216
+ ```
217
+
218
+ ### Plaintext
219
+
220
+ Plaintext will be generated when calling `to_plaintext` or using `--to=plaintext` on the command line.
221
+
222
+ ```ruby
223
+ doc = CommonMarker.render_doc('*Hello* world!', :DEFAULT)
224
+ puts(doc.to_plaintext)
225
+
226
+ Hello world!
227
+ ```
228
+
229
+ ### Commonmark
230
+
231
+ Commonmark will be generated when calling `to_commonmark` or using `--to=commonmark` on the command line.
232
+
233
+ ``` ruby
234
+ text = <<-TEXT
235
+ 1. I am a numeric list.
236
+ 2. I continue the list.
237
+ * Suddenly, an unordered list!
238
+ * What fun!
239
+ TEXT
240
+
241
+ doc = CommonMarker.render_doc(text, :DEFAULT)
242
+ puts(doc.to_commonmark)
243
+
244
+ 1. I am a numeric list.
245
+ 2. I continue the list.
246
+
247
+ <!-- end list -->
248
+
249
+ - Suddenly, an unordered list\!
250
+ - What fun\!
251
+ ```
252
+
183
253
  ## Developing locally
184
254
 
185
255
  After cloning the repo:
data/bin/commonmarker CHANGED
@@ -15,13 +15,16 @@ def parse_options
15
15
  extensions = CommonMarker.extensions
16
16
  parse_options = CommonMarker::Config::OPTS.fetch(:parse)
17
17
  render_options = CommonMarker::Config::OPTS.fetch(:render)
18
+ format_options = CommonMarker::Config::OPTS.fetch(:format)
18
19
 
19
20
  options.active_extensions = []
20
21
  options.active_parse_options = [:DEFAULT]
21
22
  options.active_render_options = [:DEFAULT]
23
+ options.output_format = :html
22
24
 
23
25
  option_parser = OptionParser.new do |opts|
24
26
  opts.banner = 'Usage: commonmarker [--html-renderer] [--extension=EXTENSION]'
27
+ opts.separator ' [--to=FORMAT]'
25
28
  opts.separator ' [--parse-option=OPTION]'
26
29
  opts.separator ' [--render-option=OPTION]'
27
30
  opts.separator ' [FILE..]'
@@ -43,6 +46,7 @@ def parse_options
43
46
  opts.on('-h', '--help', 'Prints this help') do
44
47
  puts opts
45
48
  puts
49
+ puts "Available formats: #{format_options.join(', ')}"
46
50
  puts "Available extentions: #{extensions.join(', ')}"
47
51
  puts "Available parse options: #{parse_options.keys.join(', ')}"
48
52
  puts "Available render options: #{render_options.keys.join(', ')}"
@@ -51,7 +55,16 @@ def parse_options
51
55
  exit
52
56
  end
53
57
 
54
- opts.on('--html-renderer', 'Use the HtmlRenderer renderer rather than the native C renderer') do
58
+ opts.on('-tFORMAT', '--to=FORMAT', String, 'Specify output FORMAT') do |value|
59
+ value = value.to_sym
60
+ if format_options.include?(value)
61
+ options.output_format = value
62
+ else
63
+ abort("format '#{value}' not found")
64
+ end
65
+ end
66
+
67
+ opts.on('--html-renderer', 'Use the HtmlRenderer renderer rather than the native C renderer (only valid when format is html)') do
55
68
  options.renderer = true
56
69
  end
57
70
 
@@ -88,11 +101,23 @@ end
88
101
 
89
102
  options = parse_options
90
103
 
104
+ abort("format '#{options.output_format}' does not support using the HtmlRenderer renderer") if
105
+ options.renderer && options.output_format != :html
106
+
91
107
  doc = CommonMarker.render_doc(ARGF.read, options.active_parse_options, options.active_extensions)
92
108
 
93
- if options.renderer
94
- renderer = CommonMarker::HtmlRenderer.new(extensions: options.active_extensions)
95
- $stdout.write(renderer.render(doc))
96
- else
97
- $stdout.write(doc.to_html(options.active_render_options, options.active_extensions))
109
+ case options.output_format
110
+ when :html
111
+ if options.renderer
112
+ renderer = CommonMarker::HtmlRenderer.new(options: options.active_render_options, extensions: options.active_extensions)
113
+ $stdout.write(renderer.render(doc))
114
+ else
115
+ $stdout.write(doc.to_html(options.active_render_options, options.active_extensions))
116
+ end
117
+ when :xml
118
+ $stdout.write(doc.to_xml(options.active_render_options))
119
+ when :commonmark
120
+ $stdout.write(doc.to_commonmark(options.active_render_options))
121
+ when :plaintext
122
+ $stdout.write(doc.to_plaintext(options.active_render_options))
98
123
  end
@@ -186,6 +186,40 @@ static VALUE rb_markdown_to_html(VALUE self, VALUE rb_text, VALUE rb_options, VA
186
186
  return ruby_html;
187
187
  }
188
188
 
189
+ /*
190
+ * Internal: Parses a Markdown string into an HTML string.
191
+ *
192
+ */
193
+ static VALUE rb_markdown_to_xml(VALUE self, VALUE rb_text, VALUE rb_options, VALUE rb_extensions) {
194
+ char *str, *xml;
195
+ int len;
196
+ cmark_parser *parser;
197
+ cmark_node *doc;
198
+ Check_Type(rb_text, T_STRING);
199
+ Check_Type(rb_options, T_FIXNUM);
200
+
201
+ parser = prepare_parser(rb_options, rb_extensions, cmark_get_arena_mem_allocator());
202
+
203
+ str = (char *)RSTRING_PTR(rb_text);
204
+ len = RSTRING_LEN(rb_text);
205
+
206
+ cmark_parser_feed(parser, str, len);
207
+ doc = cmark_parser_finish(parser);
208
+ if (doc == NULL) {
209
+ cmark_arena_reset();
210
+ rb_raise(rb_eNodeError, "error parsing document");
211
+ }
212
+
213
+ cmark_mem *default_mem = cmark_get_default_mem_allocator();
214
+ xml = cmark_render_xml_with_mem(doc, FIX2INT(rb_options), default_mem);
215
+ cmark_arena_reset();
216
+
217
+ VALUE ruby_xml = rb_str_new2(xml);
218
+ default_mem->free(xml);
219
+
220
+ return ruby_xml;
221
+ }
222
+
189
223
  /*
190
224
  * Internal: Creates a node based on a node type.
191
225
  *
@@ -574,6 +608,28 @@ static VALUE rb_render_html(VALUE self, VALUE rb_options, VALUE rb_extensions) {
574
608
  return ruby_html;
575
609
  }
576
610
 
611
+ /* Internal: Convert the node to an XML string.
612
+ *
613
+ * Returns a {String}.
614
+ */
615
+ static VALUE rb_render_xml(VALUE self, VALUE rb_options) {
616
+ int options;
617
+ int i;
618
+ cmark_node *node;
619
+ Check_Type(rb_options, T_FIXNUM);
620
+
621
+ options = FIX2INT(rb_options);
622
+
623
+ Data_Get_Struct(self, cmark_node, node);
624
+
625
+ char *xml = cmark_render_xml(node, options);
626
+ VALUE ruby_xml = rb_str_new2(xml);
627
+
628
+ free(xml);
629
+
630
+ return ruby_xml;
631
+ }
632
+
577
633
  /* Internal: Convert the node to a CommonMark string.
578
634
  *
579
635
  * Returns a {String}.
@@ -1216,6 +1272,8 @@ __attribute__((visibility("default"))) void Init_commonmarker() {
1216
1272
  rb_cNode = rb_define_class_under(module, "Node", rb_cObject);
1217
1273
  rb_define_singleton_method(rb_cNode, "markdown_to_html", rb_markdown_to_html,
1218
1274
  3);
1275
+ rb_define_singleton_method(rb_cNode, "markdown_to_xml", rb_markdown_to_xml,
1276
+ 3);
1219
1277
  rb_define_singleton_method(rb_cNode, "new", rb_node_new, 1);
1220
1278
  rb_define_singleton_method(rb_cNode, "parse_document", rb_parse_document, 4);
1221
1279
  rb_define_method(rb_cNode, "string_content", rb_node_get_string_content, 0);
@@ -1228,6 +1286,7 @@ __attribute__((visibility("default"))) void Init_commonmarker() {
1228
1286
  rb_define_method(rb_cNode, "next", rb_node_next, 0);
1229
1287
  rb_define_method(rb_cNode, "insert_before", rb_node_insert_before, 1);
1230
1288
  rb_define_method(rb_cNode, "_render_html", rb_render_html, 2);
1289
+ rb_define_method(rb_cNode, "_render_xml", rb_render_xml, 1);
1231
1290
  rb_define_method(rb_cNode, "_render_commonmark", rb_render_commonmark, -1);
1232
1291
  rb_define_method(rb_cNode, "_render_plaintext", rb_render_plaintext, -1);
1233
1292
  rb_define_method(rb_cNode, "insert_after", rb_node_insert_after, 1);
@@ -24,7 +24,8 @@ module CommonMarker
24
24
  FULL_INFO_STRING: (1 << 16),
25
25
  UNSAFE: (1 << 17),
26
26
  FOOTNOTES: (1 << 13)
27
- }.freeze
27
+ }.freeze,
28
+ format: %i[html xml commonmark plaintext].freeze
28
29
  }.freeze
29
30
 
30
31
  def self.process_options(option, type)
@@ -30,6 +30,16 @@ module CommonMarker
30
30
  _render_html(opts, extensions).force_encoding('utf-8')
31
31
  end
32
32
 
33
+ # Public: Convert the node to an XML string.
34
+ #
35
+ # options - A {Symbol} or {Array of Symbol}s indicating the render options
36
+ #
37
+ # Returns a {String}.
38
+ def to_xml(options = :DEFAULT)
39
+ opts = Config.process_options(options, :render)
40
+ _render_xml(opts).force_encoding('utf-8')
41
+ end
42
+
33
43
  # Public: Convert the node to a CommonMark string.
34
44
  #
35
45
  # options - A {Symbol} or {Array of Symbol}s indicating the render options
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CommonMarker
4
- VERSION = '0.22.0'
4
+ VERSION = '0.23.0'
5
5
  end
@@ -28,4 +28,45 @@ class TestCommands < Minitest::Test
28
28
  assert_includes out, '<p><del>hi</del>'
29
29
  %w[<table> <tr> <th> a </th> <td> c </td>].each { |html| assert_includes out, html }
30
30
  end
31
+
32
+ def test_understands_html_format_with_renderer_and_extensions
33
+ out = make_bin('table.md', '--to=html --extension=table,strikethrough --html-renderer')
34
+ refute_includes out, '| a'
35
+ assert_includes out, '<p><del>hi</del>'
36
+ %w[<table> <tr> <th> a </th> <td> c </td>].each { |html| assert_includes out, html }
37
+ end
38
+
39
+ def test_understands_xml_format
40
+ out = make_bin('strong.md', '--to=xml')
41
+ assert_includes out, '<?xml version="1.0" encoding="UTF-8"?>'
42
+ assert_includes out, '<text xml:space="preserve">strong</text>'
43
+ end
44
+
45
+ def test_understands_commonmark_format
46
+ out = make_bin('strong.md', '--to=commonmark')
47
+ assert_equal('I am **strong**', out)
48
+ end
49
+
50
+ def test_understands_plaintext_format
51
+ out = make_bin('strong.md', '--to=plaintext')
52
+ assert_equal('I am strong', out)
53
+ end
54
+
55
+ def test_aborts_invalid_format
56
+ _out, err = capture_subprocess_io do
57
+ make_bin('strong.md', '--to=unknown')
58
+ end
59
+
60
+ assert_match "format 'unknown' not found", err
61
+ end
62
+
63
+ def test_aborts_format_and_html_renderer_combinations
64
+ (CommonMarker::Config::OPTS[:format] - [:html]).each do |format|
65
+ _out, err = capture_subprocess_io do
66
+ make_bin('strong.md', "--to=#{format} --html-renderer")
67
+ end
68
+
69
+ assert_match "format '#{format}' does not support using the HtmlRenderer renderer", err
70
+ end
71
+ end
31
72
  end
@@ -9,6 +9,9 @@ class TestEncoding < Minitest::Test
9
9
  doc = CommonMarker.render_doc(contents, :SMART)
10
10
  render = doc.to_html
11
11
  assert_equal('<p>This curly quote “makes commonmarker throw an exception”.</p>', render.rstrip)
12
+
13
+ render = doc.to_xml
14
+ assert_includes(render, '<text xml:space="preserve">This curly quote “makes commonmarker throw an exception”.</text>')
12
15
  end
13
16
 
14
17
  def test_string_content_is_utf8
data/test/test_xml.rb ADDED
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ class TestXml < Minitest::Test
6
+ def setup
7
+ @markdown = <<~MD
8
+ Hi *there*!
9
+
10
+ 1. I am a numeric list.
11
+ 2. I continue the list.
12
+ * Suddenly, an unordered list!
13
+ * What fun!
14
+
15
+ Okay, _enough_.
16
+
17
+ | a | b |
18
+ | --- | --- |
19
+ | c | d |
20
+ MD
21
+ end
22
+
23
+ def render_doc(doc)
24
+ CommonMarker.render_doc(doc, :DEFAULT, [:table])
25
+ end
26
+
27
+ def test_to_xml
28
+ compare = render_doc(@markdown).to_xml(:SOURCEPOS)
29
+
30
+ assert_equal <<~XML, compare
31
+ <?xml version="1.0" encoding="UTF-8"?>
32
+ <!DOCTYPE document SYSTEM "CommonMark.dtd">
33
+ <document sourcepos="1:1-12:13" xmlns="http://commonmark.org/xml/1.0">
34
+ <paragraph sourcepos="1:1-1:11">
35
+ <text sourcepos="1:1-1:3" xml:space="preserve">Hi </text>
36
+ <emph sourcepos="1:4-1:10">
37
+ <text sourcepos="1:5-1:9" xml:space="preserve">there</text>
38
+ </emph>
39
+ <text sourcepos="1:11-1:11" xml:space="preserve">!</text>
40
+ </paragraph>
41
+ <list sourcepos="3:1-4:23" type="ordered" start="1" delim="period" tight="true">
42
+ <item sourcepos="3:1-3:23">
43
+ <paragraph sourcepos="3:4-3:23">
44
+ <text sourcepos="3:4-3:23" xml:space="preserve">I am a numeric list.</text>
45
+ </paragraph>
46
+ </item>
47
+ <item sourcepos="4:1-4:23">
48
+ <paragraph sourcepos="4:4-4:23">
49
+ <text sourcepos="4:4-4:23" xml:space="preserve">I continue the list.</text>
50
+ </paragraph>
51
+ </item>
52
+ </list>
53
+ <list sourcepos="5:1-7:0" type="bullet" tight="true">
54
+ <item sourcepos="5:1-5:30">
55
+ <paragraph sourcepos="5:3-5:30">
56
+ <text sourcepos="5:3-5:30" xml:space="preserve">Suddenly, an unordered list!</text>
57
+ </paragraph>
58
+ </item>
59
+ <item sourcepos="6:1-7:0">
60
+ <paragraph sourcepos="6:3-6:11">
61
+ <text sourcepos="6:3-6:11" xml:space="preserve">What fun!</text>
62
+ </paragraph>
63
+ </item>
64
+ </list>
65
+ <paragraph sourcepos="8:1-8:15">
66
+ <text sourcepos="8:1-8:6" xml:space="preserve">Okay, </text>
67
+ <emph sourcepos="8:7-8:14">
68
+ <text sourcepos="8:8-8:13" xml:space="preserve">enough</text>
69
+ </emph>
70
+ <text sourcepos="8:15-8:15" xml:space="preserve">.</text>
71
+ </paragraph>
72
+ <table sourcepos="10:1-12:13">
73
+ <table_header sourcepos="10:1-10:13">
74
+ <table_cell sourcepos="10:2-10:6">
75
+ <text sourcepos="10:3-10:3" xml:space="preserve">a</text>
76
+ </table_cell>
77
+ <table_cell sourcepos="10:8-10:12">
78
+ <text sourcepos="10:9-10:9" xml:space="preserve">b</text>
79
+ </table_cell>
80
+ </table_header>
81
+ <table_row sourcepos="12:1-12:13">
82
+ <table_cell sourcepos="12:2-12:6">
83
+ <text sourcepos="12:3-12:3" xml:space="preserve">c</text>
84
+ </table_cell>
85
+ <table_cell sourcepos="12:8-12:12">
86
+ <text sourcepos="12:9-12:9" xml:space="preserve">d</text>
87
+ </table_cell>
88
+ </table_row>
89
+ </table>
90
+ </document>
91
+ XML
92
+ end
93
+
94
+ def test_to_xml_with_quotes
95
+ compare = render_doc('"quotes" should be escaped').to_xml(:DEFAULT)
96
+
97
+ assert_equal <<~XML, compare
98
+ <?xml version="1.0" encoding="UTF-8"?>
99
+ <!DOCTYPE document SYSTEM "CommonMark.dtd">
100
+ <document xmlns="http://commonmark.org/xml/1.0">
101
+ <paragraph>
102
+ <text xml:space="preserve">&quot;quotes&quot; should be escaped</text>
103
+ </paragraph>
104
+ </document>
105
+ XML
106
+ end
107
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: commonmarker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.22.0
4
+ version: 0.23.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Garen Torikian
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-06-20 00:00:00.000000000 Z
12
+ date: 2021-08-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: awesome_print
@@ -254,6 +254,7 @@ files:
254
254
  - test/test_smartpunct.rb
255
255
  - test/test_spec.rb
256
256
  - test/test_tasklists.rb
257
+ - test/test_xml.rb
257
258
  homepage: https://github.com/gjtorikian/commonmarker
258
259
  licenses:
259
260
  - MIT
@@ -306,6 +307,7 @@ test_files:
306
307
  - test/test_helper.rb
307
308
  - test/test_options.rb
308
309
  - test/benchmark.rb
310
+ - test/test_xml.rb
309
311
  - test/test_basics.rb
310
312
  - test/test_renderer.rb
311
313
  - test/test_gc.rb