Imported File#ftype spec from rubyspecs.
[rbx.git] / lib / rdoc / markup / to_flow.rb
blob3d87b3e9c3b79b4229d2847bfcbfe86b0767e62c
1 require 'rdoc/markup/formatter'
2 require 'rdoc/markup/fragments'
3 require 'rdoc/markup/inline'
4 require 'cgi'
6 class RDoc::Markup
8   module Flow
9     P = Struct.new(:body)
10     VERB = Struct.new(:body)
11     RULE = Struct.new(:width)
12     class LIST
13       attr_reader :type, :contents
14       def initialize(type)
15         @type = type
16         @contents = []
17       end
18       def <<(stuff)
19         @contents << stuff
20       end
21     end
22     LI = Struct.new(:label, :body)
23     H = Struct.new(:level, :text)
24   end
26   class ToFlow < RDoc::Markup::Formatter
27     LIST_TYPE_TO_HTML = {
28       :BULLET     =>  [ "<ul>", "</ul>" ],
29       :NUMBER     =>  [ "<ol>", "</ol>" ],
30       :UPPERALPHA =>  [ "<ol>", "</ol>" ],
31       :LOWERALPHA =>  [ "<ol>", "</ol>" ],
32       :LABELED    =>  [ "<dl>", "</dl>" ],
33       :NOTE       =>  [ "<table>", "</table>" ],
34     }
36     InlineTag = Struct.new(:bit, :on, :off)
38     def initialize
39       super
41       init_tags
42     end
44     ##
45     # Set up the standard mapping of attributes to HTML tags
47     def init_tags
48       @attr_tags = [
49         InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:BOLD), "<b>", "</b>"),
50         InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:TT),   "<tt>", "</tt>"),
51         InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:EM),   "<em>", "</em>"),
52       ]
53     end
55     ##
56     # Add a new set of HTML tags for an attribute. We allow separate start and
57     # end tags for flexibility
59     def add_tag(name, start, stop)
60       @attr_tags << InlineTag.new(RDoc::Markup::Attribute.bitmap_for(name), start, stop)
61     end
63     ##
64     # Given an HTML tag, decorate it with class information and the like if
65     # required. This is a no-op in the base class, but is overridden in HTML
66     # output classes that implement style sheets
68     def annotate(tag)
69       tag
70     end
72     ##
73     # Here's the client side of the visitor pattern
75     def start_accepting
76       @res = []
77       @list_stack = []
78     end
80     def end_accepting
81       @res
82     end
84     def accept_paragraph(am, fragment)
85       @res << Flow::P.new((convert_flow(am.flow(fragment.txt))))
86     end
88     def accept_verbatim(am, fragment)
89       @res << Flow::VERB.new((convert_flow(am.flow(fragment.txt))))
90     end
92     def accept_rule(am, fragment)
93       size = fragment.param
94       size = 10 if size > 10
95       @res << Flow::RULE.new(size)
96     end
98     def accept_list_start(am, fragment)
99       @list_stack.push(@res)
100       list = Flow::LIST.new(fragment.type)
101       @res << list
102       @res = list
103     end
105     def accept_list_end(am, fragment)
106       @res = @list_stack.pop
107     end
109     def accept_list_item(am, fragment)
110       @res << Flow::LI.new(fragment.param, convert_flow(am.flow(fragment.txt)))
111     end
113     def accept_blank_line(am, fragment)
114       # @res << annotate("<p />") << "\n"
115     end
117     def accept_heading(am, fragment)
118       @res << Flow::H.new(fragment.head_level, convert_flow(am.flow(fragment.txt)))
119     end
121     private
123     def on_tags(res, item)
124       attr_mask = item.turn_on
125       return if attr_mask.zero?
127       @attr_tags.each do |tag|
128         if attr_mask & tag.bit != 0
129           res << annotate(tag.on)
130         end
131       end
132     end
134     def off_tags(res, item)
135       attr_mask = item.turn_off
136       return if attr_mask.zero?
138       @attr_tags.reverse_each do |tag|
139         if attr_mask & tag.bit != 0
140           res << annotate(tag.off)
141         end
142       end
143     end
145     def convert_flow(flow)
146       res = ""
147       flow.each do |item|
148         case item
149         when String
150           res << convert_string(item)
151         when AttrChanger
152           off_tags(res, item)
153           on_tags(res,  item)
154         when Special
155           res << convert_special(item)
156         else
157           raise "Unknown flow element: #{item.inspect}"
158         end
159       end
160       res
161     end
163     def convert_string(item)
164       CGI.escapeHTML(item)
165     end
167     def convert_special(special)
168       handled = false
169       Attribute.each_name_of(special.type) do |name|
170         method_name = "handle_special_#{name}"
171         if self.respond_to? method_name
172           special.text = send(method_name, special)
173           handled = true
174         end
175       end
177       raise "Unhandled special: #{special}" unless handled
179       special.text
180     end
182   end