Skip to content

Commit

Permalink
Add the methods for rendering XML from cmark-gfm
Browse files Browse the repository at this point in the history
  • Loading branch information
digitalmoksha committed Aug 26, 2021
1 parent 4ef8927 commit 640c40d
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 8 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
/.bundle
/.config
/coverage/
/.idea
/InstalledFiles
/pkg/
/spec/reports/
Expand Down
27 changes: 20 additions & 7 deletions bin/commonmarker
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@ def parse_options
extensions = CommonMarker.extensions
parse_options = CommonMarker::Config::OPTS.fetch(:parse)
render_options = CommonMarker::Config::OPTS.fetch(:render)
format_options = CommonMarker::Config::OPTS.fetch(:format)

options.active_extensions = []
options.active_parse_options = [:DEFAULT]
options.active_render_options = [:DEFAULT]
options.output_format = :html

option_parser = OptionParser.new do |opts|
opts.banner = 'Usage: commonmarker [--html-renderer] [--extension=EXTENSION]'
opts.separator ' [--to=FORMAT]'
opts.separator ' [--parse-option=OPTION]'
opts.separator ' [--render-option=OPTION]'
opts.separator ' [FILE..]'
Expand All @@ -43,6 +46,7 @@ def parse_options
opts.on('-h', '--help', 'Prints this help') do
puts opts
puts
puts "Available formats: #{format_options.join(', ')}"
puts "Available extentions: #{extensions.join(', ')}"
puts "Available parse options: #{parse_options.keys.join(', ')}"
puts "Available render options: #{render_options.keys.join(', ')}"
Expand All @@ -51,7 +55,12 @@ def parse_options
exit
end

opts.on('--html-renderer', 'Use the HtmlRenderer renderer rather than the native C renderer') do
opts.on('-tFORMAT', '--to=FORMAT', String, 'Specify output format (html, xml)') do |value|
value = value.to_sym
options.output_format = value if format_options.include?(value)
end

opts.on('--html-renderer', 'Use the HtmlRenderer renderer rather than the native C renderer (only valid when format is html)') do
options.renderer = true
end

Expand Down Expand Up @@ -87,12 +96,16 @@ def parse_options
end

options = parse_options

doc = CommonMarker.render_doc(ARGF.read, options.active_parse_options, options.active_extensions)

if options.renderer
renderer = CommonMarker::HtmlRenderer.new(extensions: options.active_extensions)
$stdout.write(renderer.render(doc))
else
$stdout.write(doc.to_html(options.active_render_options, options.active_extensions))
case options.output_format
when :html
if options.renderer
renderer = CommonMarker::HtmlRenderer.new(options: options.active_render_options, extensions: options.active_extensions)
$stdout.write(renderer.render(doc))
else
$stdout.write(doc.to_html(options.active_render_options, options.active_extensions))
end
when :xml
$stdout.write(doc.to_xml(options.active_render_options))
end
59 changes: 59 additions & 0 deletions ext/commonmarker/commonmarker.c
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,40 @@ static VALUE rb_markdown_to_html(VALUE self, VALUE rb_text, VALUE rb_options, VA
return ruby_html;
}

/*
* Internal: Parses a Markdown string into an HTML string.
*
*/
static VALUE rb_markdown_to_xml(VALUE self, VALUE rb_text, VALUE rb_options, VALUE rb_extensions) {
char *str, *xml;
int len;
cmark_parser *parser;
cmark_node *doc;
Check_Type(rb_text, T_STRING);
Check_Type(rb_options, T_FIXNUM);

parser = prepare_parser(rb_options, rb_extensions, cmark_get_arena_mem_allocator());

str = (char *)RSTRING_PTR(rb_text);
len = RSTRING_LEN(rb_text);

cmark_parser_feed(parser, str, len);
doc = cmark_parser_finish(parser);
if (doc == NULL) {
cmark_arena_reset();
rb_raise(rb_eNodeError, "error parsing document");
}

cmark_mem *default_mem = cmark_get_default_mem_allocator();
xml = cmark_render_xml_with_mem(doc, FIX2INT(rb_options), default_mem);
cmark_arena_reset();

VALUE ruby_xml = rb_str_new2(xml);
default_mem->free(xml);

return ruby_xml;
}

/*
* Internal: Creates a node based on a node type.
*
Expand Down Expand Up @@ -574,6 +608,28 @@ static VALUE rb_render_html(VALUE self, VALUE rb_options, VALUE rb_extensions) {
return ruby_html;
}

/* Internal: Convert the node to an XML string.
*
* Returns a {String}.
*/
static VALUE rb_render_xml(VALUE self, VALUE rb_options) {
int options;
int i;
cmark_node *node;
Check_Type(rb_options, T_FIXNUM);

options = FIX2INT(rb_options);

Data_Get_Struct(self, cmark_node, node);

char *xml = cmark_render_xml(node, options);
VALUE ruby_xml = rb_str_new2(xml);

free(xml);

return ruby_xml;
}

/* Internal: Convert the node to a CommonMark string.
*
* Returns a {String}.
Expand Down Expand Up @@ -1216,6 +1272,8 @@ __attribute__((visibility("default"))) void Init_commonmarker() {
rb_cNode = rb_define_class_under(module, "Node", rb_cObject);
rb_define_singleton_method(rb_cNode, "markdown_to_html", rb_markdown_to_html,
3);
rb_define_singleton_method(rb_cNode, "markdown_to_xml", rb_markdown_to_xml,
3);
rb_define_singleton_method(rb_cNode, "new", rb_node_new, 1);
rb_define_singleton_method(rb_cNode, "parse_document", rb_parse_document, 4);
rb_define_method(rb_cNode, "string_content", rb_node_get_string_content, 0);
Expand All @@ -1228,6 +1286,7 @@ __attribute__((visibility("default"))) void Init_commonmarker() {
rb_define_method(rb_cNode, "next", rb_node_next, 0);
rb_define_method(rb_cNode, "insert_before", rb_node_insert_before, 1);
rb_define_method(rb_cNode, "_render_html", rb_render_html, 2);
rb_define_method(rb_cNode, "_render_xml", rb_render_xml, 1);
rb_define_method(rb_cNode, "_render_commonmark", rb_render_commonmark, -1);
rb_define_method(rb_cNode, "_render_plaintext", rb_render_plaintext, -1);
rb_define_method(rb_cNode, "insert_after", rb_node_insert_after, 1);
Expand Down
3 changes: 2 additions & 1 deletion lib/commonmarker/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ module Config
FULL_INFO_STRING: (1 << 16),
UNSAFE: (1 << 17),
FOOTNOTES: (1 << 13)
}.freeze
}.freeze,
format: %i[html xml].freeze
}.freeze

def self.process_options(option, type)
Expand Down
10 changes: 10 additions & 0 deletions lib/commonmarker/node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@ def to_html(options = :DEFAULT, extensions = [])
_render_html(opts, extensions).force_encoding('utf-8')
end

# Public: Convert the node to an XML string.
#
# options - A {Symbol} or {Array of Symbol}s indicating the render options
#
# Returns a {String}.
def to_xml(options = :DEFAULT)
opts = Config.process_options(options, :render)
_render_xml(opts).force_encoding('utf-8')
end

# Public: Convert the node to a CommonMark string.
#
# options - A {Symbol} or {Array of Symbol}s indicating the render options
Expand Down
6 changes: 6 additions & 0 deletions test/test_commands.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,10 @@ def test_understands_multiple_extensions
assert_includes out, '<p><del>hi</del>'
%w[<table> <tr> <th> a </th> <td> c </td>].each { |html| assert_includes out, html }
end

def test_understands_format
out = make_bin('strong.md', '--to=xml')
assert_includes out, '<?xml version="1.0" encoding="UTF-8"?>'
assert_includes out, '<text xml:space="preserve">strong</text>'
end
end
93 changes: 93 additions & 0 deletions test/test_xml.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# frozen_string_literal: true

require 'test_helper'

class TestXml < Minitest::Test
def setup
@markdown = <<~MD
Hi *there*!
1. I am a numeric list.
2. I continue the list.
* Suddenly, an unordered list!
* What fun!
Okay, _enough_.
| a | b |
| --- | --- |
| c | d |
MD
end

def render_doc(doc)
CommonMarker.render_doc(doc, :DEFAULT, [:table])
end

def test_to_commonmark
compare = render_doc(@markdown).to_xml(:SOURCEPOS)

assert_equal <<~XML, compare
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE document SYSTEM "CommonMark.dtd">
<document sourcepos="1:1-12:13" xmlns="http://commonmark.org/xml/1.0">
<paragraph sourcepos="1:1-1:11">
<text sourcepos="1:1-1:3" xml:space="preserve">Hi </text>
<emph sourcepos="1:4-1:10">
<text sourcepos="1:5-1:9" xml:space="preserve">there</text>
</emph>
<text sourcepos="1:11-1:11" xml:space="preserve">!</text>
</paragraph>
<list sourcepos="3:1-4:23" type="ordered" start="1" delim="period" tight="true">
<item sourcepos="3:1-3:23">
<paragraph sourcepos="3:4-3:23">
<text sourcepos="3:4-3:23" xml:space="preserve">I am a numeric list.</text>
</paragraph>
</item>
<item sourcepos="4:1-4:23">
<paragraph sourcepos="4:4-4:23">
<text sourcepos="4:4-4:23" xml:space="preserve">I continue the list.</text>
</paragraph>
</item>
</list>
<list sourcepos="5:1-7:0" type="bullet" tight="true">
<item sourcepos="5:1-5:30">
<paragraph sourcepos="5:3-5:30">
<text sourcepos="5:3-5:30" xml:space="preserve">Suddenly, an unordered list!</text>
</paragraph>
</item>
<item sourcepos="6:1-7:0">
<paragraph sourcepos="6:3-6:11">
<text sourcepos="6:3-6:11" xml:space="preserve">What fun!</text>
</paragraph>
</item>
</list>
<paragraph sourcepos="8:1-8:15">
<text sourcepos="8:1-8:6" xml:space="preserve">Okay, </text>
<emph sourcepos="8:7-8:14">
<text sourcepos="8:8-8:13" xml:space="preserve">enough</text>
</emph>
<text sourcepos="8:15-8:15" xml:space="preserve">.</text>
</paragraph>
<table sourcepos="10:1-12:13">
<table_header sourcepos="10:1-10:13">
<table_cell sourcepos="10:2-10:6">
<text sourcepos="10:3-10:3" xml:space="preserve">a</text>
</table_cell>
<table_cell sourcepos="10:8-10:12">
<text sourcepos="10:9-10:9" xml:space="preserve">b</text>
</table_cell>
</table_header>
<table_row sourcepos="12:1-12:13">
<table_cell sourcepos="12:2-12:6">
<text sourcepos="12:3-12:3" xml:space="preserve">c</text>
</table_cell>
<table_cell sourcepos="12:8-12:12">
<text sourcepos="12:9-12:9" xml:space="preserve">d</text>
</table_cell>
</table_row>
</table>
</document>
XML
end
end

0 comments on commit 640c40d

Please sign in to comment.