commonmarker 0.21.1 → 0.23.1

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: 1a1380e99f120ae24a27bae6244ffeab5b0b3fe828155828cf8c092d8c83471a
4
- data.tar.gz: 92c85125c0e4d6f8673312e64e67feda6993a4fc1250661fad3714836bc96454
3
+ metadata.gz: 1f620ec82d102c4726e551911d21a5d87e7185010ba711f6ddc35495184c90ba
4
+ data.tar.gz: cf14b3a68481564facd7fbb46c90c822757d168db7eb41786b3a2d0ee3173e66
5
5
  SHA512:
6
- metadata.gz: 5f136c3413348cea8e3a0f3e8b0242ffc8bba92b9004278170ee7d2532e87b6c5d6af73c952d2c31d509143ae2cfe63d96aeca6ff4749a599fefbcad7580214e
7
- data.tar.gz: 4d33d9cb161d0b484ed9bc353c9c01dd3f8715f533c05a2f7f61a16792433153d19324d1b62a0ae6b6750441917843495349a1140e93d2b1960794ea0346e444
6
+ metadata.gz: 6a4d50388ad098dd9ccfd7e733e9a1274841e4b5d929d76e4f2e75ec10c55889ee85ef55afdb5ef5665214e6abde73e75bcd647fd2a53c6aa985a9396922992b
7
+ data.tar.gz: a1e587570999fbc0c5f6423f7dcf375a8b2ffee2339939e2ca80807b5950c042656a498ea4acfaf3b60ba45d14fdc8f4655a0a936a06d966a89d3f9133a32188
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # CommonMarker
2
2
 
3
- [![Build Status](https://travis-ci.org/gjtorikian/commonmarker.svg)](https://travis-ci.org/gjtorikian/commonmarker) [![Gem Version](https://badge.fury.io/rb/commonmarker.svg)](http://badge.fury.io/rb/commonmarker)
3
+ ![Build Status](https://github.com/gjtorikian/commonmarker/workflows/CI/badge.svg) [![Gem Version](https://badge.fury.io/rb/commonmarker.svg)](http://badge.fury.io/rb/commonmarker)
4
4
 
5
5
  Ruby wrapper for [libcmark-gfm](https://github.com/github/cmark),
6
6
  GitHub's fork of the reference parser for CommonMark. It passes all of the C tests, and is therefore spec-complete. It also includes extensions to the CommonMark spec as documented in the [GitHub Flavored Markdown spec](http://github.github.com/gfm/), such as support for tables, strikethroughs, and autolinking.
@@ -130,25 +130,31 @@ CommonMarker accepts the same options that CMark does, as symbols. Note that the
130
130
  | Name | Description
131
131
  | ----------------------------- | -----------
132
132
  | `:DEFAULT` | The default parsing system.
133
+ | `:SOURCEPOS` | Include source position in nodes
133
134
  | `:UNSAFE` | Allow raw/custom HTML and unsafe links.
134
- | `:FOOTNOTES` | Parse footnotes.
135
- | `:LIBERAL_HTML_TAG` | Support liberal parsing of inline HTML tags.
135
+ | `:VALIDATE_UTF8` | Replace illegal sequences with the replacement character `U+FFFD`.
136
136
  | `:SMART` | Use smart punctuation (curly quotes, etc.).
137
+ | `:LIBERAL_HTML_TAG` | Support liberal parsing of inline HTML tags.
138
+ | `:FOOTNOTES` | Parse footnotes.
137
139
  | `:STRIKETHROUGH_DOUBLE_TILDE` | Parse strikethroughs by double tildes (compatibility with [redcarpet](https://github.com/vmg/redcarpet))
138
- | `:VALIDATE_UTF8` | Replace illegal sequences with the replacement character `U+FFFD`.
139
140
 
140
141
  ### Render options
141
142
 
142
- | Name | Description |
143
- | ------------------ | ----------- |
144
- | `:DEFAULT` | The default rendering system. |
145
- | `:UNSAFE` | Allow raw/custom HTML and unsafe links. |
146
- | `:GITHUB_PRE_LANG` | Use GitHub-style `<pre lang>` for fenced code blocks. |
147
- | `:HARDBREAKS` | Treat `\n` as hardbreaks (by adding `<br/>`). |
148
- | `:NOBREAKS` | Translate `\n` in the source to a single whitespace. |
149
- | `:SOURCEPOS` | Include source position in rendered HTML. |
150
- | `:TABLE_PREFER_STYLE_ATTRIBUTES` | Use `style` insted of `align` for table cells |
151
- | `:FULL_INFO_STRING` | Include full info strings of code blocks in separate attribute |
143
+ | Name | Description |
144
+ | ------------------ | ----------- |
145
+ | `:DEFAULT` | The default rendering system. |
146
+ | `:SOURCEPOS` | Include source position in rendered HTML. |
147
+ | `:HARDBREAKS` | Treat `\n` as hardbreaks (by adding `<br/>`). |
148
+ | `:UNSAFE` | Allow raw/custom HTML and unsafe links. |
149
+ | `:NOBREAKS` | Translate `\n` in the source to a single whitespace. |
150
+ | `:VALIDATE_UTF8` | Replace illegal sequences with the replacement character `U+FFFD`. |
151
+ | `:SMART` | Use smart punctuation (curly quotes, etc.). |
152
+ | `:GITHUB_PRE_LANG` | Use GitHub-style `<pre lang>` for fenced code blocks. |
153
+ | `:LIBERAL_HTML_TAG` | Support liberal parsing of inline HTML tags. |
154
+ | `:FOOTNOTES` | Render footnotes. |
155
+ | `:STRIKETHROUGH_DOUBLE_TILDE` | Parse strikethroughs by double tildes (compatibility with [redcarpet](https://github.com/vmg/redcarpet)) |
156
+ | `:TABLE_PREFER_STYLE_ATTRIBUTES` | Use `style` insted of `align` for table cells. |
157
+ | `:FULL_INFO_STRING` | Include full info strings of code blocks in separate attribute. |
152
158
 
153
159
  ### Passing options
154
160
 
@@ -179,6 +185,76 @@ The available extensions are:
179
185
  * `:autolink` - This provides support for automatically converting URLs to anchor tags.
180
186
  * `:tagfilter` - This escapes [several "unsafe" HTML tags](https://github.github.com/gfm/#disallowed-raw-html-extension-), causing them to not have any effect.
181
187
 
188
+ ## Output formats
189
+
190
+ Like CMark, CommonMarker can generate output in several formats: HTML, XML, plaintext, and commonmark are currently supported.
191
+
192
+ ### HTML
193
+
194
+ The default output format, HTML, will be generated when calling `to_html` or using `--to=html` on the command line.
195
+
196
+ ```ruby
197
+ doc = CommonMarker.render_doc('*Hello* world!', :DEFAULT)
198
+ puts(doc.to_html)
199
+
200
+ <p><em>Hello</em> world!</p>
201
+ ```
202
+
203
+ ### XML
204
+
205
+ XML will be generated when calling `to_xml` or using `--to=xml` on the command line.
206
+
207
+ ```ruby
208
+ doc = CommonMarker.render_doc('*Hello* world!', :DEFAULT)
209
+ puts(doc.to_xml)
210
+
211
+ <?xml version="1.0" encoding="UTF-8"?>
212
+ <!DOCTYPE document SYSTEM "CommonMark.dtd">
213
+ <document xmlns="http://commonmark.org/xml/1.0">
214
+ <paragraph>
215
+ <emph>
216
+ <text xml:space="preserve">Hello</text>
217
+ </emph>
218
+ <text xml:space="preserve"> world!</text>
219
+ </paragraph>
220
+ </document>
221
+ ```
222
+
223
+ ### Plaintext
224
+
225
+ Plaintext will be generated when calling `to_plaintext` or using `--to=plaintext` on the command line.
226
+
227
+ ```ruby
228
+ doc = CommonMarker.render_doc('*Hello* world!', :DEFAULT)
229
+ puts(doc.to_plaintext)
230
+
231
+ Hello world!
232
+ ```
233
+
234
+ ### Commonmark
235
+
236
+ Commonmark will be generated when calling `to_commonmark` or using `--to=commonmark` on the command line.
237
+
238
+ ``` ruby
239
+ text = <<-TEXT
240
+ 1. I am a numeric list.
241
+ 2. I continue the list.
242
+ * Suddenly, an unordered list!
243
+ * What fun!
244
+ TEXT
245
+
246
+ doc = CommonMarker.render_doc(text, :DEFAULT)
247
+ puts(doc.to_commonmark)
248
+
249
+ 1. I am a numeric list.
250
+ 2. I continue the list.
251
+
252
+ <!-- end list -->
253
+
254
+ - Suddenly, an unordered list\!
255
+ - What fun\!
256
+ ```
257
+
182
258
  ## Developing locally
183
259
 
184
260
  After cloning the repo:
data/bin/commonmarker CHANGED
@@ -13,15 +13,18 @@ $LOAD_PATH.unshift File.expand_path('lib', root)
13
13
  def parse_options
14
14
  options = OpenStruct.new
15
15
  extensions = CommonMarker.extensions
16
- parse_options = CommonMarker::Config::Parse
17
- render_options = CommonMarker::Config::Render
16
+ parse_options = CommonMarker::Config::OPTS.fetch(:parse)
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
data/commonmarker.gemspec CHANGED
@@ -21,11 +21,10 @@ Gem::Specification.new do |s|
21
21
 
22
22
  s.executables = ['commonmarker']
23
23
  s.require_paths = %w[lib ext]
24
+ s.required_ruby_version = ['>= 2.6', '< 4.0']
24
25
 
25
26
  s.rdoc_options += ['-x', 'ext/commonmarker/cmark/.*']
26
27
 
27
- s.add_dependency 'ruby-enum', '~> 0.5'
28
-
29
28
  s.add_development_dependency 'awesome_print'
30
29
  s.add_development_dependency 'json', '~> 2.3'
31
30
  s.add_development_dependency 'minitest', '~> 5.6'
@@ -45,6 +45,13 @@ static VALUE encode_utf8_string(const char *c_string) {
45
45
  return string;
46
46
  }
47
47
 
48
+ /* Encode a C string using the encoding from Ruby string +source+. */
49
+ static VALUE encode_source_string(const char *c_string, VALUE source) {
50
+ VALUE string = rb_str_new2(c_string);
51
+ rb_enc_copy(string, source);
52
+ return string;
53
+ }
54
+
48
55
  static void rb_mark_c_struct(void *data) {
49
56
  cmark_node *node = data;
50
57
  cmark_node *child;
@@ -179,6 +186,40 @@ static VALUE rb_markdown_to_html(VALUE self, VALUE rb_text, VALUE rb_options, VA
179
186
  return ruby_html;
180
187
  }
181
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
+
182
223
  /*
183
224
  * Internal: Creates a node based on a node type.
184
225
  *
@@ -567,6 +608,28 @@ static VALUE rb_render_html(VALUE self, VALUE rb_options, VALUE rb_extensions) {
567
608
  return ruby_html;
568
609
  }
569
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
+
570
633
  /* Internal: Convert the node to a CommonMark string.
571
634
  *
572
635
  * Returns a {String}.
@@ -1130,7 +1193,8 @@ static VALUE rb_html_escape_href(VALUE self, VALUE rb_text) {
1130
1193
  if (houdini_escape_href(&buf, (const uint8_t *)RSTRING_PTR(rb_text),
1131
1194
  RSTRING_LEN(rb_text))) {
1132
1195
  result = (char *)cmark_strbuf_detach(&buf);
1133
- return rb_str_new2(result);
1196
+ return encode_source_string(result, rb_text);
1197
+
1134
1198
  }
1135
1199
 
1136
1200
  return rb_text;
@@ -1150,7 +1214,7 @@ static VALUE rb_html_escape_html(VALUE self, VALUE rb_text) {
1150
1214
  if (houdini_escape_html0(&buf, (const uint8_t *)RSTRING_PTR(rb_text),
1151
1215
  RSTRING_LEN(rb_text), 0)) {
1152
1216
  result = (char *)cmark_strbuf_detach(&buf);
1153
- return rb_str_new2(result);
1217
+ return encode_source_string(result, rb_text);
1154
1218
  }
1155
1219
 
1156
1220
  return rb_text;
@@ -1208,6 +1272,8 @@ __attribute__((visibility("default"))) void Init_commonmarker() {
1208
1272
  rb_cNode = rb_define_class_under(module, "Node", rb_cObject);
1209
1273
  rb_define_singleton_method(rb_cNode, "markdown_to_html", rb_markdown_to_html,
1210
1274
  3);
1275
+ rb_define_singleton_method(rb_cNode, "markdown_to_xml", rb_markdown_to_xml,
1276
+ 3);
1211
1277
  rb_define_singleton_method(rb_cNode, "new", rb_node_new, 1);
1212
1278
  rb_define_singleton_method(rb_cNode, "parse_document", rb_parse_document, 4);
1213
1279
  rb_define_method(rb_cNode, "string_content", rb_node_get_string_content, 0);
@@ -1220,6 +1286,7 @@ __attribute__((visibility("default"))) void Init_commonmarker() {
1220
1286
  rb_define_method(rb_cNode, "next", rb_node_next, 0);
1221
1287
  rb_define_method(rb_cNode, "insert_before", rb_node_insert_before, 1);
1222
1288
  rb_define_method(rb_cNode, "_render_html", rb_render_html, 2);
1289
+ rb_define_method(rb_cNode, "_render_xml", rb_render_xml, 1);
1223
1290
  rb_define_method(rb_cNode, "_render_commonmark", rb_render_commonmark, -1);
1224
1291
  rb_define_method(rb_cNode, "_render_plaintext", rb_render_plaintext, -1);
1225
1292
  rb_define_method(rb_cNode, "insert_after", rb_node_insert_after, 1);
@@ -1,53 +1,52 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'ruby-enum'
4
3
  module CommonMarker
5
4
  # For Ruby::Enum, these must be classes, not modules
6
5
  module Config
7
- class Parse
8
- include Ruby::Enum
9
-
10
- define :DEFAULT, 0
11
- define :VALIDATE_UTF8, (1 << 9)
12
- define :SMART, (1 << 10)
13
- define :LIBERAL_HTML_TAG, (1 << 12)
14
- define :FOOTNOTES, (1 << 13)
15
- define :STRIKETHROUGH_DOUBLE_TILDE, (1 << 14)
16
- define :UNSAFE, (1 << 17)
17
- end
18
-
19
- class Render
20
- include Ruby::Enum
21
-
22
- define :DEFAULT, 0
23
- define :SOURCEPOS, (1 << 1)
24
- define :HARDBREAKS, (1 << 2)
25
- define :NOBREAKS, (1 << 4)
26
- define :GITHUB_PRE_LANG, (1 << 11)
27
- define :TABLE_PREFER_STYLE_ATTRIBUTES, (1 << 15)
28
- define :FULL_INFO_STRING, (1 << 16)
29
- define :UNSAFE, (1 << 17)
30
- end
6
+ # See https://github.com/github/cmark-gfm/blob/master/src/cmark-gfm.h#L673
7
+ OPTS = {
8
+ parse: {
9
+ DEFAULT: 0,
10
+ SOURCEPOS: (1 << 1),
11
+ UNSAFE: (1 << 17),
12
+ VALIDATE_UTF8: (1 << 9),
13
+ SMART: (1 << 10),
14
+ LIBERAL_HTML_TAG: (1 << 12),
15
+ FOOTNOTES: (1 << 13),
16
+ STRIKETHROUGH_DOUBLE_TILDE: (1 << 14),
17
+ }.freeze,
18
+ render: {
19
+ DEFAULT: 0,
20
+ SOURCEPOS: (1 << 1),
21
+ HARDBREAKS: (1 << 2),
22
+ UNSAFE: (1 << 17),
23
+ NOBREAKS: (1 << 4),
24
+ VALIDATE_UTF8: (1 << 9),
25
+ SMART: (1 << 10),
26
+ GITHUB_PRE_LANG: (1 << 11),
27
+ LIBERAL_HTML_TAG: (1 << 12),
28
+ FOOTNOTES: (1 << 13),
29
+ STRIKETHROUGH_DOUBLE_TILDE: (1 << 14),
30
+ TABLE_PREFER_STYLE_ATTRIBUTES: (1 << 15),
31
+ FULL_INFO_STRING: (1 << 16),
32
+ }.freeze,
33
+ format: %i[html xml commonmark plaintext].freeze
34
+ }.freeze
31
35
 
32
36
  def self.process_options(option, type)
33
- type = Config.const_get(type.capitalize)
34
- if option.is_a?(Symbol)
35
- check_option(option, type)
36
- type.to_h[option]
37
- elsif option.is_a?(Array)
38
- option = [nil] if option.empty?
37
+ case option
38
+ when Symbol
39
+ OPTS.fetch(type).fetch(option)
40
+ when Array
41
+ raise TypeError if option.none?
42
+
39
43
  # neckbearding around. the map will both check the opts and then bitwise-OR it
40
- option.map do |o|
41
- check_option(o, type)
42
- type.to_h[o]
43
- end.inject(0, :|)
44
+ OPTS.fetch(type).fetch_values(*option).inject(0, :|)
44
45
  else
45
- raise TypeError, "option type must be a valid symbol or array of symbols within the #{type} context"
46
+ raise TypeError, "option type must be a valid symbol or array of symbols within the #{name}::OPTS[:#{type}] context"
46
47
  end
47
- end
48
-
49
- def self.check_option(option, type)
50
- raise TypeError, "option ':#{option}' does not exist for #{type}" unless type.key?(option)
48
+ rescue KeyError => e
49
+ raise TypeError, "option ':#{e.key}' does not exist for #{name}::OPTS[:#{type}]"
51
50
  end
52
51
  end
53
52
  end
@@ -27,11 +27,9 @@ module CommonMarker
27
27
  list_tight
28
28
  fence_info
29
29
  ].map do |name|
30
- begin
31
- [name, __send__(name)]
32
- rescue NodeError
33
- nil
34
- end
30
+ [name, __send__(name)]
31
+ rescue NodeError
32
+ nil
35
33
  end.compact
36
34
 
37
35
  printer.seplist(attrs) do |name, value|
@@ -11,7 +11,7 @@ module CommonMarker
11
11
  #
12
12
  # blk - A {Proc} representing the action to take for each child
13
13
  def walk(&block)
14
- return enum_for(:walk) unless block_given?
14
+ return enum_for(:walk) unless block
15
15
 
16
16
  yield self
17
17
  each do |child|
@@ -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
@@ -6,6 +6,7 @@ require 'stringio'
6
6
  module CommonMarker
7
7
  class Renderer
8
8
  attr_accessor :in_tight, :warnings, :in_plain
9
+
9
10
  def initialize(options: :DEFAULT, extensions: [])
10
11
  @opts = Config.process_options(options, :render)
11
12
  @stream = StringIO.new(+'')
@@ -18,11 +19,12 @@ module CommonMarker
18
19
 
19
20
  def out(*args)
20
21
  args.each do |arg|
21
- if arg == :children
22
+ case arg
23
+ when :children
22
24
  @node.each { |child| out(child) }
23
- elsif arg.is_a?(Array)
25
+ when Array
24
26
  arg.each { |x| render(x) }
25
- elsif arg.is_a?(Node)
27
+ when Node
26
28
  render(arg)
27
29
  else
28
30
  @stream.write(arg)
@@ -127,7 +129,7 @@ module CommonMarker
127
129
  end
128
130
 
129
131
  def option_enabled?(opt)
130
- (@opts & CommonMarker::Config::Render.value(opt)) != 0
132
+ (@opts & CommonMarker::Config::OPTS.dig(:render, opt)) != 0
131
133
  end
132
134
  end
133
135
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CommonMarker
4
- VERSION = '0.21.1'
4
+ VERSION = '0.23.1'
5
5
  end
data/test/test_basics.rb CHANGED
@@ -15,4 +15,21 @@ class TestBasics < Minitest::Test
15
15
  html = CommonMarker.render_html('Hi *there*')
16
16
  assert_equal "<p>Hi <em>there</em></p>\n", html
17
17
  end
18
+
19
+ # basic test that just checks if every option is accepted & no errors are thrown
20
+ def test_accept_every_option
21
+ text = "Hello **world** -- how are _you_ today? I'm ~~fine~~, ~yourself~?"
22
+ parse_opt = [:SOURCEPOS, :UNSAFE, :VALIDATE_UTF8, :SMART, :LIBERAL_HTML_TAG, :FOOTNOTES, :STRIKETHROUGH_DOUBLE_TILDE]
23
+ render_opt = parse_opt + [:HARDBREAKS, :NOBREAKS, :GITHUB_PRE_LANG, :TABLE_PREFER_STYLE_ATTRIBUTES, :FULL_INFO_STRING]
24
+
25
+ extensions = %i[table tasklist strikethrough autolink tagfilter]
26
+
27
+ assert_equal "<p>Hello <strong>world</strong> – how are <em>you</em> today? I’m <del>fine</del>, ~yourself~?</p>\n", CommonMarker.render_doc(text, parse_opt, extensions).to_html
28
+
29
+ # note how tho the doc returned has sourcepos info, by default the renderer
30
+ # won't emit it. for that we need to pass in the render opt
31
+ assert_equal "<p data-sourcepos=\"1:1-1:65\">Hello <strong>world</strong> – how are <em>you</em> today? I’m <del>fine</del>, ~yourself~?</p>\n", CommonMarker.render_doc(text, parse_opt, extensions).to_html(render_opt, extensions)
32
+
33
+ assert_equal "<p data-sourcepos=\"1:1-1:65\">Hello <strong>world</strong> – how are <em>you</em> today? I’m <del>fine</del>, ~yourself~?</p>\n", CommonMarker.render_html(text, parse_opt, extensions)
34
+ end
18
35
  end
@@ -5,27 +5,68 @@ require 'test_helper'
5
5
  class TestCommands < Minitest::Test
6
6
  def test_basic
7
7
  out = make_bin('strong.md')
8
- assert_equal out, '<p>I am <strong>strong</strong></p>'
8
+ assert_equal('<p>I am <strong>strong</strong></p>', out)
9
9
  end
10
10
 
11
11
  def test_does_not_have_extensions
12
12
  out = make_bin('table.md')
13
- assert out.include?('| a')
14
- refute out.include?('<p><del>hi</del>')
15
- refute out.include?('<table> <tr> <th> a </th> <td> c </td>')
13
+ assert_includes out, '| a'
14
+ refute_includes out, '<p><del>hi</del>'
15
+ refute_includes out, '<table> <tr> <th> a </th> <td> c </td>'
16
16
  end
17
17
 
18
18
  def test_understands_extensions
19
19
  out = make_bin('table.md', '--extension=table')
20
- refute out.include?('| a')
21
- refute out.include?('<p><del>hi</del>')
22
- %w[<table> <tr> <th> a </th> <td> c </td>].each { |html| assert out.include?(html) }
20
+ refute_includes out, '| a'
21
+ refute_includes out, '<p><del>hi</del>'
22
+ %w[<table> <tr> <th> a </th> <td> c </td>].each { |html| assert_includes out, html }
23
23
  end
24
24
 
25
25
  def test_understands_multiple_extensions
26
26
  out = make_bin('table.md', '--extension=table,strikethrough')
27
- refute out.include?('| a')
28
- assert out.include?('<p><del>hi</del>')
29
- %w[<table> <tr> <th> a </th> <td> c </td>].each { |html| assert out.include?(html) }
27
+ refute_includes out, '| a'
28
+ assert_includes out, '<p><del>hi</del>'
29
+ %w[<table> <tr> <th> a </th> <td> c </td>].each { |html| assert_includes out, html }
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
30
71
  end
31
72
  end
@@ -30,7 +30,7 @@ class TestCommonmark < Minitest::Test
30
30
  compare = render_doc(@markdown).to_commonmark
31
31
 
32
32
  assert_equal \
33
- render_doc(@markdown).to_html.gsub(/ +/, ' ').gsub(HTML_COMMENT, ''),
34
- render_doc(compare).to_html.gsub(/ +/, ' ').gsub(HTML_COMMENT, '')
33
+ render_doc(@markdown).to_html.squeeze(' ').gsub(HTML_COMMENT, ''),
34
+ render_doc(compare).to_html.squeeze(' ').gsub(HTML_COMMENT, '')
35
35
  end
36
36
  end
data/test/test_doc.rb CHANGED
@@ -17,114 +17,114 @@ class TestDocNode < Minitest::Test
17
17
  end
18
18
 
19
19
  def test_get_type
20
- assert_equal @doc.type, :document
20
+ assert_equal(:document, @doc.type)
21
21
  end
22
22
 
23
23
  def test_get_type_string
24
- assert_equal @doc.type_string, 'document'
24
+ assert_equal('document', @doc.type_string)
25
25
  end
26
26
 
27
27
  def test_get_first_child
28
- assert_equal @first_child.type, :paragraph
28
+ assert_equal(:paragraph, @first_child.type)
29
29
  end
30
30
 
31
31
  def test_get_next
32
- assert_equal @first_child.first_child.next.type, :emph
32
+ assert_equal(:emph, @first_child.first_child.next.type)
33
33
  end
34
34
 
35
35
  def test_insert_before
36
36
  paragraph = Node.new(:paragraph)
37
- assert_equal @first_child.insert_before(paragraph), true
37
+ assert(@first_child.insert_before(paragraph))
38
38
  assert_match "<p></p>\n<p>Hi <em>there</em>.", @doc.to_html
39
39
  end
40
40
 
41
41
  def test_insert_after
42
42
  paragraph = Node.new(:paragraph)
43
- assert_equal @first_child.insert_after(paragraph), true
43
+ assert(@first_child.insert_after(paragraph))
44
44
  assert_match "<strong>many nodes</strong>!</p>\n<p></p>\n", @doc.to_html
45
45
  end
46
46
 
47
47
  def test_prepend_child
48
48
  code = Node.new(:code)
49
- assert_equal @first_child.prepend_child(code), true
49
+ assert(@first_child.prepend_child(code))
50
50
  assert_match '<p><code></code>Hi <em>there</em>.', @doc.to_html
51
51
  end
52
52
 
53
53
  def test_append_child
54
54
  strong = Node.new(:strong)
55
- assert_equal @first_child.append_child(strong), true
55
+ assert(@first_child.append_child(strong))
56
56
  assert_match "!<strong></strong></p>\n", @doc.to_html
57
57
  end
58
58
 
59
59
  def test_get_last_child
60
- assert_equal @last_child.type, :paragraph
60
+ assert_equal(:paragraph, @last_child.type)
61
61
  end
62
62
 
63
63
  def test_get_parent
64
- assert_equal @first_child.first_child.next.parent.type, :paragraph
64
+ assert_equal(:paragraph, @first_child.first_child.next.parent.type)
65
65
  end
66
66
 
67
67
  def test_get_previous
68
- assert_equal @first_child.first_child.next.previous.type, :text
68
+ assert_equal(:text, @first_child.first_child.next.previous.type)
69
69
  end
70
70
 
71
71
  def test_get_url
72
- assert_equal @link.url, 'https://www.github.com'
72
+ assert_equal('https://www.github.com', @link.url)
73
73
  end
74
74
 
75
75
  def test_set_url
76
- assert_equal @link.url = 'https://www.mozilla.org', 'https://www.mozilla.org'
76
+ assert_equal('https://www.mozilla.org', @link.url = 'https://www.mozilla.org')
77
77
  end
78
78
 
79
79
  def test_get_title
80
- assert_equal @image.title, 'Favicon'
80
+ assert_equal('Favicon', @image.title)
81
81
  end
82
82
 
83
83
  def test_set_title
84
- assert_equal @image.title = 'Octocat', 'Octocat'
84
+ assert_equal('Octocat', @image.title = 'Octocat')
85
85
  end
86
86
 
87
87
  def test_get_header_level
88
- assert_equal @header.header_level, 3
88
+ assert_equal(3, @header.header_level)
89
89
  end
90
90
 
91
91
  def test_set_header_level
92
- assert_equal @header.header_level = 6, 6
92
+ assert_equal(6, @header.header_level = 6)
93
93
  end
94
94
 
95
95
  def test_get_list_type
96
- assert_equal @ul_list.list_type, :bullet_list
97
- assert_equal @ol_list.list_type, :ordered_list
96
+ assert_equal(:bullet_list, @ul_list.list_type)
97
+ assert_equal(:ordered_list, @ol_list.list_type)
98
98
  end
99
99
 
100
100
  def test_set_list_type
101
- assert_equal @ul_list.list_type = :ordered_list, :ordered_list
102
- assert_equal @ol_list.list_type = :bullet_list, :bullet_list
101
+ assert_equal(:ordered_list, @ul_list.list_type = :ordered_list)
102
+ assert_equal(:bullet_list, @ol_list.list_type = :bullet_list)
103
103
  end
104
104
 
105
105
  def test_get_list_start
106
- assert_equal @ol_list.list_start, 1
106
+ assert_equal(1, @ol_list.list_start)
107
107
  end
108
108
 
109
109
  def test_set_list_start
110
- assert_equal @ol_list.list_start = 8, 8
110
+ assert_equal(8, @ol_list.list_start = 8)
111
111
  end
112
112
 
113
113
  def test_get_list_tight
114
- assert_equal @ul_list.list_tight, true
115
- assert_equal @ol_list.list_tight, true
114
+ assert(@ul_list.list_tight)
115
+ assert(@ol_list.list_tight)
116
116
  end
117
117
 
118
118
  def test_set_list_tight
119
- assert_equal @ul_list.list_tight = false, false
120
- assert_equal @ol_list.list_tight = false, false
119
+ refute(@ul_list.list_tight = false)
120
+ refute(@ol_list.list_tight = false)
121
121
  end
122
122
 
123
123
  def test_get_fence_info
124
- assert_equal @fence.fence_info, 'ruby'
124
+ assert_equal('ruby', @fence.fence_info)
125
125
  end
126
126
 
127
127
  def test_set_fence_info
128
- assert_equal @fence.fence_info = 'javascript', 'javascript'
128
+ assert_equal('javascript', @fence.fence_info = 'javascript')
129
129
  end
130
130
  end
@@ -8,13 +8,16 @@ class TestEncoding < Minitest::Test
8
8
  contents = fixtures_file('curly.md')
9
9
  doc = CommonMarker.render_doc(contents, :SMART)
10
10
  render = doc.to_html
11
- assert_equal render.rstrip, '<p>This curly quote “makes commonmarker throw an exception”.</p>'
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
15
18
  doc = CommonMarker.render_doc('Hi *there*')
16
19
  text = doc.first_child.last_child.first_child
17
- assert_equal text.string_content, 'there'
18
- assert_equal text.string_content.encoding.name, 'UTF-8'
20
+ assert_equal('there', text.string_content)
21
+ assert_equal('UTF-8', text.string_content.encoding.name)
19
22
  end
20
23
  end
@@ -9,30 +9,33 @@ class TestExtensions < Minitest::Test
9
9
 
10
10
  def test_uses_specified_extensions
11
11
  CommonMarker.render_html(@markdown, :DEFAULT, %i[]).tap do |out|
12
- assert out.include?('| a')
13
- assert out.include?('| <strong>x</strong>')
14
- assert out.include?('~~hi~~')
12
+ assert_includes out, '| a'
13
+ assert_includes out, '| <strong>x</strong>'
14
+ assert_includes out, '~~hi~~'
15
15
  end
16
16
 
17
17
  CommonMarker.render_html(@markdown, :DEFAULT, %i[table]).tap do |out|
18
- refute out.include?('| a')
19
- %w[<table> <tr> <th> a </th> <td> c </td> <strong>x</strong>].each { |html| assert out.include?(html) }
20
- assert out.include?('~~hi~~')
18
+ refute_includes out, '| a'
19
+ %w[<table> <tr> <th> a </th> <td> c </td> <strong>x</strong>].each { |html| assert_includes out, html }
20
+ assert_includes out, '~~hi~~'
21
21
  end
22
22
 
23
23
  CommonMarker.render_html(@markdown, :DEFAULT, %i[strikethrough]).tap do |out|
24
- assert out.include?('| a')
25
- refute out.include?('~~hi~~')
26
- assert out.include?('<del>hi</del>')
24
+ assert_includes out, '| a'
25
+ refute_includes out, '~~hi~~'
26
+ assert_includes out, '<del>hi</del>'
27
27
  end
28
28
 
29
29
  doc = CommonMarker.render_doc('~a~ ~~b~~ ~~~c~~~', :STRIKETHROUGH_DOUBLE_TILDE, [:strikethrough])
30
- assert_equal doc.to_html, "<p>~a~ <del>b</del> ~~~c~~~</p>\n"
30
+ assert_equal("<p>~a~ <del>b</del> ~~~c~~~</p>\n", doc.to_html)
31
+
32
+ html = CommonMarker.render_html('~a~ ~~b~~ ~~~c~~~', :STRIKETHROUGH_DOUBLE_TILDE, [:strikethrough])
33
+ assert_equal("<p>~a~ <del>b</del> ~~~c~~~</p>\n", html)
31
34
 
32
35
  CommonMarker.render_html(@markdown, :DEFAULT, %i[table strikethrough]).tap do |out|
33
- refute out.include?('| a')
34
- refute out.include?('| <strong>x</strong>')
35
- refute out.include?('~~hi~~')
36
+ refute_includes out, '| a'
37
+ refute_includes out, '| <strong>x</strong>'
38
+ refute_includes out, '~~hi~~'
36
39
  end
37
40
  end
38
41
 
@@ -40,19 +43,19 @@ class TestExtensions < Minitest::Test
40
43
  doc = CommonMarker.render_doc(@markdown, :DEFAULT, %i[table])
41
44
 
42
45
  doc.to_html.tap do |out|
43
- refute out.include?('| a')
44
- %w[<table> <tr> <th> a </th> <td> c </td> <strong>x</strong>].each { |html| assert out.include?(html) }
45
- assert out.include?('~~hi~~')
46
+ refute_includes out, '| a'
47
+ %w[<table> <tr> <th> a </th> <td> c </td> <strong>x</strong>].each { |html| assert_includes out, html }
48
+ assert_includes out, '~~hi~~'
46
49
  end
47
50
 
48
51
  HtmlRenderer.new.render(doc).tap do |out|
49
- refute out.include?('| a')
50
- %w[<table> <tr> <th> a </th> <td> c </td> <strong>x</strong>].each { |html| assert out.include?(html) }
51
- assert out.include?('~~hi~~')
52
+ refute_includes out, '| a'
53
+ %w[<table> <tr> <th> a </th> <td> c </td> <strong>x</strong>].each { |html| assert_includes out, html }
54
+ assert_includes out, '~~hi~~'
52
55
  end
53
56
 
54
57
  doc = CommonMarker.render_doc('~a~ ~~b~~ ~~~c~~~', :STRIKETHROUGH_DOUBLE_TILDE, [:strikethrough])
55
- assert_equal HtmlRenderer.new.render(doc), "<p>~a~ <del>b</del> ~~~c~~~</p>\n"
58
+ assert_equal("<p>~a~ <del>b</del> ~~~c~~~</p>\n", HtmlRenderer.new.render(doc))
56
59
  end
57
60
 
58
61
  def test_bad_extension_specifications
@@ -24,4 +24,25 @@ class TestFootnotes < Minitest::Test
24
24
  def test_html_renderer
25
25
  assert_equal @expected, CommonMarker::HtmlRenderer.new.render(@doc)
26
26
  end
27
+
28
+ def test_render_html
29
+ md = <<~MARKDOWN
30
+ # footnotes
31
+ Let's render some footnotes[^1]
32
+
33
+ [^1]: This is a footnote
34
+ MARKDOWN
35
+ expected = <<~HTML
36
+ <h1>footnotes</h1>
37
+ <p>Let's render some footnotes<sup class="footnote-ref"><a href="#fn1" id="fnref1">1</a></sup></p>
38
+ <section class="footnotes">
39
+ <ol>
40
+ <li id="fn1">
41
+ <p>This is a footnote <a href="#fnref1" class="footnote-backref">↩</a></p>
42
+ </li>
43
+ </ol>
44
+ </section>
45
+ HTML
46
+ assert_equal expected, CommonMarker.render_html(md, :FOOTNOTES)
47
+ end
27
48
  end
data/test/test_helper.rb CHANGED
@@ -44,8 +44,8 @@ def open_spec_file(filename)
44
44
  example_number += 1
45
45
  end_line = line_number
46
46
  tests << {
47
- markdown: markdown_lines.join('').tr('→', "\t"),
48
- html: html_lines.join('').tr('→', "\t").rstrip,
47
+ markdown: markdown_lines.join.tr('→', "\t"),
48
+ html: html_lines.join.tr('→', "\t").rstrip,
49
49
  example: example_number,
50
50
  start_line: start_line,
51
51
  end_line: end_line,
@@ -67,11 +67,6 @@ module CommonMarker
67
67
  CommonMarker.render_html(nil)
68
68
  end
69
69
 
70
- err = assert_raises TypeError do
71
- CommonMarker.render_html("foo \n baz", [:SMART])
72
- end
73
- assert_equal err.message, 'option \':SMART\' does not exist for CommonMarker::Config::Render'
74
-
75
70
  assert_raises TypeError do
76
71
  CommonMarker.render_doc("foo \n baz", 123)
77
72
  end
@@ -79,7 +74,7 @@ module CommonMarker
79
74
  err = assert_raises TypeError do
80
75
  CommonMarker.render_doc("foo \n baz", :safe)
81
76
  end
82
- assert_equal err.message, 'option \':safe\' does not exist for CommonMarker::Config::Parse'
77
+ assert_equal('option \':safe\' does not exist for CommonMarker::Config::OPTS[:parse]', err.message)
83
78
 
84
79
  assert_raises TypeError do
85
80
  CommonMarker.render_doc("foo \n baz", :totes_fake)
@@ -10,7 +10,7 @@ end
10
10
  # list of pairs consisting of input and a regex that must match the output.
11
11
  pathological = {
12
12
  'nested strong emph' =>
13
- [('*a **a ' * 65_000) + 'b' + (' a** a*' * 65_000),
13
+ ["#{'*a **a ' * 65_000}b#{' a** a*' * 65_000}",
14
14
  Regexp.compile('(<em>a <strong>a ){65_000}b( a</strong> a</em>){65_000}')],
15
15
  'many emph closers with no openers' =>
16
16
  [('a_ ' * 65_000),
@@ -34,10 +34,10 @@ pathological = {
34
34
  ['**x [a*b**c*](d)',
35
35
  Regexp.compile('\\*\\*x <a href=\'d\'>a<em>b</em><em>c</em></a>')],
36
36
  'nested brackets' =>
37
- [('[' * 50_000) + 'a' + (']' * 50_000),
37
+ ["#{'[' * 50_000}a#{']' * 50_000}",
38
38
  Regexp.compile('\[{50000}a\]{50000}')],
39
39
  'nested block quotes' =>
40
- [(('> ' * 50_000) + 'a'),
40
+ ["#{'> ' * 50_000}a",
41
41
  Regexp.compile('(<blockquote>\n){50000}')],
42
42
  'U+0000 in input' =>
43
43
  ['abc\u0000de\u0000',
@@ -53,41 +53,41 @@ end
53
53
 
54
54
  if ENV['BENCH']
55
55
  class PathologicalInputsPerformanceTest < Minitest::Benchmark
56
- def bench_pathological_1
56
+ def test_bench_pathological_one
57
57
  assert_performance_linear 0.99 do |n|
58
58
  star = '*' * (n * 10)
59
59
  markdown("#{star}#{star}hi#{star}#{star}")
60
60
  end
61
61
  end
62
62
 
63
- def bench_pathological_2
63
+ def test_bench_pathological_two
64
64
  assert_performance_linear 0.99 do |n|
65
65
  c = '`t`t`t`t`t`t' * (n * 10)
66
66
  markdown(c)
67
67
  end
68
68
  end
69
69
 
70
- def bench_pathological_3
70
+ def test_bench_pathological_three
71
71
  assert_performance_linear 0.99 do |n|
72
72
  markdown(" [a]: #{'A' * n}\n\n#{'[a][]' * n}\n")
73
73
  end
74
74
  end
75
75
 
76
- def bench_pathological_4
76
+ def test_bench_pathological_four
77
77
  assert_performance_linear 0.5 do |n|
78
78
  markdown("#{'[' * n}a#{']' * n}")
79
79
  end
80
80
  end
81
81
 
82
- def bench_pathological_5
82
+ def test_bench_pathological_five
83
83
  assert_performance_linear 0.99 do |n|
84
84
  markdown("#{'**a *a ' * n}#{'a* a**' * n}")
85
85
  end
86
86
  end
87
87
 
88
- def bench_unbound_recursion
88
+ def test_bench_unbound_recursion
89
89
  assert_performance_linear 0.99 do |n|
90
- markdown(('[' * n) + 'foo' + ('](bar)' * n))
90
+ markdown("#{'[' * n}foo#{'](bar)' * n}")
91
91
  end
92
92
  end
93
93
  end
@@ -27,4 +27,21 @@ class TestRenderer < Minitest::Test
27
27
  results = CommonMarker::HtmlRenderer.new.render(doc)
28
28
  assert_equal 2, results.scan(/<tbody>/).size
29
29
  end
30
+
31
+ def test_escape_html_encoding
32
+ my_renderer = Class.new(HtmlRenderer) do
33
+ attr_reader :input_encoding, :output_encoding
34
+
35
+ def text(node)
36
+ @input_encoding = node.string_content.encoding
37
+ escape_html(node.string_content).tap do |escaped|
38
+ @output_encoding = escaped.encoding
39
+ end
40
+ end
41
+ end
42
+
43
+ renderer = my_renderer.new
44
+ assert_equal Encoding::UTF_8, renderer.render(@doc).encoding
45
+ assert_equal renderer.input_encoding, renderer.output_encoding
46
+ end
30
47
  end
@@ -7,11 +7,14 @@ class SmartPunctTest < Minitest::Test
7
7
 
8
8
  smart_punct.each do |testcase|
9
9
  doc = CommonMarker.render_doc(testcase[:markdown], :SMART)
10
+ html = CommonMarker.render_html(testcase[:markdown], :SMART)
10
11
 
11
12
  define_method("test_smart_punct_example_#{testcase[:example]}") do
12
- actual = doc.to_html.strip
13
+ doc_rendered = doc.to_html.strip
14
+ html_rendered = html.strip
13
15
 
14
- assert_equal testcase[:html], actual, testcase[:markdown]
16
+ assert_equal testcase[:html], doc_rendered, testcase[:markdown]
17
+ assert_equal testcase[:html], html_rendered, testcase[:markdown]
15
18
  end
16
19
  end
17
20
 
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.21.1
4
+ version: 0.23.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Garen Torikian
@@ -9,22 +9,8 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-01-21 00:00:00.000000000 Z
12
+ date: 2021-09-03 00:00:00.000000000 Z
13
13
  dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: ruby-enum
16
- requirement: !ruby/object:Gem::Requirement
17
- requirements:
18
- - - "~>"
19
- - !ruby/object:Gem::Version
20
- version: '0.5'
21
- type: :runtime
22
- prerelease: false
23
- version_requirements: !ruby/object:Gem::Requirement
24
- requirements:
25
- - - "~>"
26
- - !ruby/object:Gem::Version
27
- version: '0.5'
28
14
  - !ruby/object:Gem::Dependency
29
15
  name: awesome_print
30
16
  requirement: !ruby/object:Gem::Requirement
@@ -268,6 +254,7 @@ files:
268
254
  - test/test_smartpunct.rb
269
255
  - test/test_spec.rb
270
256
  - test/test_tasklists.rb
257
+ - test/test_xml.rb
271
258
  homepage: https://github.com/gjtorikian/commonmarker
272
259
  licenses:
273
260
  - MIT
@@ -283,7 +270,10 @@ required_ruby_version: !ruby/object:Gem::Requirement
283
270
  requirements:
284
271
  - - ">="
285
272
  - !ruby/object:Gem::Version
286
- version: '0'
273
+ version: '2.6'
274
+ - - "<"
275
+ - !ruby/object:Gem::Version
276
+ version: '4.0'
287
277
  required_rubygems_version: !ruby/object:Gem::Requirement
288
278
  requirements:
289
279
  - - ">="
@@ -317,6 +307,7 @@ test_files:
317
307
  - test/test_helper.rb
318
308
  - test/test_options.rb
319
309
  - test/benchmark.rb
310
+ - test/test_xml.rb
320
311
  - test/test_basics.rb
321
312
  - test/test_renderer.rb
322
313
  - test/test_gc.rb