source: extensions/gsdl-video/trunk/installed/cmdline/lib/ruby/1.8/rdoc/markup/simple_markup/fragments.rb@ 18425

Last change on this file since 18425 was 18425, checked in by davidb, 15 years ago

Video extension to Greenstone

File size: 7.2 KB
Line 
1require 'rdoc/markup/simple_markup/lines.rb'
2#require 'rdoc/markup/simple_markup/to_flow.rb'
3
4module SM
5
6 ##
7 # A Fragment is a chunk of text, subclassed as a paragraph, a list
8 # entry, or verbatim text
9
10 class Fragment
11 attr_reader :level, :param, :txt
12 attr_accessor :type
13
14 def initialize(level, param, type, txt)
15 @level = level
16 @param = param
17 @type = type
18 @txt = ""
19 add_text(txt) if txt
20 end
21
22 def add_text(txt)
23 @txt << " " if @txt.length > 0
24 @txt << txt.tr_s("\n ", " ").strip
25 end
26
27 def to_s
28 "L#@level: #{self.class.name.split('::')[-1]}\n#@txt"
29 end
30
31 ######
32 # This is a simple factory system that lets us associate fragement
33 # types (a string) with a subclass of fragment
34
35 TYPE_MAP = {}
36
37 def Fragment.type_name(name)
38 TYPE_MAP[name] = self
39 end
40
41 def Fragment.for(line)
42 klass = TYPE_MAP[line.type] ||
43 raise("Unknown line type: '#{line.type.inspect}:' '#{line.text}'")
44 return klass.new(line.level, line.param, line.flag, line.text)
45 end
46 end
47
48 ##
49 # A paragraph is a fragment which gets wrapped to fit. We remove all
50 # newlines when we're created, and have them put back on output
51
52 class Paragraph < Fragment
53 type_name Line::PARAGRAPH
54 end
55
56 class BlankLine < Paragraph
57 type_name Line::BLANK
58 end
59
60 class Heading < Paragraph
61 type_name Line::HEADING
62
63 def head_level
64 @param.to_i
65 end
66 end
67
68 ##
69 # A List is a fragment with some kind of label
70 #
71
72 class ListBase < Paragraph
73 # List types
74 BULLET = :BULLET
75 NUMBER = :NUMBER
76 UPPERALPHA = :UPPERALPHA
77 LOWERALPHA = :LOWERALPHA
78 LABELED = :LABELED
79 NOTE = :NOTE
80 end
81
82 class ListItem < ListBase
83 type_name Line::LIST
84
85 # def label
86 # am = AttributeManager.new(@param)
87 # am.flow
88 # end
89 end
90
91 class ListStart < ListBase
92 def initialize(level, param, type)
93 super(level, param, type, nil)
94 end
95 end
96
97 class ListEnd < ListBase
98 def initialize(level, type)
99 super(level, "", type, nil)
100 end
101 end
102
103 ##
104 # Verbatim code contains lines that don't get wrapped.
105
106 class Verbatim < Fragment
107 type_name Line::VERBATIM
108
109 def add_text(txt)
110 @txt << txt.chomp << "\n"
111 end
112
113 end
114
115 ##
116 # A horizontal rule
117 class Rule < Fragment
118 type_name Line::RULE
119 end
120
121
122 # Collect groups of lines together. Each group
123 # will end up containing a flow of text
124
125 class LineCollection
126
127 def initialize
128 @fragments = []
129 end
130
131 def add(fragment)
132 @fragments << fragment
133 end
134
135 def each(&b)
136 @fragments.each(&b)
137 end
138
139 # For testing
140 def to_a
141 @fragments.map {|fragment| fragment.to_s}
142 end
143
144 # Factory for different fragment types
145 def fragment_for(*args)
146 Fragment.for(*args)
147 end
148
149 # tidy up at the end
150 def normalize
151 change_verbatim_blank_lines
152 add_list_start_and_ends
153 add_list_breaks
154 tidy_blank_lines
155 end
156
157 def to_s
158 @fragments.join("\n----\n")
159 end
160
161 def accept(am, visitor)
162
163 visitor.start_accepting
164
165 @fragments.each do |fragment|
166 case fragment
167 when Verbatim
168 visitor.accept_verbatim(am, fragment)
169 when Rule
170 visitor.accept_rule(am, fragment)
171 when ListStart
172 visitor.accept_list_start(am, fragment)
173 when ListEnd
174 visitor.accept_list_end(am, fragment)
175 when ListItem
176 visitor.accept_list_item(am, fragment)
177 when BlankLine
178 visitor.accept_blank_line(am, fragment)
179 when Heading
180 visitor.accept_heading(am, fragment)
181 when Paragraph
182 visitor.accept_paragraph(am, fragment)
183 end
184 end
185
186 visitor.end_accepting
187 end
188 #######
189 private
190 #######
191
192 # If you have:
193 #
194 # normal paragraph text.
195 #
196 # this is code
197 #
198 # and more code
199 #
200 # You'll end up with the fragments Paragraph, BlankLine,
201 # Verbatim, BlankLine, Verbatim, BlankLine, etc
202 #
203 # The BlankLine in the middle of the verbatim chunk needs to
204 # be changed to a real verbatim newline, and the two
205 # verbatim blocks merged
206 #
207 #
208 def change_verbatim_blank_lines
209 frag_block = nil
210 blank_count = 0
211 @fragments.each_with_index do |frag, i|
212 if frag_block.nil?
213 frag_block = frag if Verbatim === frag
214 else
215 case frag
216 when Verbatim
217 blank_count.times { frag_block.add_text("\n") }
218 blank_count = 0
219 frag_block.add_text(frag.txt)
220 @fragments[i] = nil # remove out current fragment
221 when BlankLine
222 if frag_block
223 blank_count += 1
224 @fragments[i] = nil
225 end
226 else
227 frag_block = nil
228 blank_count = 0
229 end
230 end
231 end
232 @fragments.compact!
233 end
234
235 # List nesting is implicit given the level of
236 # Make it explicit, just to make life a tad
237 # easier for the output processors
238
239 def add_list_start_and_ends
240 level = 0
241 res = []
242 type_stack = []
243
244 @fragments.each do |fragment|
245 # $stderr.puts "#{level} : #{fragment.class.name} : #{fragment.level}"
246 new_level = fragment.level
247 while (level < new_level)
248 level += 1
249 type = fragment.type
250 res << ListStart.new(level, fragment.param, type) if type
251 type_stack.push type
252 # $stderr.puts "Start: #{level}"
253 end
254
255 while level > new_level
256 type = type_stack.pop
257 res << ListEnd.new(level, type) if type
258 level -= 1
259 # $stderr.puts "End: #{level}, #{type}"
260 end
261
262 res << fragment
263 level = fragment.level
264 end
265 level.downto(1) do |i|
266 type = type_stack.pop
267 res << ListEnd.new(i, type) if type
268 end
269
270 @fragments = res
271 end
272
273 # now insert start/ends between list entries at the
274 # same level that have different element types
275
276 def add_list_breaks
277 res = @fragments
278
279 @fragments = []
280 list_stack = []
281
282 res.each do |fragment|
283 case fragment
284 when ListStart
285 list_stack.push fragment
286 when ListEnd
287 start = list_stack.pop
288 fragment.type = start.type
289 when ListItem
290 l = list_stack.last
291 if fragment.type != l.type
292 @fragments << ListEnd.new(l.level, l.type)
293 start = ListStart.new(l.level, fragment.param, fragment.type)
294 @fragments << start
295 list_stack.pop
296 list_stack.push start
297 end
298 else
299 ;
300 end
301 @fragments << fragment
302 end
303 end
304
305 # Finally tidy up the blank lines:
306 # * change Blank/ListEnd into ListEnd/Blank
307 # * remove blank lines at the front
308
309 def tidy_blank_lines
310 (@fragments.size - 1).times do |i|
311 if @fragments[i].kind_of?(BlankLine) and
312 @fragments[i+1].kind_of?(ListEnd)
313 @fragments[i], @fragments[i+1] = @fragments[i+1], @fragments[i]
314 end
315 end
316
317 # remove leading blanks
318 @fragments.each_with_index do |f, i|
319 break unless f.kind_of? BlankLine
320 @fragments[i] = nil
321 end
322
323 @fragments.compact!
324 end
325
326 end
327
328end
Note: See TracBrowser for help on using the repository browser.