cmark

My personal build of CMark ✏️

Commit
f4947a4a82b212f524c42a437a4cde6c7655144b
Parent
05425ef2476f51131364d7218bfb9facb6e0687b
Author
John MacFarlane <jgm@berkeley.edu>
Date

commonmark.rb: separated Renderer class.

Diffstat

1 file changed, 85 insertions, 45 deletions

Status File Name N° Changes Insertions Deletions
Modified commonmark.rb 130 85 45
diff --git a/commonmark.rb b/commonmark.rb
@@ -1,5 +1,6 @@
 require 'ffi'
 require 'stringio'
+require 'cgi'
 
 module CMark
   extend FFI::Library
@@ -22,63 +23,102 @@ 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
+end
 
-  class Node
-    attr_accessor :type, :children, :string_content, :pointer
-    def initialize(pointer)
-      if pointer.null?
-        return nil
-      end
-      @pointer = pointer
-      @type = CMark::cmark_node_get_type(pointer)
-      @children = []
-      first_child = CMark::cmark_node_first_child(pointer)
-      b = first_child
-      while !b.null?
-        @children << Node.new(b)
-        b = CMark::cmark_node_next(b)
-      end
-      @string_content = CMark::cmark_node_get_string_content(pointer)
-      if @type == :document
-        self.free
-      end
+class Node
+  attr_accessor :type, :children, :string_content, :pointer
+  def initialize(pointer)
+    if pointer.null?
+      return nil
     end
-
-    def to_html_stream(file)
-      file.printf("[%s]\n", self.type.to_s)
-      case self.type
-      when :document
-        self.children.each do |child|
-          child.to_html_stream(file)
-        end
-      when :paragraph
-        self.children.each do |child|
-          child.to_html_stream(file)
-        end
-      when :str
-        file.puts(self.string_content)
-      else
-      end
+    @pointer = pointer
+    @type = CMark::cmark_node_get_type(pointer)
+    @children = []
+    first_child = CMark::cmark_node_first_child(pointer)
+    b = first_child
+    while !b.null?
+      @children << Node.new(b)
+      b = CMark::cmark_node_next(b)
     end
-
-    def to_html
-      self.to_html_stream(StringIO.new)
+    @string_content = CMark::cmark_node_get_string_content(pointer)
+    if @type == :document
+      self.free
     end
+  end
 
-    def self.from_markdown(s)
-      len = s.bytes.length
-      Node.new(CMark::cmark_parse_document(s, len))
+  def self.from_markdown(s)
+    len = s.bytes.length
+    Node.new(CMark::cmark_parse_document(s, len))
+  end
+
+  def free
+      CMark::cmark_free_nodes(@pointer)
+  end
+end
+
+class Renderer
+  def initialize(stream = nil)
+    if stream
+      @stream = stream
+      @stringwriter = false
+    else
+      @stringwriter = true
+      @stream = StringIO.new
     end
+  end
 
-    def free
-        CMark::cmark_free_nodes(@pointer)
+  def outf(format, *args)
+    @stream.printf(format, *args)
+  end
+
+  def out(arg)
+    @stream.puts(arg)
+  end
+
+  def render(node)
+    case node.type
+    when :document
+      self.document(node.children)
+      if @stringwriter
+        @stream.string
+      end
+    when :paragraph
+      self.paragraph(node.children)
+    when :str
+      self.str(node.string_content)
+    else
+      # raise "unimplemented " + node.type.to_s
     end
   end
 
+  def document(children)
+    children.each { |x| render(x) }
+  end
+
+  def paragraph(children)
+    children.each { |x| render(x) }
+  end
+
+  def str(content)
+    self.out(content)
+  end
+end
+
+class HtmlRenderer < Renderer
+  def paragraph(children)
+    self.out("<p>")
+    children.each { |x| render(x) }
+    self.out("</p>\n")
+  end
+
+  def str(content)
+    self.out(CGI.escapeHTML(content))
+  end
 end
 
-doc = CMark::Node.from_markdown(STDIN.read())
-doc.to_html_stream(STDOUT)
+doc = Node.from_markdown(STDIN.read())
+renderer = HtmlRenderer.new(STDOUT)
+renderer.render(doc)
 
 # def markdown_to_html(s)
 #   len = s.bytes.length