2003-12-16 00:44:25 -05:00
|
|
|
# We're responsible for generating all the HTML files
|
|
|
|
# from the object tree defined in code_objects.rb. We
|
|
|
|
# generate:
|
|
|
|
#
|
|
|
|
# [files] an html file for each input file given. These
|
|
|
|
# input files appear as objects of class
|
|
|
|
# TopLevel
|
|
|
|
#
|
|
|
|
# [classes] an html file for each class or module encountered.
|
|
|
|
# These classes are not grouped by file: if a file
|
|
|
|
# contains four classes, we'll generate an html
|
|
|
|
# file for the file itself, and four html files
|
|
|
|
# for the individual classes.
|
|
|
|
#
|
|
|
|
# [indices] we generate three indices for files, classes,
|
|
|
|
# and methods. These are displayed in a browser
|
|
|
|
# like window with three index panes across the
|
|
|
|
# top and the selected description below
|
|
|
|
#
|
|
|
|
# Method descriptions appear in whatever entity (file, class,
|
|
|
|
# or module) that contains them.
|
|
|
|
#
|
|
|
|
# We generate files in a structure below a specified subdirectory,
|
|
|
|
# normally +doc+.
|
|
|
|
#
|
|
|
|
# opdir
|
|
|
|
# |
|
|
|
|
# |___ files
|
|
|
|
# | |__ per file summaries
|
|
|
|
# |
|
|
|
|
# |___ classes
|
|
|
|
# |__ per class/module descriptions
|
|
|
|
#
|
|
|
|
# HTML is generated using the Template class.
|
|
|
|
#
|
|
|
|
|
|
|
|
require 'ftools'
|
|
|
|
|
|
|
|
require 'rdoc/options'
|
|
|
|
require 'rdoc/template'
|
|
|
|
require 'rdoc/markup/simple_markup'
|
|
|
|
require 'rdoc/markup/simple_markup/to_flow'
|
|
|
|
require 'cgi'
|
|
|
|
|
2003-12-18 16:08:25 -05:00
|
|
|
require 'rdoc/ri/ri_cache'
|
|
|
|
require 'rdoc/ri/ri_reader'
|
2003-12-16 00:44:25 -05:00
|
|
|
require 'rdoc/ri/ri_writer'
|
|
|
|
require 'rdoc/ri/ri_descriptions'
|
|
|
|
|
|
|
|
module Generators
|
|
|
|
|
|
|
|
|
|
|
|
class RIGenerator
|
|
|
|
|
|
|
|
# Generators may need to return specific subclasses depending
|
|
|
|
# on the options they are passed. Because of this
|
|
|
|
# we create them using a factory
|
|
|
|
|
|
|
|
def RIGenerator.for(options)
|
|
|
|
new(options)
|
|
|
|
end
|
|
|
|
|
|
|
|
class <<self
|
|
|
|
protected :new
|
|
|
|
end
|
|
|
|
|
|
|
|
# Set up a new HTML generator. Basically all we do here is load
|
|
|
|
# up the correct output temlate
|
|
|
|
|
|
|
|
def initialize(options) #:not-new:
|
|
|
|
@options = options
|
|
|
|
@ri_writer = RI::RiWriter.new(options.op_dir)
|
|
|
|
@markup = SM::SimpleMarkup.new
|
|
|
|
@to_flow = SM::ToFlow.new
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
##
|
|
|
|
# Build the initial indices and output objects
|
|
|
|
# based on an array of TopLevel objects containing
|
|
|
|
# the extracted information.
|
|
|
|
|
|
|
|
def generate(toplevels)
|
|
|
|
RDoc::TopLevel.all_classes_and_modules.each do |cls|
|
|
|
|
process_class(cls)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def process_class(from_class)
|
|
|
|
generate_class_info(from_class)
|
|
|
|
|
|
|
|
# now recure into this classes constituent classess
|
|
|
|
from_class.each_classmodule do |mod|
|
|
|
|
process_class(mod)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def generate_class_info(cls)
|
2003-12-21 02:28:54 -05:00
|
|
|
if cls === RDoc::NormalModule
|
2004-01-02 01:01:12 -05:00
|
|
|
cls_desc = RI::ModuleDescription.new
|
|
|
|
else
|
2003-12-21 02:28:54 -05:00
|
|
|
cls_desc = RI::ClassDescription.new
|
|
|
|
cls_desc.superclass = cls.superclass
|
|
|
|
end
|
2003-12-16 00:44:25 -05:00
|
|
|
cls_desc.name = cls.name
|
|
|
|
cls_desc.full_name = cls.full_name
|
|
|
|
cls_desc.comment = markup(cls.comment)
|
|
|
|
|
|
|
|
cls_desc.attributes =cls.attributes.sort.map do |a|
|
|
|
|
RI::Attribute.new(a.name, a.rw, markup(a.comment))
|
|
|
|
end
|
|
|
|
|
|
|
|
cls_desc.constants = cls.constants.map do |c|
|
|
|
|
RI::Constant.new(c.name, c.value, markup(c.comment))
|
|
|
|
end
|
|
|
|
|
|
|
|
cls_desc.includes = cls.includes.map do |i|
|
|
|
|
RI::IncludedModule.new(i.name)
|
|
|
|
end
|
|
|
|
|
2003-12-16 15:28:44 -05:00
|
|
|
class_methods, instance_methods = method_list(cls)
|
2003-12-16 00:44:25 -05:00
|
|
|
|
2003-12-16 15:28:44 -05:00
|
|
|
cls_desc.class_methods = class_methods.map do |m|
|
|
|
|
RI::MethodSummary.new(m.name)
|
|
|
|
end
|
|
|
|
cls_desc.instance_methods = instance_methods.map do |m|
|
2003-12-16 00:44:25 -05:00
|
|
|
RI::MethodSummary.new(m.name)
|
|
|
|
end
|
|
|
|
|
2003-12-18 16:08:25 -05:00
|
|
|
update_or_replace(cls_desc)
|
2003-12-16 00:44:25 -05:00
|
|
|
|
2003-12-16 15:28:44 -05:00
|
|
|
class_methods.each do |m|
|
|
|
|
generate_method_info(cls_desc, m)
|
|
|
|
end
|
|
|
|
|
|
|
|
instance_methods.each do |m|
|
2003-12-16 00:44:25 -05:00
|
|
|
generate_method_info(cls_desc, m)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
def generate_method_info(cls_desc, method)
|
|
|
|
meth_desc = RI::MethodDescription.new
|
|
|
|
meth_desc.name = method.name
|
|
|
|
meth_desc.full_name = cls_desc.full_name
|
|
|
|
if method.singleton
|
|
|
|
meth_desc.full_name += "::"
|
|
|
|
else
|
|
|
|
meth_desc.full_name += "#"
|
|
|
|
end
|
|
|
|
meth_desc.full_name << method.name
|
|
|
|
|
|
|
|
meth_desc.comment = markup(method.comment)
|
|
|
|
meth_desc.params = params_of(method)
|
|
|
|
meth_desc.visibility = method.visibility.to_s
|
|
|
|
meth_desc.is_singleton = method.singleton
|
|
|
|
meth_desc.block_params = method.block_params
|
|
|
|
|
|
|
|
meth_desc.aliases = method.aliases.map do |a|
|
|
|
|
RI::AliasName.new(a.name)
|
|
|
|
end
|
|
|
|
|
|
|
|
@ri_writer.add_method(cls_desc, meth_desc)
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
2003-12-16 15:28:44 -05:00
|
|
|
# return a list of class and instance methods that we'll be
|
|
|
|
# documenting
|
2003-12-16 00:44:25 -05:00
|
|
|
|
|
|
|
def method_list(cls)
|
|
|
|
list = cls.method_list
|
|
|
|
unless @options.show_all
|
|
|
|
list = list.find_all do |m|
|
* array.c: replace rb_protect_inspect() and rb_inspecting_p() by
rb_exec_recursive() in eval.c.
* eval.c (rb_exec_recursive): new function.
* array.c (rb_ary_join): use rb_exec_recursive().
* array.c (rb_ary_inspect, rb_ary_hash): ditto.
* file.c (rb_file_join): ditto.
* hash.c (rb_hash_inspect, rb_hash_to_s, rb_hash_hash): ditto.
* io.c (rb_io_puts): ditto.
* object.c (rb_obj_inspect): ditto
* struct.c (rb_struct_inspect): ditto.
* lib/set.rb (SortedSet::setup): a hack to shut up warning.
[ruby-talk:132866]
* lib/time.rb (Time::strptime): add new function. inspired by
[ruby-talk:132815].
* lib/parsedate.rb (ParseDate::strptime): ditto.
* regparse.c: move st_*_strend() functions from st.c. fixed some
potential memory leaks.
* exception error messages updated. [ruby-core:04497]
* ext/socket/socket.c (Init_socket): add bunch of Socket
constants. Patch from Sam Roberts <sroberts@uniserve.com>.
[ruby-core:04409]
* array.c (rb_ary_s_create): no need for negative argc check.
[ruby-core:04463]
* array.c (rb_ary_unshift_m): ditto.
* lib/xmlrpc/parser.rb (XMLRPC::FaultException): make it subclass
of StandardError class, not Exception class. [ruby-core:04429]
* parse.y (fcall_gen): lvar(arg) will be evaluated as
lvar.call(arg) when lvar is a defined local variable. [new]
* object.c (rb_class_initialize): call inherited method before
calling initializing block.
* eval.c (rb_thread_start_1): initialize newly pushed frame.
* lib/open3.rb (Open3::popen3): $? should not be EXIT_FAILURE.
fixed: [ruby-core:04444]
* eval.c (is_defined): NODE_IASGN is an assignment.
* ext/readline/readline.c (Readline.readline): use rl_outstream
and rl_instream. [ruby-dev:25699]
* ext/etc/etc.c (Init_etc): sGroup needs HAVE_ST_GR_PASSWD check
[ruby-dev:25675]
* misc/ruby-mode.el: [ruby-core:04415]
* lib/rdoc/generators/html_generator.rb: [ruby-core:04412]
* lib/rdoc/generators/ri_generator.rb: ditto.
* struct.c (make_struct): fixed: [ruby-core:04402]
* ext/curses/curses.c (window_color_set): [ruby-core:04393]
* ext/socket/socket.c (Init_socket): SO_REUSEPORT added.
[ruby-talk:130092]
* object.c: [ruby-doc:818]
* parse.y (open_args): fix too verbose warnings for the space
before argument parentheses. [ruby-dev:25492]
* parse.y (parser_yylex): ditto.
* parse.y (parser_yylex): the first expression in the parentheses
should not be a command. [ruby-dev:25492]
* lib/irb/context.rb (IRB::Context::initialize): [ruby-core:04330]
* object.c (Init_Object): remove Object#type. [ruby-core:04335]
* st.c (st_foreach): report success/failure by return value.
[ruby-Bugs-1396]
* parse.y: forgot to initialize parser struct. [ruby-dev:25492]
* parse.y (parser_yylex): no tLABEL on EXPR_BEG.
[ruby-talk:127711]
* document updates - [ruby-core:04296], [ruby-core:04301],
[ruby-core:04302], [ruby-core:04307]
* dir.c (rb_push_glob): should work for NUL delimited patterns.
* dir.c (rb_glob2): should aware of offset in the pattern.
* string.c (rb_str_new4): should propagate taintedness.
* env.h: rename member names in struct FRAME; last_func -> callee,
orig_func -> this_func, last_class -> this_class.
* struct.c (rb_struct_set): use original method name, not callee
name, to retrieve member slot. [ruby-core:04268]
* time.c (time_strftime): protect from format modification from GC
finalizers.
* object.c (Init_Object): remove rb_obj_id_obsolete()
* eval.c (rb_mod_define_method): incomplete subclass check.
[ruby-dev:25464]
* gc.c (rb_data_object_alloc): klass may be NULL.
[ruby-list:40498]
* bignum.c (rb_big_rand): should return positive random number.
[ruby-dev:25401]
* bignum.c (rb_big_rand): do not use rb_big_modulo to generate
random bignums. [ruby-dev:25396]
* variable.c (rb_autoload): [ruby-dev:25373]
* eval.c (svalue_to_avalue): [ruby-dev:25366]
* string.c (rb_str_justify): [ruby-dev:25367]
* io.c (rb_f_select): [ruby-dev:25312]
* ext/socket/socket.c (sock_s_getservbyport): [ruby-talk:124072]
* struct.c (make_struct): [ruby-dev:25249]
* dir.c (dir_open_dir): new function. [ruby-dev:25242]
* io.c (rb_f_open): add type check for return value from to_open.
* lib/pstore.rb (PStore#transaction): Use the empty content when a
file is not found. [ruby-dev:24561]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@8068 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2005-03-04 01:47:45 -05:00
|
|
|
m.visibility == :public || m.visibility == :protected || m.force_documentation
|
2003-12-16 00:44:25 -05:00
|
|
|
end
|
|
|
|
end
|
2003-12-16 15:28:44 -05:00
|
|
|
|
|
|
|
c = []
|
|
|
|
i = []
|
|
|
|
list.sort.each do |m|
|
|
|
|
if m.singleton
|
|
|
|
c << m
|
|
|
|
else
|
|
|
|
i << m
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return c,i
|
2003-12-16 00:44:25 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def params_of(method)
|
2003-12-23 23:24:29 -05:00
|
|
|
if method.call_seq
|
|
|
|
method.call_seq
|
2003-12-16 15:28:44 -05:00
|
|
|
else
|
2003-12-23 23:24:29 -05:00
|
|
|
params = method.params || ""
|
|
|
|
|
2003-12-16 15:28:44 -05:00
|
|
|
p = params.gsub(/\s*\#.*/, '')
|
|
|
|
p = p.tr("\n", " ").squeeze(" ")
|
|
|
|
p = "(" + p + ")" unless p[0] == ?(
|
|
|
|
|
|
|
|
if (block = method.block_params)
|
|
|
|
block.gsub!(/\s*\#.*/, '')
|
|
|
|
block = block.tr("\n", " ").squeeze(" ")
|
|
|
|
if block[0] == ?(
|
|
|
|
block.sub!(/^\(/, '').sub!(/\)/, '')
|
|
|
|
end
|
|
|
|
p << " {|#{block.strip}| ...}"
|
2003-12-16 00:44:25 -05:00
|
|
|
end
|
2003-12-23 23:24:29 -05:00
|
|
|
p
|
2003-12-16 00:44:25 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def markup(comment)
|
|
|
|
return nil if !comment || comment.empty?
|
|
|
|
|
|
|
|
# Convert leading comment markers to spaces, but only
|
|
|
|
# if all non-blank lines have them
|
|
|
|
|
|
|
|
if comment =~ /^(?>\s*)[^\#]/
|
|
|
|
content = comment
|
|
|
|
else
|
|
|
|
content = comment.gsub(/^\s*(#+)/) { $1.tr('#',' ') }
|
|
|
|
end
|
|
|
|
@markup.convert(content, @to_flow)
|
|
|
|
end
|
|
|
|
|
2003-12-18 16:08:25 -05:00
|
|
|
|
|
|
|
# By default we replace existing classes with the
|
|
|
|
# same name. If the --merge option was given, we instead
|
|
|
|
# merge this definition into an existing class. We add
|
|
|
|
# our methods, aliases, etc to that class, but do not
|
|
|
|
# change the class's description.
|
|
|
|
|
|
|
|
def update_or_replace(cls_desc)
|
|
|
|
old_cls = nil
|
|
|
|
|
|
|
|
if @options.merge
|
|
|
|
rdr = RI::RiReader.new(RI::RiCache.new(@options.op_dir))
|
|
|
|
|
|
|
|
namespace = rdr.top_level_namespace
|
|
|
|
namespace = rdr.lookup_namespace_in(cls_desc.name, namespace)
|
|
|
|
if namespace.empty?
|
2003-12-18 19:01:19 -05:00
|
|
|
$stderr.puts "You asked me to merge this source into existing "
|
|
|
|
$stderr.puts "documentation. This file references a class or "
|
|
|
|
$stderr.puts "module called #{cls_desc.name} which I don't"
|
|
|
|
$stderr.puts "have existing documentation for."
|
|
|
|
$stderr.puts
|
|
|
|
$stderr.puts "Perhaps you need to generate its documentation first"
|
|
|
|
exit 1
|
2003-12-18 16:08:25 -05:00
|
|
|
else
|
|
|
|
old_cls = namespace[0]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if old_cls.nil?
|
|
|
|
# no merge: simply overwrite
|
|
|
|
@ri_writer.remove_class(cls_desc)
|
|
|
|
@ri_writer.add_class(cls_desc)
|
|
|
|
else
|
|
|
|
# existing class: merge in
|
|
|
|
old_desc = rdr.get_class(old_cls)
|
|
|
|
|
|
|
|
old_desc.merge_in(cls_desc)
|
|
|
|
@ri_writer.add_class(old_desc)
|
|
|
|
end
|
|
|
|
end
|
2003-12-16 00:44:25 -05:00
|
|
|
end
|
|
|
|
end
|