require 'rdoc/markup/simple_markup/fragments' require 'rdoc/markup/simple_markup/inline' module SM module Flow P = Struct.new(:body) VERB = Struct.new(:body) RULE = Struct.new(:width) class LIST attr_reader :type, :contents def initialize(type) @type = type @contents = [] end def <<(stuff) @contents << stuff end end LI = Struct.new(:label, :body) H = Struct.new(:level, :text) end class ToFlow LIST_TYPE_TO_HTML = { ListBase::BULLET => [ "" ], ListBase::NUMBER => [ "
    ", "
" ], ListBase::UPPERALPHA => [ "
    ", "
" ], ListBase::LOWERALPHA => [ "
    ", "
" ], ListBase::LABELED => [ "
", "
" ], ListBase::NOTE => [ "", "
" ], } InlineTag = Struct.new(:bit, :on, :off) def initialize init_tags end ## # Set up the standard mapping of attributes to HTML tags # def init_tags @attr_tags = [ InlineTag.new(SM::Attribute.bitmap_for(:BOLD), "", ""), InlineTag.new(SM::Attribute.bitmap_for(:TT), "", ""), InlineTag.new(SM::Attribute.bitmap_for(:EM), "", ""), ] end ## # Add a new set of HTML tags for an attribute. We allow # separate start and end tags for flexibility # def add_tag(name, start, stop) @attr_tags << InlineTag.new(SM::Attribute.bitmap_for(name), start, stop) end ## # Given an HTML tag, decorate it with class information # and the like if required. This is a no-op in the base # class, but is overridden in HTML output classes that # implement style sheets def annotate(tag) tag end ## # Here's the client side of the visitor pattern def start_accepting @res = [] @list_stack = [] end def end_accepting @res end def accept_paragraph(am, fragment) @res << Flow::P.new((convert_flow(am.flow(fragment.txt)))) end def accept_verbatim(am, fragment) @res << Flow::VERB.new((convert_flow(am.flow(fragment.txt)))) end def accept_rule(am, fragment) @res << Rule.new(size) end def accept_list_start(am, fragment) @list_stack.push(@res) list = Flow::LIST.new(fragment.type) @res << list @res = list end def accept_list_end(am, fragment) @res = @list_stack.pop end def accept_list_item(am, fragment) @res << Flow::LI.new(fragment.param, convert_flow(am.flow(fragment.txt))) end def accept_blank_line(am, fragment) # @res << annotate("

") << "\n" end def accept_heading(am, fragment) @res << Flow::H.new(fragment.head_level, am.flow(fragment.txt)) end ####################################################################### private ####################################################################### def on_tags(res, item) attr_mask = item.turn_on return if attr_mask.zero? @attr_tags.each do |tag| if attr_mask & tag.bit != 0 res << annotate(tag.on) end end end def off_tags(res, item) attr_mask = item.turn_off return if attr_mask.zero? @attr_tags.reverse_each do |tag| if attr_mask & tag.bit != 0 res << annotate(tag.off) end end end def convert_flow(flow) res = "" flow.each do |item| case item when String res << convert_string(item) when AttrChanger off_tags(res, item) on_tags(res, item) when Special res << convert_special(item) else raise "Unknown flow element: #{item.inspect}" end end res end # some of these patterns are taken from SmartyPants... def convert_string(item) CGI.escapeHTML(item) end def convert_special(special) handled = false Attribute.each_name_of(special.type) do |name| method_name = "handle_special_#{name}" if self.respond_to? method_name special.text = send(method_name, special) handled = true end end raise "Unhandled special: #{special}" unless handled special.text end end end