From f28ef91f1199f483492438533bc23b8960047445 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 21 Nov 2014 18:02:27 -0800 Subject: commonmark.rb - support remaining elements in HTML renderer. Halt on finding unsupported method. --- commonmark.rb | 147 +++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 135 insertions(+), 12 deletions(-) mode change 100644 => 100755 commonmark.rb diff --git a/commonmark.rb b/commonmark.rb old mode 100644 new mode 100755 index 32a4a72..8767c04 --- a/commonmark.rb +++ b/commonmark.rb @@ -1,7 +1,9 @@ +#!/usr/bin/env ruby require 'ffi' require 'stringio' require 'cgi' require 'set' +require 'uri' module CMark extend FFI::Library @@ -12,6 +14,7 @@ module CMark :atx_header, :setext_header, :hrule, :reference_def, :str, :softbreak, :linebreak, :code, :inline_html, :emph, :strong, :link, :image] + enum :list_type, [:no_list, :bullet_list, :ordered_list] attach_function :cmark_free_nodes, [:node], :void attach_function :cmark_node_unlink, [:node], :void @@ -24,11 +27,17 @@ module CMark attach_function :cmark_node_previous, [:node], :node attach_function :cmark_node_get_type, [:node], :node_type attach_function :cmark_node_get_string_content, [:node], :string + attach_function :cmark_node_get_url, [:node], :string + attach_function :cmark_node_get_title, [:node], :string attach_function :cmark_node_get_header_level, [:node], :int + attach_function :cmark_node_get_list_type, [:node], :list_type + attach_function :cmark_node_get_list_start, [:node], :int + attach_function :cmark_node_get_list_tight, [:node], :bool end class Node - attr_accessor :type, :children, :string_content, :header_level + attr_accessor :type, :children, :string_content, :header_level, + :list_type, :list_start, :list_tight, :url, :title def initialize(pointer) if pointer.null? return nil @@ -43,7 +52,20 @@ class Node b = CMark::cmark_node_next(b) end @string_content = CMark::cmark_node_get_string_content(pointer) - @header_level = CMark::cmark_node_get_header_level(pointer) + if @type == :atx_header || @type == :setext_header + @header_level = CMark::cmark_node_get_header_level(pointer) + end + if @type == :list + @list_type = CMark::cmark_node_get_list_type(pointer) + @list_start = CMark::cmark_node_get_list_start(pointer) + @list_tight = CMark::cmark_node_get_list_tight(pointer) + end + if @type == :link || @type == :image + @url = CMark::cmark_node_get_url(pointer) + if !@url then @url = "" end + @title = CMark::cmark_node_get_title(pointer) + if !@title then @title = "" end + end if @type == :document self.free end @@ -64,7 +86,7 @@ class Node end class Renderer - attr_reader :warnings + attr_accessor :in_tight, :warnings, :in_plain def initialize(stream = nil) if stream @stream = stream @@ -75,6 +97,8 @@ class Renderer end @need_blocksep = false @warnings = Set.new [] + @in_tight = false + @in_plain = false end def outf(format, *args) @@ -103,12 +127,17 @@ class Renderer if @stringwriter return @stream.string end + elsif self.in_plain && node.type != :str && node.type != :softbreak + # pass through looking for str, softbreak + node.children.each do |child| + render(child) + end else begin self.send(node.type, node) - rescue NoMethodError + rescue NoMethodError => e @warnings.add("WARNING: " + node.type.to_s + " not implemented.") - self.out(node.children) + raise e end end end @@ -140,43 +169,114 @@ class Renderer self.out("\n\n") end - def asblock(&blk) + def containersep + if !self.in_tight + self.out("\n") + end + end + + def block(&blk) if @need_blocksep self.blocksep end blk.call @need_blocksep = true end + + def container(starter, ender, &blk) + self.out(starter) + self.containersep + @need_blocksep = false + blk.call + self.containersep + self.out(ender) + end + + def plain(&blk) + old_in_plain = @in_plain + @in_plain = true + blk.call + @in_plain = old_in_plain + end end class HtmlRenderer < Renderer def header(node) - asblock do + block do self.out("", node.children, "") end end def paragraph(node) - asblock do - self.out("

", node.children, "

") + block do + if self.in_tight + self.out(node.children) + else + self.out("

", node.children, "

") + end + end + end + + def list(node) + old_in_tight = self.in_tight + self.in_tight = node.list_tight + block do + if node.list_type == :bullet_list + container("") do + self.out(node.children) + end + else + start = node.list_start == 1 ? '' : + (' start="' + node.list_start.to_s + '"') + container(start, "") do + self.out(node.children) + end + end + end + self.in_tight = old_in_tight + end + + def list_item(node) + block do + container("
  • ", "
  • ") do + self.out(node.children) + end + end + end + + def blockquote(node) + block do + container("
    ", "
    ") do + self.out(node.children) + end end end def hrule(node) - asblock do + block do self.out("
    ") end end def code_block(node) - asblock do + block do self.out("
    ")
           self.out(CGI.escapeHTML(node.string_content))
           self.out("
    ") end end + def html(node) + block do + self.out(node.string_content) + end + end + + def inline_html(node) + self.out(node.string_content) + end + def emph(node) self.out("", node.children, "") end @@ -185,6 +285,24 @@ class HtmlRenderer < Renderer self.out("", node.children, "") end + def link(node) + self.out('', node.children, '') + end + + def image(node) + self.out('', node.children, '') + end + end + def str(node) self.out(CGI.escapeHTML(node.string_content)) end @@ -195,12 +313,17 @@ class HtmlRenderer < Renderer self.out("") end + def linebreak(node) + self.out("
    ") + self.softbreak(node) + end + def softbreak(node) self.out("\n") end end -doc = Node.parse_file(STDIN) +doc = Node.parse_file(ARGF) renderer = HtmlRenderer.new(STDOUT) renderer.render(doc) renderer.warnings.each do |w| -- cgit v1.2.3