source: extensions/gsdl-video/trunk/installed/cmdline/lib/ruby/1.8/rdoc/generators/ri_generator.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.1 KB
Line 
1# We're responsible for generating all the HTML files
2# from the object tree defined in code_objects.rb. We
3# generate:
4#
5# [files] an html file for each input file given. These
6# input files appear as objects of class
7# TopLevel
8#
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.
14#
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
19#
20# Method descriptions appear in whatever entity (file, class,
21# or module) that contains them.
22#
23# We generate files in a structure below a specified subdirectory,
24# normally +doc+.
25#
26# opdir
27# |
28# |___ files
29# | |__ per file summaries
30# |
31# |___ classes
32# |__ per class/module descriptions
33#
34# HTML is generated using the Template class.
35#
36
37require 'ftools'
38
39require 'rdoc/options'
40require 'rdoc/template'
41require 'rdoc/markup/simple_markup'
42require 'rdoc/markup/simple_markup/to_flow'
43require 'cgi'
44
45require 'rdoc/ri/ri_cache'
46require 'rdoc/ri/ri_reader'
47require 'rdoc/ri/ri_writer'
48require 'rdoc/ri/ri_descriptions'
49
50module Generators
51
52
53 class RIGenerator
54
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
58
59 def RIGenerator.for(options)
60 new(options)
61 end
62
63 class <<self
64 protected :new
65 end
66
67 # Set up a new HTML generator. Basically all we do here is load
68 # up the correct output temlate
69
70 def initialize(options) #:not-new:
71 @options = options
72 @ri_writer = RI::RiWriter.new(".")
73 @markup = SM::SimpleMarkup.new
74 @to_flow = SM::ToFlow.new
75 end
76
77
78 ##
79 # Build the initial indices and output objects
80 # based on an array of TopLevel objects containing
81 # the extracted information.
82
83 def generate(toplevels)
84 RDoc::TopLevel.all_classes_and_modules.each do |cls|
85 process_class(cls)
86 end
87 end
88
89 def process_class(from_class)
90 generate_class_info(from_class)
91
92 # now recure into this classes constituent classess
93 from_class.each_classmodule do |mod|
94 process_class(mod)
95 end
96 end
97
98 def generate_class_info(cls)
99 if cls === RDoc::NormalModule
100 cls_desc = RI::ModuleDescription.new
101 else
102 cls_desc = RI::ClassDescription.new
103 cls_desc.superclass = cls.superclass
104 end
105 cls_desc.name = cls.name
106 cls_desc.full_name = cls.full_name
107 cls_desc.comment = markup(cls.comment)
108
109 cls_desc.attributes =cls.attributes.sort.map do |a|
110 RI::Attribute.new(a.name, a.rw, markup(a.comment))
111 end
112
113 cls_desc.constants = cls.constants.map do |c|
114 RI::Constant.new(c.name, c.value, markup(c.comment))
115 end
116
117 cls_desc.includes = cls.includes.map do |i|
118 RI::IncludedModule.new(i.name)
119 end
120
121 class_methods, instance_methods = method_list(cls)
122
123 cls_desc.class_methods = class_methods.map do |m|
124 RI::MethodSummary.new(m.name)
125 end
126 cls_desc.instance_methods = instance_methods.map do |m|
127 RI::MethodSummary.new(m.name)
128 end
129
130 update_or_replace(cls_desc)
131
132 class_methods.each do |m|
133 generate_method_info(cls_desc, m)
134 end
135
136 instance_methods.each do |m|
137 generate_method_info(cls_desc, m)
138 end
139 end
140
141
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
146 if method.singleton
147 meth_desc.full_name += "::"
148 else
149 meth_desc.full_name += "#"
150 end
151 meth_desc.full_name << method.name
152
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
158
159 meth_desc.aliases = method.aliases.map do |a|
160 RI::AliasName.new(a.name)
161 end
162
163 @ri_writer.add_method(cls_desc, meth_desc)
164 end
165
166 private
167
168 # return a list of class and instance methods that we'll be
169 # documenting
170
171 def method_list(cls)
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
176 end
177 end
178
179 c = []
180 i = []
181 list.sort.each do |m|
182 if m.singleton
183 c << m
184 else
185 i << m
186 end
187 end
188 return c,i
189 end
190
191 def params_of(method)
192 if method.call_seq
193 method.call_seq
194 else
195 params = method.params || ""
196
197 p = params.gsub(/\s*\#.*/, '')
198 p = p.tr("\n", " ").squeeze(" ")
199 p = "(" + p + ")" unless p[0] == ?(
200
201 if (block = method.block_params)
202 block.gsub!(/\s*\#.*/, '')
203 block = block.tr("\n", " ").squeeze(" ")
204 if block[0] == ?(
205 block.sub!(/^\(/, '').sub!(/\)/, '')
206 end
207 p << " {|#{block.strip}| ...}"
208 end
209 p
210 end
211 end
212
213 def markup(comment)
214 return nil if !comment || comment.empty?
215
216 # Convert leading comment markers to spaces, but only
217 # if all non-blank lines have them
218
219 if comment =~ /^(?>\s*)[^\#]/
220 content = comment
221 else
222 content = comment.gsub(/^\s*(#+)/) { $1.tr('#',' ') }
223 end
224 @markup.convert(content, @to_flow)
225 end
226
227
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.
233
234 def update_or_replace(cls_desc)
235 old_cls = nil
236
237 if @options.merge
238 rdr = RI::RiReader.new(RI::RiCache.new(@options.op_dir))
239
240 namespace = rdr.top_level_namespace
241 namespace = rdr.lookup_namespace_in(cls_desc.name, namespace)
242 if namespace.empty?
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."
247 $stderr.puts
248 $stderr.puts "Perhaps you need to generate its documentation first"
249 exit 1
250 else
251 old_cls = namespace[0]
252 end
253 end
254
255 if old_cls.nil?
256 # no merge: simply overwrite
257 @ri_writer.remove_class(cls_desc)
258 @ri_writer.add_class(cls_desc)
259 else
260 # existing class: merge in
261 old_desc = rdr.get_class(old_cls)
262
263 old_desc.merge_in(cls_desc)
264 @ri_writer.add_class(old_desc)
265 end
266 end
267 end
268end
Note: See TracBrowser for help on using the repository browser.