rdoc-f95 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. data/History.txt +4 -0
  2. data/Manifest.txt +79 -0
  3. data/PostInstall.txt +7 -0
  4. data/README.rdoc +147 -0
  5. data/Rakefile +28 -0
  6. data/bin/rdoc-f95 +70 -0
  7. data/lib/rdoc-f95.rb +306 -0
  8. data/lib/rdoc-f95/code_objects.rb +776 -0
  9. data/lib/rdoc-f95/diagram.rb +342 -0
  10. data/lib/rdoc-f95/dot.rb +249 -0
  11. data/lib/rdoc-f95/generator.rb +1088 -0
  12. data/lib/rdoc-f95/generator/chm.rb +113 -0
  13. data/lib/rdoc-f95/generator/chm/chm.rb +98 -0
  14. data/lib/rdoc-f95/generator/html.rb +370 -0
  15. data/lib/rdoc-f95/generator/html/hefss.rb +414 -0
  16. data/lib/rdoc-f95/generator/html/html.rb +708 -0
  17. data/lib/rdoc-f95/generator/html/kilmer.rb +418 -0
  18. data/lib/rdoc-f95/generator/html/one_page_html.rb +121 -0
  19. data/lib/rdoc-f95/generator/ri.rb +229 -0
  20. data/lib/rdoc-f95/generator/xhtml.rb +106 -0
  21. data/lib/rdoc-f95/generator/xhtml/ctop.xsl +1318 -0
  22. data/lib/rdoc-f95/generator/xhtml/mathml.xsl +42 -0
  23. data/lib/rdoc-f95/generator/xhtml/pmathml.xsl +612 -0
  24. data/lib/rdoc-f95/generator/xhtml/pmathmlcss.xsl +872 -0
  25. data/lib/rdoc-f95/generator/xhtml/xhtml.rb +732 -0
  26. data/lib/rdoc-f95/generator/xml.rb +120 -0
  27. data/lib/rdoc-f95/generator/xml/rdf.rb +113 -0
  28. data/lib/rdoc-f95/generator/xml/xml.rb +111 -0
  29. data/lib/rdoc-f95/install.rb +166 -0
  30. data/lib/rdoc-f95/markup.rb +506 -0
  31. data/lib/rdoc-f95/markup/formatter.rb +14 -0
  32. data/lib/rdoc-f95/markup/fragments.rb +337 -0
  33. data/lib/rdoc-f95/markup/inline.rb +361 -0
  34. data/lib/rdoc-f95/markup/install.rb +57 -0
  35. data/lib/rdoc-f95/markup/lines.rb +152 -0
  36. data/lib/rdoc-f95/markup/mathml_wrapper.rb +91 -0
  37. data/lib/rdoc-f95/markup/preprocess.rb +71 -0
  38. data/lib/rdoc-f95/markup/sample/rdoc2latex.rb +16 -0
  39. data/lib/rdoc-f95/markup/sample/sample.rb +42 -0
  40. data/lib/rdoc-f95/markup/to_flow.rb +185 -0
  41. data/lib/rdoc-f95/markup/to_html.rb +357 -0
  42. data/lib/rdoc-f95/markup/to_html_crossref.rb +123 -0
  43. data/lib/rdoc-f95/markup/to_latex.rb +328 -0
  44. data/lib/rdoc-f95/markup/to_test.rb +50 -0
  45. data/lib/rdoc-f95/markup/to_xhtml_texparser.rb +234 -0
  46. data/lib/rdoc-f95/options.rb +745 -0
  47. data/lib/rdoc-f95/parsers/parse_c.rb +775 -0
  48. data/lib/rdoc-f95/parsers/parse_f95.rb +2499 -0
  49. data/lib/rdoc-f95/parsers/parse_rb.rb +2587 -0
  50. data/lib/rdoc-f95/parsers/parse_simple.rb +39 -0
  51. data/lib/rdoc-f95/parsers/parserfactory.rb +99 -0
  52. data/lib/rdoc-f95/ri.rb +2 -0
  53. data/lib/rdoc-f95/ri/cache.rb +188 -0
  54. data/lib/rdoc-f95/ri/descriptions.rb +147 -0
  55. data/lib/rdoc-f95/ri/display.rb +244 -0
  56. data/lib/rdoc-f95/ri/driver.rb +435 -0
  57. data/lib/rdoc-f95/ri/formatter.rb +603 -0
  58. data/lib/rdoc-f95/ri/paths.rb +105 -0
  59. data/lib/rdoc-f95/ri/reader.rb +106 -0
  60. data/lib/rdoc-f95/ri/util.rb +81 -0
  61. data/lib/rdoc-f95/ri/writer.rb +64 -0
  62. data/lib/rdoc-f95/stats.rb +23 -0
  63. data/lib/rdoc-f95/template.rb +64 -0
  64. data/lib/rdoc-f95/tokenstream.rb +33 -0
  65. data/lib/rdoc-f95/usage.rb +210 -0
  66. data/script/console +10 -0
  67. data/script/destroy +14 -0
  68. data/script/generate +14 -0
  69. data/test/test_helper.rb +3 -0
  70. data/test/test_rdoc-f95.rb +11 -0
  71. metadata +156 -0
@@ -0,0 +1,14 @@
1
+ require 'rdoc-f95/markup'
2
+
3
+ class RDocF95::Markup::Formatter
4
+
5
+ def initialize
6
+ @markup = RDocF95::Markup.new
7
+ end
8
+
9
+ def convert(content, block_exceptions=nil)
10
+ @markup.convert content, self, block_exceptions
11
+ end
12
+
13
+ end
14
+
@@ -0,0 +1,337 @@
1
+ require 'rdoc-f95/markup'
2
+ require 'rdoc-f95/markup/lines'
3
+
4
+ class RDocF95::Markup
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
+ ######
15
+ # This is a simple factory system that lets us associate fragement
16
+ # types (a string) with a subclass of fragment
17
+
18
+ TYPE_MAP = {}
19
+
20
+ def self.type_name(name)
21
+ TYPE_MAP[name] = self
22
+ end
23
+
24
+ def self.for(line)
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)
28
+ end
29
+
30
+ def initialize(level, param, type, txt)
31
+ @level = level
32
+ @param = param
33
+ @type = type
34
+ @txt = ""
35
+ add_text(txt) if txt
36
+ end
37
+
38
+ def add_text(txt)
39
+ @txt << " " if @txt.length > 0
40
+ @txt << txt.tr_s("\n ", " ").strip
41
+ end
42
+
43
+ def to_s
44
+ "L#@level: #{self.class.name.split('::')[-1]}\n#@txt"
45
+ end
46
+
47
+ end
48
+
49
+ ##
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.
52
+
53
+ class Paragraph < Fragment
54
+ type_name :PARAGRAPH
55
+ end
56
+
57
+ class BlankLine < Paragraph
58
+ type_name :BLANK
59
+ end
60
+
61
+ class Heading < Paragraph
62
+ type_name :HEADING
63
+
64
+ def head_level
65
+ @param.to_i
66
+ end
67
+ end
68
+
69
+ ##
70
+ # A List is a fragment with some kind of label
71
+
72
+ class ListBase < Paragraph
73
+ LIST_TYPES = [
74
+ :BULLET,
75
+ :NUMBER,
76
+ :UPPERALPHA,
77
+ :LOWERALPHA,
78
+ :LABELED,
79
+ :NOTE,
80
+ ]
81
+ end
82
+
83
+ class ListItem < ListBase
84
+ type_name :LIST
85
+
86
+ def to_s
87
+ text = if [:NOTE, :LABELED].include? type then
88
+ "#{@param}: #{@txt}"
89
+ else
90
+ @txt
91
+ end
92
+
93
+ "L#@level: #{type} #{self.class.name.split('::')[-1]}\n#{text}"
94
+ end
95
+
96
+ end
97
+
98
+ class ListStart < ListBase
99
+ def initialize(level, param, type)
100
+ super(level, param, type, nil)
101
+ end
102
+ end
103
+
104
+ class ListEnd < ListBase
105
+ def initialize(level, type)
106
+ super(level, "", type, nil)
107
+ end
108
+ end
109
+
110
+ ##
111
+ # Verbatim code contains lines that don't get wrapped.
112
+
113
+ class Verbatim < Fragment
114
+ type_name :VERBATIM
115
+
116
+ def add_text(txt)
117
+ @txt << txt.chomp << "\n"
118
+ end
119
+
120
+ end
121
+
122
+ ##
123
+ # A horizontal rule
124
+
125
+ class Rule < Fragment
126
+ type_name :RULE
127
+ end
128
+
129
+ ##
130
+ # Collect groups of lines together. Each group will end up containing a flow
131
+ # of text.
132
+
133
+ class LineCollection
134
+
135
+ def initialize
136
+ @fragments = []
137
+ end
138
+
139
+ def add(fragment)
140
+ @fragments << fragment
141
+ end
142
+
143
+ def each(&b)
144
+ @fragments.each(&b)
145
+ end
146
+
147
+ def to_a # :nodoc:
148
+ @fragments.map {|fragment| fragment.to_s}
149
+ end
150
+
151
+ ##
152
+ # Factory for different fragment types
153
+
154
+ def fragment_for(*args)
155
+ Fragment.for(*args)
156
+ end
157
+
158
+ ##
159
+ # Tidy up at the end
160
+
161
+ def normalize
162
+ change_verbatim_blank_lines
163
+ add_list_start_and_ends
164
+ add_list_breaks
165
+ tidy_blank_lines
166
+ end
167
+
168
+ def to_s
169
+ @fragments.join("\n----\n")
170
+ end
171
+
172
+ def accept(am, visitor)
173
+ visitor.start_accepting
174
+
175
+ @fragments.each do |fragment|
176
+ case fragment
177
+ when Verbatim
178
+ visitor.accept_verbatim(am, fragment)
179
+ when Rule
180
+ visitor.accept_rule(am, fragment)
181
+ when ListStart
182
+ visitor.accept_list_start(am, fragment)
183
+ when ListEnd
184
+ visitor.accept_list_end(am, fragment)
185
+ when ListItem
186
+ visitor.accept_list_item(am, fragment)
187
+ when BlankLine
188
+ visitor.accept_blank_line(am, fragment)
189
+ when Heading
190
+ visitor.accept_heading(am, fragment)
191
+ when Paragraph
192
+ visitor.accept_paragraph(am, fragment)
193
+ end
194
+ end
195
+
196
+ visitor.end_accepting
197
+ end
198
+
199
+ private
200
+
201
+ # If you have:
202
+ #
203
+ # normal paragraph text.
204
+ #
205
+ # this is code
206
+ #
207
+ # and more code
208
+ #
209
+ # You'll end up with the fragments Paragraph, BlankLine, Verbatim,
210
+ # BlankLine, Verbatim, BlankLine, etc.
211
+ #
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
214
+
215
+ def change_verbatim_blank_lines
216
+ frag_block = nil
217
+ blank_count = 0
218
+ @fragments.each_with_index do |frag, i|
219
+ if frag_block.nil?
220
+ frag_block = frag if Verbatim === frag
221
+ else
222
+ case frag
223
+ when Verbatim
224
+ blank_count.times { frag_block.add_text("\n") }
225
+ blank_count = 0
226
+ frag_block.add_text(frag.txt)
227
+ @fragments[i] = nil # remove out current fragment
228
+ when BlankLine
229
+ if frag_block
230
+ blank_count += 1
231
+ @fragments[i] = nil
232
+ end
233
+ else
234
+ frag_block = nil
235
+ blank_count = 0
236
+ end
237
+ end
238
+ end
239
+ @fragments.compact!
240
+ end
241
+
242
+ ##
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
245
+
246
+ def add_list_start_and_ends
247
+ level = 0
248
+ res = []
249
+ type_stack = []
250
+
251
+ @fragments.each do |fragment|
252
+ # $stderr.puts "#{level} : #{fragment.class.name} : #{fragment.level}"
253
+ new_level = fragment.level
254
+ while (level < new_level)
255
+ level += 1
256
+ type = fragment.type
257
+ res << ListStart.new(level, fragment.param, type) if type
258
+ type_stack.push type
259
+ # $stderr.puts "Start: #{level}"
260
+ end
261
+
262
+ while level > new_level
263
+ type = type_stack.pop
264
+ res << ListEnd.new(level, type) if type
265
+ level -= 1
266
+ # $stderr.puts "End: #{level}, #{type}"
267
+ end
268
+
269
+ res << fragment
270
+ level = fragment.level
271
+ end
272
+ level.downto(1) do |i|
273
+ type = type_stack.pop
274
+ res << ListEnd.new(i, type) if type
275
+ end
276
+
277
+ @fragments = res
278
+ end
279
+
280
+ ##
281
+ # Inserts start/ends between list entries at the same level that have
282
+ # different element types
283
+
284
+ def add_list_breaks
285
+ res = @fragments
286
+
287
+ @fragments = []
288
+ list_stack = []
289
+
290
+ res.each do |fragment|
291
+ case fragment
292
+ when ListStart
293
+ list_stack.push fragment
294
+ when ListEnd
295
+ start = list_stack.pop
296
+ fragment.type = start.type
297
+ when ListItem
298
+ l = list_stack.last
299
+ if fragment.type != l.type
300
+ @fragments << ListEnd.new(l.level, l.type)
301
+ start = ListStart.new(l.level, fragment.param, fragment.type)
302
+ @fragments << start
303
+ list_stack.pop
304
+ list_stack.push start
305
+ end
306
+ else
307
+ ;
308
+ end
309
+ @fragments << fragment
310
+ end
311
+ end
312
+
313
+ ##
314
+ # Tidy up the blank lines:
315
+ # * change Blank/ListEnd into ListEnd/Blank
316
+ # * remove blank lines at the front
317
+
318
+ def tidy_blank_lines
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]
322
+ end
323
+ end
324
+
325
+ # remove leading blanks
326
+ @fragments.each_with_index do |f, i|
327
+ break unless f.kind_of? BlankLine
328
+ @fragments[i] = nil
329
+ end
330
+
331
+ @fragments.compact!
332
+ end
333
+
334
+ end
335
+
336
+ end
337
+
@@ -0,0 +1,361 @@
1
+ require 'rdoc-f95/markup'
2
+
3
+ class RDocF95::Markup
4
+
5
+ ##
6
+ # We manage a set of attributes. Each attribute has a symbol name and a bit
7
+ # value.
8
+
9
+ class Attribute
10
+ SPECIAL = 1
11
+
12
+ @@name_to_bitmap = { :_SPECIAL_ => SPECIAL }
13
+ @@next_bitmap = 2
14
+
15
+ def self.bitmap_for(name)
16
+ bitmap = @@name_to_bitmap[name]
17
+ unless bitmap then
18
+ bitmap = @@next_bitmap
19
+ @@next_bitmap <<= 1
20
+ @@name_to_bitmap[name] = bitmap
21
+ end
22
+ bitmap
23
+ end
24
+
25
+ def self.as_string(bitmap)
26
+ return "none" if bitmap.zero?
27
+ res = []
28
+ @@name_to_bitmap.each do |name, bit|
29
+ res << name if (bitmap & bit) != 0
30
+ end
31
+ res.join(",")
32
+ end
33
+
34
+ def self.each_name_of(bitmap)
35
+ @@name_to_bitmap.each do |name, bit|
36
+ next if bit == SPECIAL
37
+ yield name.to_s if (bitmap & bit) != 0
38
+ end
39
+ end
40
+ end
41
+
42
+ ##
43
+ # An AttrChanger records a change in attributes. It contains a bitmap of the
44
+ # attributes to turn on, and a bitmap of those to turn off.
45
+
46
+ AttrChanger = Struct.new(:turn_on, :turn_off)
47
+
48
+ class AttrChanger
49
+ def to_s
50
+ "Attr: +#{Attribute.as_string(@turn_on)}/-#{Attribute.as_string(@turn_on)}"
51
+ end
52
+ end
53
+
54
+ ##
55
+ # An array of attributes which parallels the characters in a string.
56
+
57
+ class AttrSpan
58
+ def initialize(length)
59
+ @attrs = Array.new(length, 0)
60
+ end
61
+
62
+ def set_attrs(start, length, bits)
63
+ for i in start ... (start+length)
64
+ @attrs[i] |= bits
65
+ end
66
+ end
67
+
68
+ def [](n)
69
+ @attrs[n]
70
+ end
71
+ end
72
+
73
+ ##
74
+ # Hold details of a special sequence
75
+
76
+ class Special
77
+ attr_reader :type
78
+ attr_accessor :text
79
+
80
+ def initialize(type, text)
81
+ @type, @text = type, text
82
+ end
83
+
84
+ def ==(o)
85
+ self.text == o.text && self.type == o.type
86
+ end
87
+
88
+ def inspect
89
+ "#<RDocF95::Markup::Special:0x%x @type=%p, name=%p @text=%p>" % [
90
+ object_id, @type, RDocF95::Markup::Attribute.as_string(type), text.dump]
91
+ end
92
+
93
+ def to_s
94
+ "Special: type=#{type}, name=#{RDocF95::Markup::Attribute.as_string type}, text=#{text.dump}"
95
+ end
96
+
97
+ end
98
+
99
+ class AttributeManager
100
+
101
+ NULL = "\000".freeze
102
+
103
+ ##
104
+ # We work by substituting non-printing characters in to the text. For now
105
+ # I'm assuming that I can substitute a character in the range 0..8 for a 7
106
+ # bit character without damaging the encoded string, but this might be
107
+ # optimistic
108
+
109
+ A_PROTECT = 004
110
+ PROTECT_ATTR = A_PROTECT.chr
111
+
112
+ ##
113
+ # This maps delimiters that occur around words (such as *bold* or +tt+)
114
+ # where the start and end delimiters and the same. This lets us optimize
115
+ # the regexp
116
+
117
+ MATCHING_WORD_PAIRS = {}
118
+
119
+ ##
120
+ # And this is used when the delimiters aren't the same. In this case the
121
+ # hash maps a pattern to the attribute character
122
+
123
+ WORD_PAIR_MAP = {}
124
+
125
+ ##
126
+ # This maps HTML tags to the corresponding attribute char
127
+
128
+ HTML_TAGS = {}
129
+
130
+ ##
131
+ # And this maps _special_ sequences to a name. A special sequence is
132
+ # something like a WikiWord
133
+
134
+ SPECIAL = {}
135
+
136
+ ##
137
+ # Return an attribute object with the given turn_on and turn_off bits set
138
+
139
+ def attribute(turn_on, turn_off)
140
+ AttrChanger.new(turn_on, turn_off)
141
+ end
142
+
143
+ def change_attribute(current, new)
144
+ diff = current ^ new
145
+ attribute(new & diff, current & diff)
146
+ end
147
+
148
+ def changed_attribute_by_name(current_set, new_set)
149
+ current = new = 0
150
+ current_set.each {|name| current |= Attribute.bitmap_for(name) }
151
+ new_set.each {|name| new |= Attribute.bitmap_for(name) }
152
+ change_attribute(current, new)
153
+ end
154
+
155
+ def copy_string(start_pos, end_pos)
156
+ res = @str[start_pos...end_pos]
157
+ res.gsub!(/\000/, '')
158
+ res
159
+ end
160
+
161
+ ##
162
+ # Map attributes like <b>text</b>to the sequence
163
+ # \001\002<char>\001\003<char>, where <char> is a per-attribute specific
164
+ # character
165
+
166
+ def convert_attrs(str, attrs)
167
+ # first do matching ones
168
+ tags = MATCHING_WORD_PAIRS.keys.join("")
169
+
170
+ re = /(^|\W)([#{tags}])([#\\]?[\w.\/]+?\S?)\2(\W|$)/
171
+
172
+ 1 while str.gsub!(re) do
173
+ attr = MATCHING_WORD_PAIRS[$2]
174
+ attrs.set_attrs($`.length + $1.length + $2.length, $3.length, attr)
175
+ $1 + NULL * $2.length + $3 + NULL * $2.length + $4
176
+ end
177
+
178
+ # then non-matching
179
+ unless WORD_PAIR_MAP.empty? then
180
+ WORD_PAIR_MAP.each do |regexp, attr|
181
+ str.gsub!(regexp) {
182
+ attrs.set_attrs($`.length + $1.length, $2.length, attr)
183
+ NULL * $1.length + $2 + NULL * $3.length
184
+ }
185
+ end
186
+ end
187
+ end
188
+
189
+ def convert_html(str, attrs)
190
+ tags = HTML_TAGS.keys.join '|'
191
+
192
+ 1 while str.gsub!(/<(#{tags})>(.*?)<\/\1>/i) {
193
+ attr = HTML_TAGS[$1.downcase]
194
+ html_length = $1.length + 2
195
+ seq = NULL * html_length
196
+ attrs.set_attrs($`.length + html_length, $2.length, attr)
197
+ seq + $2 + seq + NULL
198
+ }
199
+ end
200
+
201
+ def convert_specials(str, attrs)
202
+ unless SPECIAL.empty?
203
+ SPECIAL.each do |regexp, attr|
204
+ str.scan(regexp) do
205
+ attrs.set_attrs($`.length, $&.length, attr | Attribute::SPECIAL)
206
+ end
207
+ end
208
+ end
209
+ end
210
+
211
+ ##
212
+ # A \ in front of a character that would normally be processed turns off
213
+ # processing. We do this by turning \< into <#{PROTECT}
214
+
215
+ PROTECTABLE = %w[<\\]
216
+
217
+ def mask_protected_sequences
218
+ protect_pattern = Regexp.new("\\\\([#{Regexp.escape(PROTECTABLE.join(''))}])")
219
+ @str.gsub!(protect_pattern, "\\1#{PROTECT_ATTR}")
220
+ end
221
+
222
+ def unmask_protected_sequences
223
+ @str.gsub!(/(.)#{PROTECT_ATTR}/, "\\1\000")
224
+ end
225
+
226
+ def initialize
227
+ add_word_pair("*", "*", :BOLD)
228
+ add_word_pair("_", "_", :EM)
229
+ add_word_pair("+", "+", :TT)
230
+
231
+ add_html("em", :EM)
232
+ add_html("i", :EM)
233
+ add_html("b", :BOLD)
234
+ add_html("tt", :TT)
235
+ add_html("code", :TT)
236
+
237
+ add_special(/<!--(.*?)-->/, :COMMENT)
238
+ end
239
+
240
+ def add_word_pair(start, stop, name)
241
+ raise "Word flags may not start '<'" if start[0] == ?<
242
+ bitmap = Attribute.bitmap_for(name)
243
+ if start == stop
244
+ MATCHING_WORD_PAIRS[start] = bitmap
245
+ else
246
+ pattern = Regexp.new("(" + Regexp.escape(start) + ")" +
247
+ # "([A-Za-z]+)" +
248
+ "(\\S+)" +
249
+ "(" + Regexp.escape(stop) +")")
250
+ WORD_PAIR_MAP[pattern] = bitmap
251
+ end
252
+ PROTECTABLE << start[0,1]
253
+ PROTECTABLE.uniq!
254
+ end
255
+
256
+ def add_html(tag, name)
257
+ HTML_TAGS[tag.downcase] = Attribute.bitmap_for(name)
258
+ end
259
+
260
+ def add_special(pattern, name)
261
+ SPECIAL[pattern] = Attribute.bitmap_for(name)
262
+ end
263
+
264
+ def flow(str)
265
+ @str = str
266
+
267
+ puts("Before flow, str='#{@str.dump}'") if $DEBUG_RDOC
268
+ mask_protected_sequences
269
+
270
+ @attrs = AttrSpan.new(@str.length)
271
+
272
+ puts("After protecting, str='#{@str.dump}'") if $DEBUG_RDOC
273
+
274
+ convert_attrs(@str, @attrs)
275
+ convert_html(@str, @attrs)
276
+ convert_specials(str, @attrs)
277
+
278
+ unmask_protected_sequences
279
+
280
+ puts("After flow, str='#{@str.dump}'") if $DEBUG_RDOC
281
+
282
+ return split_into_flow
283
+ end
284
+
285
+ def display_attributes
286
+ puts
287
+ puts @str.tr(NULL, "!")
288
+ bit = 1
289
+ 16.times do |bno|
290
+ line = ""
291
+ @str.length.times do |i|
292
+ if (@attrs[i] & bit) == 0
293
+ line << " "
294
+ else
295
+ if bno.zero?
296
+ line << "S"
297
+ else
298
+ line << ("%d" % (bno+1))
299
+ end
300
+ end
301
+ end
302
+ puts(line) unless line =~ /^ *$/
303
+ bit <<= 1
304
+ end
305
+ end
306
+
307
+ def split_into_flow
308
+ display_attributes if $DEBUG_RDOC
309
+
310
+ res = []
311
+ current_attr = 0
312
+ str = ""
313
+
314
+ str_len = @str.length
315
+
316
+ # skip leading invisible text
317
+ i = 0
318
+ i += 1 while i < str_len and @str[i] == "\0"
319
+ start_pos = i
320
+
321
+ # then scan the string, chunking it on attribute changes
322
+ while i < str_len
323
+ new_attr = @attrs[i]
324
+ if new_attr != current_attr
325
+ if i > start_pos
326
+ res << copy_string(start_pos, i)
327
+ start_pos = i
328
+ end
329
+
330
+ res << change_attribute(current_attr, new_attr)
331
+ current_attr = new_attr
332
+
333
+ if (current_attr & Attribute::SPECIAL) != 0
334
+ i += 1 while i < str_len and (@attrs[i] & Attribute::SPECIAL) != 0
335
+ res << Special.new(current_attr, copy_string(start_pos, i))
336
+ start_pos = i
337
+ next
338
+ end
339
+ end
340
+
341
+ # move on, skipping any invisible characters
342
+ begin
343
+ i += 1
344
+ end while i < str_len and @str[i] == "\0"
345
+ end
346
+
347
+ # tidy up trailing text
348
+ if start_pos < str_len
349
+ res << copy_string(start_pos, str_len)
350
+ end
351
+
352
+ # and reset to all attributes off
353
+ res << change_attribute(current_attr, 0) if current_attr != 0
354
+
355
+ return res
356
+ end
357
+
358
+ end
359
+
360
+ end
361
+