2 require 'rdoc/markup/lines'
7 # A Fragment is a chunk of text, subclassed as a paragraph, a list
8 # entry, or verbatim text.
11 attr_reader :level, :param, :txt
15 # This is a simple factory system that lets us associate fragement
16 # types (a string) with a subclass of fragment
20 def self.type_name(name)
25 klass = TYPE_MAP[line.type] ||
26 raise("Unknown line type: '#{line.type.inspect}:' '#{line.text}'")
27 return klass.new(line.level, line.param, line.flag, line.text)
30 def initialize(level, param, type, txt)
39 @txt << " " if @txt.length > 0
40 @txt << txt.tr_s("\n ", " ").strip
44 "L#@level: #{self.class.name.split('::')[-1]}\n#@txt"
50 # A paragraph is a fragment which gets wrapped to fit. We remove all
51 # newlines when we're created, and have them put back on output.
53 class Paragraph < Fragment
57 class BlankLine < Paragraph
61 class Heading < Paragraph
70 # A List is a fragment with some kind of label
72 class ListBase < Paragraph
83 class ListItem < ListBase
87 text = if [:NOTE, :LABELED].include? type then
93 "L#@level: #{type} #{self.class.name.split('::')[-1]}\n#{text}"
98 class ListStart < ListBase
99 def initialize(level, param, type)
100 super(level, param, type, nil)
104 class ListEnd < ListBase
105 def initialize(level, type)
106 super(level, "", type, nil)
111 # Verbatim code contains lines that don't get wrapped.
113 class Verbatim < Fragment
117 @txt << txt.chomp << "\n"
125 class Rule < Fragment
130 # Collect groups of lines together. Each group will end up containing a flow
140 @fragments << fragment
148 @fragments.map {|fragment| fragment.to_s}
152 # Factory for different fragment types
154 def fragment_for(*args)
162 change_verbatim_blank_lines
163 add_list_start_and_ends
169 @fragments.join("\n----\n")
172 def accept(am, visitor)
173 visitor.start_accepting
175 @fragments.each do |fragment|
178 visitor.accept_verbatim(am, fragment)
180 visitor.accept_rule(am, fragment)
182 visitor.accept_list_start(am, fragment)
184 visitor.accept_list_end(am, fragment)
186 visitor.accept_list_item(am, fragment)
188 visitor.accept_blank_line(am, fragment)
190 visitor.accept_heading(am, fragment)
192 visitor.accept_paragraph(am, fragment)
196 visitor.end_accepting
203 # normal paragraph text.
209 # You'll end up with the fragments Paragraph, BlankLine, Verbatim,
210 # BlankLine, Verbatim, BlankLine, etc.
212 # The BlankLine in the middle of the verbatim chunk needs to be changed to
213 # a real verbatim newline, and the two verbatim blocks merged
215 def change_verbatim_blank_lines
218 @fragments.each_with_index do |frag, i|
220 frag_block = frag if Verbatim === frag
224 blank_count.times { frag_block.add_text("\n") }
226 frag_block.add_text(frag.txt)
227 @fragments[i] = nil # remove out current fragment
243 # List nesting is implicit given the level of indentation. Make it
244 # explicit, just to make life a tad easier for the output processors
246 def add_list_start_and_ends
251 @fragments.each do |fragment|
252 # $stderr.puts "#{level} : #{fragment.class.name} : #{fragment.level}"
253 new_level = fragment.level
254 while (level < new_level)
257 res << ListStart.new(level, fragment.param, type) if type
259 # $stderr.puts "Start: #{level}"
262 while level > new_level
263 type = type_stack.pop
264 res << ListEnd.new(level, type) if type
266 # $stderr.puts "End: #{level}, #{type}"
270 level = fragment.level
272 level.downto(1) do |i|
273 type = type_stack.pop
274 res << ListEnd.new(i, type) if type
281 # Inserts start/ends between list entries at the same level that have
282 # different element types
290 res.each do |fragment|
293 list_stack.push fragment
295 start = list_stack.pop
296 fragment.type = start.type
299 if fragment.type != l.type
300 @fragments << ListEnd.new(l.level, l.type)
301 start = ListStart.new(l.level, fragment.param, fragment.type)
304 list_stack.push start
309 @fragments << fragment
314 # Tidy up the blank lines:
315 # * change Blank/ListEnd into ListEnd/Blank
316 # * remove blank lines at the front
319 (@fragments.size - 1).times do |i|
320 if BlankLine === @fragments[i] and ListEnd === @fragments[i+1] then
321 @fragments[i], @fragments[i+1] = @fragments[i+1], @fragments[i]
325 # remove leading blanks
326 @fragments.each_with_index do |f, i|
327 break unless f.kind_of? BlankLine