rdoc 2.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rdoc might be problematic. Click here for more details.

Files changed (62) hide show
  1. data/History.txt +13 -0
  2. data/Manifest.txt +61 -0
  3. data/README.txt +34 -0
  4. data/Rakefile +10 -0
  5. data/bin/rdoc +22 -0
  6. data/bin/ri +6 -0
  7. data/lib/rdoc.rb +277 -0
  8. data/lib/rdoc/code_objects.rb +776 -0
  9. data/lib/rdoc/diagram.rb +338 -0
  10. data/lib/rdoc/dot.rb +249 -0
  11. data/lib/rdoc/generator.rb +1048 -0
  12. data/lib/rdoc/generator/chm.rb +113 -0
  13. data/lib/rdoc/generator/chm/chm.rb +98 -0
  14. data/lib/rdoc/generator/html.rb +370 -0
  15. data/lib/rdoc/generator/html/hefss.rb +414 -0
  16. data/lib/rdoc/generator/html/html.rb +704 -0
  17. data/lib/rdoc/generator/html/kilmer.rb +418 -0
  18. data/lib/rdoc/generator/html/one_page_html.rb +121 -0
  19. data/lib/rdoc/generator/ri.rb +229 -0
  20. data/lib/rdoc/generator/xml.rb +120 -0
  21. data/lib/rdoc/generator/xml/rdf.rb +113 -0
  22. data/lib/rdoc/generator/xml/xml.rb +111 -0
  23. data/lib/rdoc/markup.rb +473 -0
  24. data/lib/rdoc/markup/attribute_manager.rb +274 -0
  25. data/lib/rdoc/markup/formatter.rb +14 -0
  26. data/lib/rdoc/markup/fragments.rb +337 -0
  27. data/lib/rdoc/markup/inline.rb +101 -0
  28. data/lib/rdoc/markup/lines.rb +152 -0
  29. data/lib/rdoc/markup/preprocess.rb +71 -0
  30. data/lib/rdoc/markup/to_flow.rb +185 -0
  31. data/lib/rdoc/markup/to_html.rb +353 -0
  32. data/lib/rdoc/markup/to_html_crossref.rb +86 -0
  33. data/lib/rdoc/markup/to_latex.rb +328 -0
  34. data/lib/rdoc/markup/to_test.rb +50 -0
  35. data/lib/rdoc/options.rb +616 -0
  36. data/lib/rdoc/parsers/parse_c.rb +775 -0
  37. data/lib/rdoc/parsers/parse_f95.rb +1841 -0
  38. data/lib/rdoc/parsers/parse_rb.rb +2584 -0
  39. data/lib/rdoc/parsers/parse_simple.rb +40 -0
  40. data/lib/rdoc/parsers/parserfactory.rb +99 -0
  41. data/lib/rdoc/rdoc.rb +277 -0
  42. data/lib/rdoc/ri.rb +4 -0
  43. data/lib/rdoc/ri/cache.rb +188 -0
  44. data/lib/rdoc/ri/descriptions.rb +150 -0
  45. data/lib/rdoc/ri/display.rb +274 -0
  46. data/lib/rdoc/ri/driver.rb +452 -0
  47. data/lib/rdoc/ri/formatter.rb +616 -0
  48. data/lib/rdoc/ri/paths.rb +102 -0
  49. data/lib/rdoc/ri/reader.rb +106 -0
  50. data/lib/rdoc/ri/util.rb +81 -0
  51. data/lib/rdoc/ri/writer.rb +68 -0
  52. data/lib/rdoc/stats.rb +25 -0
  53. data/lib/rdoc/template.rb +64 -0
  54. data/lib/rdoc/tokenstream.rb +33 -0
  55. data/test/test_rdoc_c_parser.rb +261 -0
  56. data/test/test_rdoc_markup.rb +613 -0
  57. data/test/test_rdoc_markup_attribute_manager.rb +224 -0
  58. data/test/test_rdoc_ri_attribute_formatter.rb +42 -0
  59. data/test/test_rdoc_ri_default_display.rb +295 -0
  60. data/test/test_rdoc_ri_formatter.rb +318 -0
  61. data/test/test_rdoc_ri_overstrike_formatter.rb +69 -0
  62. metadata +134 -0
@@ -0,0 +1,274 @@
1
+ require 'rdoc/markup/inline'
2
+
3
+ class RDoc::Markup::AttributeManager
4
+
5
+ NULL = "\000".freeze
6
+
7
+ ##
8
+ # We work by substituting non-printing characters in to the text. For now
9
+ # I'm assuming that I can substitute a character in the range 0..8 for a 7
10
+ # bit character without damaging the encoded string, but this might be
11
+ # optimistic
12
+
13
+ A_PROTECT = 004
14
+ PROTECT_ATTR = A_PROTECT.chr
15
+
16
+ ##
17
+ # This maps delimiters that occur around words (such as *bold* or +tt+)
18
+ # where the start and end delimiters and the same. This lets us optimize
19
+ # the regexp
20
+
21
+ MATCHING_WORD_PAIRS = {}
22
+
23
+ ##
24
+ # And this is used when the delimiters aren't the same. In this case the
25
+ # hash maps a pattern to the attribute character
26
+
27
+ WORD_PAIR_MAP = {}
28
+
29
+ ##
30
+ # This maps HTML tags to the corresponding attribute char
31
+
32
+ HTML_TAGS = {}
33
+
34
+ ##
35
+ # And this maps _special_ sequences to a name. A special sequence is
36
+ # something like a WikiWord
37
+
38
+ SPECIAL = {}
39
+
40
+ ##
41
+ # Return an attribute object with the given turn_on and turn_off bits set
42
+
43
+ def attribute(turn_on, turn_off)
44
+ RDoc::Markup::AttrChanger.new turn_on, turn_off
45
+ end
46
+
47
+ def change_attribute(current, new)
48
+ diff = current ^ new
49
+ attribute(new & diff, current & diff)
50
+ end
51
+
52
+ def changed_attribute_by_name(current_set, new_set)
53
+ current = new = 0
54
+ current_set.each do |name|
55
+ current |= RDoc::Markup::Attribute.bitmap_for(name)
56
+ end
57
+
58
+ new_set.each do |name|
59
+ new |= RDoc::Markup::Attribute.bitmap_for(name)
60
+ end
61
+
62
+ change_attribute(current, new)
63
+ end
64
+
65
+ def copy_string(start_pos, end_pos)
66
+ res = @str[start_pos...end_pos]
67
+ res.gsub!(/\000/, '')
68
+ res
69
+ end
70
+
71
+ ##
72
+ # Map attributes like <b>text</b>to the sequence
73
+ # \001\002<char>\001\003<char>, where <char> is a per-attribute specific
74
+ # character
75
+
76
+ def convert_attrs(str, attrs)
77
+ # first do matching ones
78
+ tags = MATCHING_WORD_PAIRS.keys.join("")
79
+
80
+ re = /(^|\W)([#{tags}])([#:\\]?[\w.\/-]+?\S?)\2(\W|$)/
81
+
82
+ 1 while str.gsub!(re) do
83
+ attr = MATCHING_WORD_PAIRS[$2]
84
+ attrs.set_attrs($`.length + $1.length + $2.length, $3.length, attr)
85
+ $1 + NULL * $2.length + $3 + NULL * $2.length + $4
86
+ end
87
+
88
+ # then non-matching
89
+ unless WORD_PAIR_MAP.empty? then
90
+ WORD_PAIR_MAP.each do |regexp, attr|
91
+ str.gsub!(regexp) {
92
+ attrs.set_attrs($`.length + $1.length, $2.length, attr)
93
+ NULL * $1.length + $2 + NULL * $3.length
94
+ }
95
+ end
96
+ end
97
+ end
98
+
99
+ def convert_html(str, attrs)
100
+ tags = HTML_TAGS.keys.join '|'
101
+
102
+ 1 while str.gsub!(/<(#{tags})>(.*?)<\/\1>/i) {
103
+ attr = HTML_TAGS[$1.downcase]
104
+ html_length = $1.length + 2
105
+ seq = NULL * html_length
106
+ attrs.set_attrs($`.length + html_length, $2.length, attr)
107
+ seq + $2 + seq + NULL
108
+ }
109
+ end
110
+
111
+ def convert_specials(str, attrs)
112
+ unless SPECIAL.empty?
113
+ SPECIAL.each do |regexp, attr|
114
+ str.scan(regexp) do
115
+ attrs.set_attrs($`.length, $&.length,
116
+ attr | RDoc::Markup::Attribute::SPECIAL)
117
+ end
118
+ end
119
+ end
120
+ end
121
+
122
+ ##
123
+ # A \ in front of a character that would normally be processed turns off
124
+ # processing. We do this by turning \< into <#{PROTECT}
125
+
126
+ PROTECTABLE = %w[<\\]
127
+
128
+ def mask_protected_sequences
129
+ protect_pattern = Regexp.new("\\\\([#{Regexp.escape(PROTECTABLE.join(''))}])")
130
+ @str.gsub!(protect_pattern, "\\1#{PROTECT_ATTR}")
131
+ end
132
+
133
+ def unmask_protected_sequences
134
+ @str.gsub!(/(.)#{PROTECT_ATTR}/, "\\1\000")
135
+ end
136
+
137
+ def initialize
138
+ add_word_pair("*", "*", :BOLD)
139
+ add_word_pair("_", "_", :EM)
140
+ add_word_pair("+", "+", :TT)
141
+
142
+ add_html("em", :EM)
143
+ add_html("i", :EM)
144
+ add_html("b", :BOLD)
145
+ add_html("tt", :TT)
146
+ add_html("code", :TT)
147
+
148
+ add_special(/<!--(.*?)-->/, :COMMENT)
149
+ end
150
+
151
+ def add_word_pair(start, stop, name)
152
+ raise ArgumentError, "Word flags may not start with '<'" if
153
+ start[0,1] == '<'
154
+
155
+ bitmap = RDoc::Markup::Attribute.bitmap_for name
156
+
157
+ if start == stop then
158
+ MATCHING_WORD_PAIRS[start] = bitmap
159
+ else
160
+ pattern = /(#{Regexp.escape start})(\S+)(#{Regexp.escape stop})/
161
+ WORD_PAIR_MAP[pattern] = bitmap
162
+ end
163
+
164
+ PROTECTABLE << start[0,1]
165
+ PROTECTABLE.uniq!
166
+ end
167
+
168
+ def add_html(tag, name)
169
+ HTML_TAGS[tag.downcase] = RDoc::Markup::Attribute.bitmap_for name
170
+ end
171
+
172
+ def add_special(pattern, name)
173
+ SPECIAL[pattern] = RDoc::Markup::Attribute.bitmap_for name
174
+ end
175
+
176
+ def flow(str)
177
+ @str = str
178
+
179
+ puts("Before flow, str='#{@str.dump}'") if $DEBUG_RDOC
180
+ mask_protected_sequences
181
+
182
+ @attrs = RDoc::Markup::AttrSpan.new @str.length
183
+
184
+ puts("After protecting, str='#{@str.dump}'") if $DEBUG_RDOC
185
+
186
+ convert_attrs(@str, @attrs)
187
+ convert_html(@str, @attrs)
188
+ convert_specials(str, @attrs)
189
+
190
+ unmask_protected_sequences
191
+
192
+ puts("After flow, str='#{@str.dump}'") if $DEBUG_RDOC
193
+
194
+ return split_into_flow
195
+ end
196
+
197
+ def display_attributes
198
+ puts
199
+ puts @str.tr(NULL, "!")
200
+ bit = 1
201
+ 16.times do |bno|
202
+ line = ""
203
+ @str.length.times do |i|
204
+ if (@attrs[i] & bit) == 0
205
+ line << " "
206
+ else
207
+ if bno.zero?
208
+ line << "S"
209
+ else
210
+ line << ("%d" % (bno+1))
211
+ end
212
+ end
213
+ end
214
+ puts(line) unless line =~ /^ *$/
215
+ bit <<= 1
216
+ end
217
+ end
218
+
219
+ def split_into_flow
220
+ display_attributes if $DEBUG_RDOC
221
+
222
+ res = []
223
+ current_attr = 0
224
+ str = ""
225
+
226
+ str_len = @str.length
227
+
228
+ # skip leading invisible text
229
+ i = 0
230
+ i += 1 while i < str_len and @str[i].chr == "\0"
231
+ start_pos = i
232
+
233
+ # then scan the string, chunking it on attribute changes
234
+ while i < str_len
235
+ new_attr = @attrs[i]
236
+ if new_attr != current_attr
237
+ if i > start_pos
238
+ res << copy_string(start_pos, i)
239
+ start_pos = i
240
+ end
241
+
242
+ res << change_attribute(current_attr, new_attr)
243
+ current_attr = new_attr
244
+
245
+ if (current_attr & RDoc::Markup::Attribute::SPECIAL) != 0 then
246
+ i += 1 while
247
+ i < str_len and (@attrs[i] & RDoc::Markup::Attribute::SPECIAL) != 0
248
+
249
+ res << RDoc::Markup::Special.new(current_attr,
250
+ copy_string(start_pos, i))
251
+ start_pos = i
252
+ next
253
+ end
254
+ end
255
+
256
+ # move on, skipping any invisible characters
257
+ begin
258
+ i += 1
259
+ end while i < str_len and @str[i].chr == "\0"
260
+ end
261
+
262
+ # tidy up trailing text
263
+ if start_pos < str_len
264
+ res << copy_string(start_pos, str_len)
265
+ end
266
+
267
+ # and reset to all attributes off
268
+ res << change_attribute(current_attr, 0) if current_attr != 0
269
+
270
+ return res
271
+ end
272
+
273
+ end
274
+
@@ -0,0 +1,14 @@
1
+ require 'rdoc/markup'
2
+
3
+ class RDoc::Markup::Formatter
4
+
5
+ def initialize
6
+ @markup = RDoc::Markup.new
7
+ end
8
+
9
+ def convert(content)
10
+ @markup.convert content, self
11
+ end
12
+
13
+ end
14
+
@@ -0,0 +1,337 @@
1
+ require 'rdoc/markup'
2
+ require 'rdoc/markup/lines'
3
+
4
+ class RDoc::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
+