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