1
# We're responsible for generating all the HTML files
2
# from the object tree defined in code_objects.rb. We
5
# [files] an html file for each input file given. These
6
# input files appear as objects of class
9
# [classes] an html file for each class or module encountered.
10
# These classes are not grouped by file: if a file
11
# contains four classes, we'll generate an html
12
# file for the file itself, and four html files
13
# for the individual classes.
15
# [indices] we generate three indices for files, classes,
16
# and methods. These are displayed in a browser
17
# like window with three index panes across the
18
# top and the selected description below
20
# Method descriptions appear in whatever entity (file, class,
21
# or module) that contains them.
23
# We generate files in a structure below a specified subdirectory,
29
# | |__ per file summaries
32
# |__ per class/module descriptions
34
# HTML is generated using the Template class.
39
require 'rdoc/options'
40
require 'rdoc/template'
41
require 'rdoc/markup/simple_markup'
42
require 'rdoc/markup/simple_markup/to_flow'
45
require 'rdoc/ri/ri_cache'
46
require 'rdoc/ri/ri_reader'
47
require 'rdoc/ri/ri_writer'
48
require 'rdoc/ri/ri_descriptions'
55
# Generators may need to return specific subclasses depending
56
# on the options they are passed. Because of this
57
# we create them using a factory
59
def RIGenerator.for(options)
67
# Set up a new HTML generator. Basically all we do here is load
68
# up the correct output temlate
70
def initialize(options) #:not-new:
72
@ri_writer = RI::RiWriter.new(".")
73
@markup = SM::SimpleMarkup.new
74
@to_flow = SM::ToFlow.new
79
# Build the initial indices and output objects
80
# based on an array of TopLevel objects containing
81
# the extracted information.
83
def generate(toplevels)
84
RDoc::TopLevel.all_classes_and_modules.each do |cls|
89
def process_class(from_class)
90
generate_class_info(from_class)
92
# now recure into this classes constituent classess
93
from_class.each_classmodule do |mod|
98
def generate_class_info(cls)
99
if cls === RDoc::NormalModule
100
cls_desc = RI::ModuleDescription.new
102
cls_desc = RI::ClassDescription.new
103
cls_desc.superclass = cls.superclass
105
cls_desc.name = cls.name
106
cls_desc.full_name = cls.full_name
107
cls_desc.comment = markup(cls.comment)
109
cls_desc.attributes =cls.attributes.sort.map do |a|
110
RI::Attribute.new(a.name, a.rw, markup(a.comment))
113
cls_desc.constants = cls.constants.map do |c|
114
RI::Constant.new(c.name, c.value, markup(c.comment))
117
cls_desc.includes = cls.includes.map do |i|
118
RI::IncludedModule.new(i.name)
121
class_methods, instance_methods = method_list(cls)
123
cls_desc.class_methods = class_methods.map do |m|
124
RI::MethodSummary.new(m.name)
126
cls_desc.instance_methods = instance_methods.map do |m|
127
RI::MethodSummary.new(m.name)
130
update_or_replace(cls_desc)
132
class_methods.each do |m|
133
generate_method_info(cls_desc, m)
136
instance_methods.each do |m|
137
generate_method_info(cls_desc, m)
142
def generate_method_info(cls_desc, method)
143
meth_desc = RI::MethodDescription.new
144
meth_desc.name = method.name
145
meth_desc.full_name = cls_desc.full_name
147
meth_desc.full_name += "::"
149
meth_desc.full_name += "#"
151
meth_desc.full_name << method.name
153
meth_desc.comment = markup(method.comment)
154
meth_desc.params = params_of(method)
155
meth_desc.visibility = method.visibility.to_s
156
meth_desc.is_singleton = method.singleton
157
meth_desc.block_params = method.block_params
159
meth_desc.aliases = method.aliases.map do |a|
160
RI::AliasName.new(a.name)
163
@ri_writer.add_method(cls_desc, meth_desc)
168
# return a list of class and instance methods that we'll be
172
list = cls.method_list
173
unless @options.show_all
174
list = list.find_all do |m|
175
m.visibility == :public || m.visibility == :protected || m.force_documentation
181
list.sort.each do |m|
191
def params_of(method)
195
params = method.params || ""
197
p = params.gsub(/\s*\#.*/, '')
198
p = p.tr("\n", " ").squeeze(" ")
199
p = "(" + p + ")" unless p[0] == ?(
201
if (block = method.block_params)
202
block.gsub!(/\s*\#.*/, '')
203
block = block.tr("\n", " ").squeeze(" ")
205
block.sub!(/^\(/, '').sub!(/\)/, '')
207
p << " {|#{block.strip}| ...}"
214
return nil if !comment || comment.empty?
216
# Convert leading comment markers to spaces, but only
217
# if all non-blank lines have them
219
if comment =~ /^(?>\s*)[^\#]/
222
content = comment.gsub(/^\s*(#+)/) { $1.tr('#',' ') }
224
@markup.convert(content, @to_flow)
228
# By default we replace existing classes with the
229
# same name. If the --merge option was given, we instead
230
# merge this definition into an existing class. We add
231
# our methods, aliases, etc to that class, but do not
232
# change the class's description.
234
def update_or_replace(cls_desc)
238
rdr = RI::RiReader.new(RI::RiCache.new(@options.op_dir))
240
namespace = rdr.top_level_namespace
241
namespace = rdr.lookup_namespace_in(cls_desc.name, namespace)
243
$stderr.puts "You asked me to merge this source into existing "
244
$stderr.puts "documentation. This file references a class or "
245
$stderr.puts "module called #{cls_desc.name} which I don't"
246
$stderr.puts "have existing documentation for."
248
$stderr.puts "Perhaps you need to generate its documentation first"
251
old_cls = namespace[0]
256
# no merge: simply overwrite
257
@ri_writer.remove_class(cls_desc)
258
@ri_writer.add_class(cls_desc)
260
# existing class: merge in
261
old_desc = rdr.get_class(old_cls)
263
old_desc.merge_in(cls_desc)
264
@ri_writer.add_class(old_desc)