Import RDoc 2.2.1 r185

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@19537 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
drbrain 2008-09-25 02:43:03 +00:00
parent 00b4a3f9c4
commit 858362e761
54 changed files with 2893 additions and 2268 deletions

View File

@ -1,3 +1,7 @@
Thu Sep 25 11:22:51 2008
* lib/rdoc*: Update to RDoc 2.2.1 r185.
Thu Sep 25 02:08:47 2008 Tanaka Akira <akr@fsij.org> Thu Sep 25 02:08:47 2008 Tanaka Akira <akr@fsij.org>
* io.c (rb_io_mode_enc): make it static. * io.c (rb_io_mode_enc): make it static.

3
bin/ri
View File

@ -1,6 +1,5 @@
#!/usr//bin/env ruby #!/usr/bin/env ruby
require 'rdoc/ri/driver' require 'rdoc/ri/driver'
RDoc::RI::Driver.run ARGV RDoc::RI::Driver.run ARGV

View File

@ -1,14 +1,14 @@
$DEBUG_RDOC = nil $DEBUG_RDOC = nil
## ##
# RDoc - Ruby Documentation System # = \RDoc - Ruby Documentation System
# #
# This package contains RDoc and RDoc::Markup. RDoc is an application that # This package contains RDoc and RDoc::Markup. RDoc is an application that
# produces documentation for one or more Ruby source files. We work similarly # produces documentation for one or more Ruby source files. It works similarly
# to JavaDoc, parsing the source, and extracting the definition for classes, # to JavaDoc, parsing the source, and extracting the definition for classes,
# modules, and methods (along with includes and requires). We associate with # modules, and methods (along with includes and requires). It associates with
# these optional documentation contained in the immediately preceding comment # these optional documentation contained in the immediately preceding comment
# block, and then render the result using a pluggable output formatter. # block, and then renders the result using a pluggable output formatter.
# RDoc::Markup is a library that converts plain text into various output # RDoc::Markup is a library that converts plain text into various output
# formats. The markup library is used to interpret the comment blocks that # formats. The markup library is used to interpret the comment blocks that
# RDoc uses to document methods, classes, and so on. # RDoc uses to document methods, classes, and so on.
@ -18,8 +18,6 @@ $DEBUG_RDOC = nil
# * If you want to use RDoc to create documentation for your Ruby source files, # * If you want to use RDoc to create documentation for your Ruby source files,
# read on. # read on.
# * If you want to include extensions written in C, see RDoc::Parser::C # * If you want to include extensions written in C, see RDoc::Parser::C
# * For information on the various markups available in comment blocks, see
# RDoc::Markup.
# * If you want to drive RDoc programmatically, see RDoc::RDoc. # * If you want to drive RDoc programmatically, see RDoc::RDoc.
# * If you want to use the library to format text blocks into HTML, have a look # * If you want to use the library to format text blocks into HTML, have a look
# at RDoc::Markup. # at RDoc::Markup.
@ -28,21 +26,21 @@ $DEBUG_RDOC = nil
# #
# == Summary # == Summary
# #
# Once installed, you can create documentation using the 'rdoc' command # Once installed, you can create documentation using the +rdoc+ command
# (the command is 'rdoc.bat' under Windows)
# #
# % rdoc [options] [names...] # % rdoc [options] [names...]
# #
# Type "rdoc --help" for an up-to-date option summary. # For an up-to-date option summary, type
# % rdoc --help
# #
# A typical use might be to generate documentation for a package of Ruby # A typical use might be to generate documentation for a package of Ruby
# source (such as rdoc itself). # source (such as RDoc itself).
# #
# % rdoc # % rdoc
# #
# This command generates documentation for all the Ruby and C source # This command generates documentation for all the Ruby and C source
# files in and below the current directory. These will be stored in a # files in and below the current directory. These will be stored in a
# documentation tree starting in the subdirectory 'doc'. # documentation tree starting in the subdirectory +doc+.
# #
# You can make this slightly more useful for your readers by having the # You can make this slightly more useful for your readers by having the
# index page contain the documentation for the primary file. In our # index page contain the documentation for the primary file. In our
@ -54,20 +52,46 @@ $DEBUG_RDOC = nil
# in comment blocks in the documentation this generates. # in comment blocks in the documentation this generates.
# #
# RDoc uses file extensions to determine how to process each file. File names # RDoc uses file extensions to determine how to process each file. File names
# ending +.rb+ and <tt>.rbw</tt> are assumed to be Ruby source. Files # ending +.rb+ and +.rbw+ are assumed to be Ruby source. Files
# ending +.c+ are parsed as C files. All other files are assumed to # ending +.c+ are parsed as C files. All other files are assumed to
# contain just Markup-style markup (with or without leading '#' comment # contain just Markup-style markup (with or without leading '#' comment
# markers). If directory names are passed to RDoc, they are scanned # markers). If directory names are passed to RDoc, they are scanned
# recursively for C and Ruby source files only. # recursively for C and Ruby source files only.
# #
# = Markup # == \Options
# rdoc can be passed a variety of command-line options. In addition,
# options can be specified via the +RDOCOPT+ environment variable, which
# functions similarly to the +RUBYOPT+ environment variable.
# #
# For information on how to make lists, hyperlinks, etc. with RDoc, see # % export RDOCOPT="-S"
# RDoc::Markup.
# #
# Comment blocks can be written fairly naturally, either using '#' on # will make rdoc default to inline method source code. Command-line options
# always will override those in +RDOCOPT+.
#
# Run
#
# % rdoc --help
#
# for full details on rdoc's options.
#
# Here are some of the most commonly used options.
# [-d, --diagram]
# Generate diagrams showing modules and
# classes. You need dot V1.8.6 or later to
# use the --diagram option correctly. Dot is
# available from http://graphviz.org
#
# [-S, --inline-source]
# Show method source code inline, rather than via a popup link.
#
# [-T, --template=NAME]
# Set the template used when generating output.
#
# == Documenting Source Code
#
# Comment blocks can be written fairly naturally, either using +#+ on
# successive lines of the comment, or by including the comment in # successive lines of the comment, or by including the comment in
# an =begin/=end block. If you use the latter form, the =begin line must be # a =begin/=end block. If you use the latter form, the =begin line must be
# flagged with an RDoc tag: # flagged with an RDoc tag:
# #
# =begin rdoc # =begin rdoc
@ -93,7 +117,7 @@ $DEBUG_RDOC = nil
# # ... # # ...
# end # end
# #
# Names of classes, source files, and any method names containing an # Names of classes, files, and any method names containing an
# underscore or preceded by a hash character are automatically hyperlinked # underscore or preceded by a hash character are automatically hyperlinked
# from comment text to their description. # from comment text to their description.
# #
@ -124,15 +148,109 @@ $DEBUG_RDOC = nil
# +:yields:+ is an example of a documentation directive. These appear # +:yields:+ is an example of a documentation directive. These appear
# immediately after the start of the document element they are modifying. # immediately after the start of the document element they are modifying.
# #
# == \Markup
#
# * The markup engine looks for a document's natural left margin. This is
# used as the initial margin for the document.
#
# * Consecutive lines starting at this margin are considered to be a
# paragraph.
#
# * If a paragraph starts with a "*", "-", or with "<digit>.", then it is
# taken to be the start of a list. The margin in increased to be the first
# non-space following the list start flag. Subsequent lines should be
# indented to this new margin until the list ends. For example:
#
# * this is a list with three paragraphs in
# the first item. This is the first paragraph.
#
# And this is the second paragraph.
#
# 1. This is an indented, numbered list.
# 2. This is the second item in that list
#
# This is the third conventional paragraph in the
# first list item.
#
# * This is the second item in the original list
#
# * You can also construct labeled lists, sometimes called description
# or definition lists. Do this by putting the label in square brackets
# and indenting the list body:
#
# [cat] a small furry mammal
# that seems to sleep a lot
#
# [ant] a little insect that is known
# to enjoy picnics
#
# A minor variation on labeled lists uses two colons to separate the
# label from the list body:
#
# cat:: a small furry mammal
# that seems to sleep a lot
#
# ant:: a little insect that is known
# to enjoy picnics
#
# This latter style guarantees that the list bodies' left margins are
# aligned: think of them as a two column table.
#
# * Any line that starts to the right of the current margin is treated
# as verbatim text. This is useful for code listings. The example of a
# list above is also verbatim text.
#
# * A line starting with an equals sign (=) is treated as a
# heading. Level one headings have one equals sign, level two headings
# have two,and so on.
#
# * A line starting with three or more hyphens (at the current indent)
# generates a horizontal rule. The more hyphens, the thicker the rule
# (within reason, and if supported by the output device)
#
# * You can use markup within text (except verbatim) to change the
# appearance of parts of that text. Out of the box, RDoc::Markup
# supports word-based and general markup.
#
# Word-based markup uses flag characters around individual words:
#
# [\*word*] displays word in a *bold* font
# [\_word_] displays word in an _emphasized_ font
# [\+word+] displays word in a +code+ font
#
# General markup affects text between a start delimiter and and end
# delimiter. Not surprisingly, these delimiters look like HTML markup.
#
# [\<b>text...</b>] displays word in a *bold* font
# [\<em>text...</em>] displays word in an _emphasized_ font
# [\\<i>text...</i>] displays word in an <i>italicized</i> font
# [\<tt>text...</tt>] displays word in a +code+ font
#
# Unlike conventional Wiki markup, general markup can cross line
# boundaries. You can turn off the interpretation of markup by
# preceding the first character with a backslash. This only works for
# simple markup, not HTML-style markup.
#
# * Hyperlinks to the web starting http:, mailto:, ftp:, or www. are
# recognized. An HTTP url that references an external image file is
# converted into an inline <IMG..>. Hyperlinks starting 'link:' are
# assumed to refer to local files whose path is relative to the --op
# directory.
#
# Hyperlinks can also be of the form <tt>label</tt>[url], in which
# case the label is used in the displayed text, and +url+ is
# used as the target. If +label+ contains multiple words,
# put it in braces: <em>{multi word label}[</em>url<em>]</em>.
#
# == Directives # == Directives
# #
# [+:nodoc:+ / +:nodoc:+ all] # [+:nodoc:+ / +:nodoc:+ all]
# Don't include this element in the documentation. For classes # This directive prevents documentation for the element from
# and modules, the methods, aliases, constants, and attributes # being generated. For classes and modules, the methods, aliases,
# directly within the affected class or module will also be # constants, and attributes directly within the affected class or
# omitted. By default, though, modules and classes within that # module also will be omitted. By default, though, modules and
# class of module _will_ be documented. This is turned off by # classes within that class of module _will_ be documented. This is
# adding the +all+ modifier. # turned off by adding the +all+ modifier.
# #
# module MyModule # :nodoc: # module MyModule # :nodoc:
# class Input # class Input
@ -144,22 +262,22 @@ $DEBUG_RDOC = nil
# end # end
# end # end
# #
# In the above code, only class +MyModule::Input+ will be documented.The # In the above code, only class <tt>MyModule::Input</tt> will be documented.
# The :nodoc: directive is global across all files the class or module # The +:nodoc:+ directive is global across all files for the class or module
# appears in, so use :stopdoc:/:startdoc: to only omit documentation for a # to which it applies, so use +:stopdoc:+/+:startdoc:+ to suppress
# particular set of methods, etc. # documentation only for a particular set of methods, etc.
# #
# [+:doc:+] # [+:doc:+]
# Force a method or attribute to be documented even if it wouldn't otherwise # Forces a method or attribute to be documented even if it wouldn't be
# be. Useful if, for example, you want to include documentation of a # otherwise. Useful if, for example, you want to include documentation of a
# particular private method. # particular private method.
# #
# [+:notnew:+] # [+:notnew:+]
# Only applicable to the +initialize+ instance method. Normally RDoc # Only applicable to the +initialize+ instance method. Normally RDoc
# assumes that the documentation and parameters for #initialize are # assumes that the documentation and parameters for +initialize+ are
# actually for the ::new method, and so fakes out a ::new for the class. # actually for the +new+ method, and so fakes out a +new+ for the class.
# The :notnew: modifier stops this. Remember that #initialize is protected, # The +:notnew:+ modifier stops this. Remember that +initialize+ is private,
# so you won't see the documentation unless you use the -a command line # so you won't see the documentation unless you use the +-a+ command line
# option. # option.
# #
# Comment blocks can contain other directives: # Comment blocks can contain other directives:
@ -209,7 +327,7 @@ $DEBUG_RDOC = nil
# last. If you don't specify a +:startdoc:+ by the end of the container, # last. If you don't specify a +:startdoc:+ by the end of the container,
# disables documentation for the entire class or module. # disables documentation for the entire class or module.
# #
# = Other stuff # == Other stuff
# #
# RDoc is currently being maintained by Eric Hodel <drbrain@segment7.net> # RDoc is currently being maintained by Eric Hodel <drbrain@segment7.net>
# #
@ -254,7 +372,7 @@ module RDoc
## ##
# RDoc version you are using # RDoc version you are using
VERSION = "2.1.0" VERSION = "2.2.1"
## ##
# Name of the dotfile that contains the description of files to be processed # Name of the dotfile that contains the description of files to be processed

View File

@ -219,6 +219,22 @@ module RDoc
@modules.values @modules.values
end end
##
# return the classes Hash (only to be used internally)
def classes_hash
@classes
end
protected :classes_hash
##
# return the modules Hash (only to be used internally)
def modules_hash
@modules
end
protected :modules_hash
## ##
# Change the default visibility for new methods # Change the default visibility for new methods
@ -272,7 +288,24 @@ module RDoc
end end
def add_class(class_type, name, superclass) def add_class(class_type, name, superclass)
add_class_or_module @classes, class_type, name, superclass klass = add_class_or_module @classes, class_type, name, superclass
#
# If the parser encounters Container::Item before encountering
# Container, then it assumes that Container is a module. This
# may not be the case, so remove Container from the module list
# if present and transfer any contained classes and modules to
# the new class.
#
mod = @modules.delete(name)
if mod then
klass.classes_hash.update(mod.classes_hash)
klass.modules_hash.update(mod.modules_hash)
klass.method_list.concat(mod.method_list)
end
return klass
end end
def add_module(class_type, name) def add_module(class_type, name)
@ -282,25 +315,41 @@ module RDoc
def add_method(a_method) def add_method(a_method)
a_method.visibility = @visibility a_method.visibility = @visibility
add_to(@method_list, a_method) add_to(@method_list, a_method)
unmatched_alias_list = @unmatched_alias_lists[a_method.name]
if unmatched_alias_list then
unmatched_alias_list.each do |unmatched_alias|
add_alias_impl unmatched_alias, a_method
@aliases.delete unmatched_alias
end
@unmatched_alias_lists.delete a_method.name
end
end end
def add_attribute(an_attribute) def add_attribute(an_attribute)
add_to(@attributes, an_attribute) add_to(@attributes, an_attribute)
end end
def add_alias_impl(an_alias, meth)
new_meth = AnyMethod.new(an_alias.text, an_alias.new_name)
new_meth.is_alias_for = meth
new_meth.singleton = meth.singleton
new_meth.params = meth.params
new_meth.comment = "Alias for \##{meth.name}"
meth.add_alias(new_meth)
add_method(new_meth)
end
def add_alias(an_alias) def add_alias(an_alias)
meth = find_instance_method_named(an_alias.old_name) meth = find_instance_method_named(an_alias.old_name)
if meth then if meth then
new_meth = AnyMethod.new(an_alias.text, an_alias.new_name) add_alias_impl(an_alias, meth)
new_meth.is_alias_for = meth
new_meth.singleton = meth.singleton
new_meth.params = meth.params
new_meth.comment = "Alias for \##{meth.name}"
meth.add_alias(new_meth)
add_method(new_meth)
else else
add_to(@aliases, an_alias) add_to(@aliases, an_alias)
unmatched_alias_list = @unmatched_alias_lists[an_alias.old_name] ||= []
unmatched_alias_list.push(an_alias)
end end
an_alias an_alias
@ -360,6 +409,10 @@ module RDoc
@requires = [] @requires = []
@includes = [] @includes = []
@constants = [] @constants = []
# This Hash maps a method name to a list of unmatched
# aliases (aliases of a method not yet encountered).
@unmatched_alias_lists = {}
end end
# and remove classes and modules when we see a :nodoc: all # and remove classes and modules when we see a :nodoc: all
@ -374,9 +427,12 @@ module RDoc
# Find a named module # Find a named module
def find_module_named(name) def find_module_named(name)
return self if self.name == name # First check the enclosed modules, then check the module itself,
# then check the enclosing modules (this mirrors the check done by
# the Ruby parser)
res = @modules[name] || @classes[name] res = @modules[name] || @classes[name]
return res if res return res if res
return self if self.name == name
find_enclosing_module_named(name) find_enclosing_module_named(name)
end end
@ -435,6 +491,7 @@ module RDoc
unless modules.empty? then unless modules.empty? then
module_name = modules.shift module_name = modules.shift
result = find_module_named(module_name) result = find_module_named(module_name)
if result then if result then
modules.each do |name| modules.each do |name|
result = result.find_module_named(name) result = result.find_module_named(name)
@ -573,9 +630,18 @@ module RDoc
cls = all[name] cls = all[name]
unless cls then if !cls then
cls = class_type.new name, superclass cls = class_type.new name, superclass
all[name] = cls unless @done_documenting all[name] = cls unless @done_documenting
else
# If the class has been encountered already, check that its
# superclass has been set (it may not have been, depending on
# the context in which it was encountered).
if class_type == NormalClass
if !cls.superclass then
cls.superclass = superclass
end
end
end end
collection[name] = cls unless @done_documenting collection[name] = cls unless @done_documenting

View File

@ -311,28 +311,30 @@ module RDoc
# inclusion on the page # inclusion on the page
def wrap_in_image_map(src, dot) def wrap_in_image_map(src, dot)
res = %{<map id="map" name="map">\n} res = ""
dot_map = `dot -Tismap #{src}` dot_map = `dot -Tismap #{src}`
dot_map.split($/).each do |area|
unless area =~ /^rectangle \((\d+),(\d+)\) \((\d+),(\d+)\) ([\/\w.]+)\s*(.*)/ if(!dot_map.empty?)
$stderr.puts "Unexpected output from dot:\n#{area}" res << %{<map id="map" name="map">\n}
return nil dot_map.split($/).each do |area|
unless area =~ /^rectangle \((\d+),(\d+)\) \((\d+),(\d+)\) ([\/\w.]+)\s*(.*)/
$stderr.puts "Unexpected output from dot:\n#{area}"
return nil
end
xs, ys = [$1.to_i, $3.to_i], [$2.to_i, $4.to_i]
url, area_name = $5, $6
res << %{ <area shape="rect" coords="#{xs.min},#{ys.min},#{xs.max},#{ys.max}" }
res << %{ href="#{url}" alt="#{area_name}" />\n}
end end
res << "</map>\n"
xs, ys = [$1.to_i, $3.to_i], [$2.to_i, $4.to_i]
url, area_name = $5, $6
res << %{ <area shape="rect" coords="#{xs.min},#{ys.min},#{xs.max},#{ys.max}" }
res << %{ href="#{url}" alt="#{area_name}" />\n}
end end
res << "</map>\n"
# map_file = src.sub(/.dot/, '.map') res << %{<img src="#{dot}" usemap="#map" alt="#{dot}" />}
# system("dot -Timap #{src} -o #{map_file}")
res << %{<img src="#{dot}" usemap="#map" border="0" alt="#{dot}">}
return res return res
end end
end end
end end

View File

@ -127,7 +127,7 @@ module RDoc::Generator
# * a complete list of all hyperlinkable terms (file, class, module, and # * a complete list of all hyperlinkable terms (file, class, module, and
# method names) # method names)
def self.build_indicies(toplevels, options) def self.build_indices(toplevels, options)
files = [] files = []
classes = [] classes = []
@ -215,7 +215,7 @@ module RDoc::Generator
@methods.sort.map do |meth| @methods.sort.map do |meth|
{ {
"name" => CGI.escapeHTML(meth.name), "name" => CGI.escapeHTML(meth.name),
"aref" => "#{path_prefix}\##{meth.aref}" "aref" => "##{meth.aref}"
} }
end end
end end
@ -614,9 +614,9 @@ module RDoc::Generator
def class_attribute_values def class_attribute_values
h_name = CGI.escapeHTML(name) h_name = CGI.escapeHTML(name)
@values["path"] = @path @values["href"] = @path
@values["classmod"] = @is_module ? "Module" : "Class" @values["classmod"] = @is_module ? "Module" : "Class"
@values["title"] = "#{@values['classmod']}: #{h_name}" @values["title"] = "#{@values['classmod']}: #{h_name} [#{@options.title}]"
c = @context c = @context
c = c.parent while c and not c.diagram c = c.parent while c and not c.diagram
@ -704,7 +704,7 @@ module RDoc::Generator
def filename_to_label def filename_to_label
@context.file_relative_name.gsub(/%|\/|\?|\#/) do @context.file_relative_name.gsub(/%|\/|\?|\#/) do
'%%%x' % $&[0].unpack('C') ('%%%x' % $&[0]).unpack('C')
end end
end end
@ -791,7 +791,7 @@ module RDoc::Generator
full_path = @context.file_absolute_name full_path = @context.file_absolute_name
short_name = ::File.basename full_path short_name = ::File.basename full_path
@values["title"] = CGI.escapeHTML("File: #{short_name}") @values["title"] = CGI.escapeHTML("File: #{short_name} [#{@options.title}]")
if @context.diagram then if @context.diagram then
@values["diagram"] = diagram_reference(@context.diagram) @values["diagram"] = diagram_reference(@context.diagram)
@ -821,18 +821,18 @@ module RDoc::Generator
attr_reader :img_url attr_reader :img_url
attr_reader :source_code attr_reader :source_code
@@seq = "M000000"
@@all_methods = []
def self.all_methods def self.all_methods
@@all_methods @@all_methods
end end
def self.reset def self.reset
@@all_methods = [] @@all_methods = []
@@seq = "M000000"
end end
# Initialize the class variables.
self.reset
def initialize(context, html_class, options) def initialize(context, html_class, options)
# TODO: rethink the class hierarchy here... # TODO: rethink the class hierarchy here...
@context = context @context = context
@ -1043,12 +1043,18 @@ module RDoc::Generator
first = $1.to_i - 1 first = $1.to_i - 1
last = first + src.count("\n") last = first + src.count("\n")
size = last.to_s.length size = last.to_s.length
real_fmt = "%#{size}d: " fmt = "%#{size}d: "
fmt = " " * (size+2) is_first_line = true
line_num = first
src.gsub!(/^/) do src.gsub!(/^/) do
res = sprintf(fmt, first) if is_first_line then
first += 1 is_first_line = false
fmt = real_fmt res = " " * (size+2)
else
res = sprintf(fmt, line_num)
end
line_num += 1
res res
end end
end end

View File

@ -6,6 +6,8 @@ module RDoc::Generator::CHM::CHM
HTML = RDoc::Generator::HTML::HTML HTML = RDoc::Generator::HTML::HTML
INDEX = HTML::INDEX INDEX = HTML::INDEX
STYLE = HTML::STYLE
CLASS_INDEX = HTML::CLASS_INDEX CLASS_INDEX = HTML::CLASS_INDEX
CLASS_PAGE = HTML::CLASS_PAGE CLASS_PAGE = HTML::CLASS_PAGE

View File

@ -68,7 +68,6 @@ class RDoc::Generator::HTML
def initialize(options) #:not-new: def initialize(options) #:not-new:
@options = options @options = options
load_html_template load_html_template
@main_page_path = nil
end end
## ##
@ -94,6 +93,15 @@ class RDoc::Generator::HTML
# If the template name contains a slash, use it literally # If the template name contains a slash, use it literally
def load_html_template def load_html_template
#
# If the template is not a path, first look for it
# in rdoc's HTML template directory. Perhaps this behavior should
# be reversed (first try to include the template and, only if that
# fails, try to include it in the default template directory).
# One danger with reversing the behavior, however, is that
# if something like require 'html' could load up an
# unrelated file in the standard library or in a gem.
#
template = @options.template template = @options.template
unless template =~ %r{/|\\} then unless template =~ %r{/|\\} then
@ -101,14 +109,25 @@ class RDoc::Generator::HTML
template) template)
end end
require template begin
require template
@template = self.class.const_get @options.template.upcase @template = self.class.const_get @options.template.upcase
@options.template_class = @template @options.template_class = @template
rescue LoadError => e
#
# The template did not exist in the default template directory, so
# see if require can find the template elsewhere (in a gem, for
# instance).
#
if(e.message[template] && template != @options.template)
template = @options.template
retry
end
rescue LoadError $stderr.puts "Could not find HTML template '#{template}': #{e.message}"
$stderr.puts "Could not find HTML template '#{template}'" exit 99
exit 99 end
end end
## ##
@ -146,14 +165,16 @@ class RDoc::Generator::HTML
end end
def build_indices def build_indices
@files, @classes = RDoc::Generator::Context.build_indicies(@toplevels, @files, @classes = RDoc::Generator::Context.build_indices(@toplevels,
@options) @options)
end end
## ##
# Generate all the HTML # Generate all the HTML
def generate_html def generate_html
@main_url = main_url
# the individual descriptions for files and classes # the individual descriptions for files and classes
gen_into(@files) gen_into(@files)
gen_into(@classes) gen_into(@classes)
@ -165,23 +186,50 @@ class RDoc::Generator::HTML
gen_main_index gen_main_index
# this method is defined in the template file # this method is defined in the template file
write_extra_pages if defined? write_extra_pages values = {
'title_suffix' => CGI.escapeHTML("[#{@options.title}]"),
'charset' => @options.charset,
'style_url' => style_url('', @options.css),
}
@template.write_extra_pages(values) if @template.respond_to?(:write_extra_pages)
end end
def gen_into(list) def gen_into(list)
@file_list ||= index_to_links @files #
@class_list ||= index_to_links @classes # The file, class, and method lists technically should be regenerated
@method_list ||= index_to_links RDoc::Generator::Method.all_methods # for every output file, in order that the relative links be correct
# (we are worried here about frameless templates, which need this
# information for every generated page). Doing this is a bit slow,
# however. For a medium-sized gem, this increased rdoc's runtime by
# about 5% (using the 'time' command-line utility). While this is not
# necessarily a problem, I do not want to pessimize rdoc for large
# projects, however, and so we only regenerate the lists when the
# directory of the output file changes, which seems like a reasonable
# optimization.
#
file_list = {}
class_list = {}
method_list = {}
prev_op_dir = nil
list.each do |item| list.each do |item|
next unless item.document_self next unless item.document_self
op_file = item.path op_file = item.path
op_dir = File.dirname(op_file)
FileUtils.mkdir_p File.dirname(op_file) if(op_dir != prev_op_dir)
file_list = index_to_links op_file, @files
class_list = index_to_links op_file, @classes
method_list = index_to_links op_file, RDoc::Generator::Method.all_methods
end
prev_op_dir = op_dir
FileUtils.mkdir_p op_dir
open op_file, 'w' do |io| open op_file, 'w' do |io|
item.write_on io, @file_list, @class_list, @method_list item.write_on io, file_list, class_list, method_list
end end
end end
end end
@ -211,8 +259,9 @@ class RDoc::Generator::HTML
values = { values = {
"entries" => res, "entries" => res,
'title' => CGI.escapeHTML("#{title} [#{@options.title}]"),
'list_title' => CGI.escapeHTML(title), 'list_title' => CGI.escapeHTML(title),
'index_url' => main_url, 'index_url' => @main_url,
'charset' => @options.charset, 'charset' => @options.charset,
'style_url' => style_url('', @options.css), 'style_url' => style_url('', @options.css),
} }
@ -230,47 +279,55 @@ class RDoc::Generator::HTML
def gen_main_index def gen_main_index
if @template.const_defined? :FRAMELESS then if @template.const_defined? :FRAMELESS then
main = @files.find do |file| #
@main_page == file.name # If we're using a template without frames, then just redirect
end # to it from index.html.
#
if main.nil? then # One alternative to this, expanding the main page's template into
main = @classes.find do |klass| # index.html, is tricky because the relative URLs will be different
main_page == klass.context.full_name # (since index.html is located in at the site's root,
end # rather than within a files or a classes subdirectory).
#
open 'index.html', 'w' do |f|
f.puts(%{<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">})
f.puts(%{<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
lang="en">})
f.puts(%{<head>})
f.puts(%{<title>#{CGI.escapeHTML(@options.title)}</title>})
f.puts(%{<meta http-equiv="refresh" content="0; url=#{@main_url}" />})
f.puts(%{</head>})
f.puts(%{<body></body>})
f.puts(%{</html>})
end end
else else
main = RDoc::TemplatePage.new @template::INDEX main = RDoc::TemplatePage.new @template::INDEX
end
open 'index.html', 'w' do |f| open 'index.html', 'w' do |f|
style_url = style_url '', @options.css style_url = style_url '', @options.css
classes = @classes.sort.map { |klass| klass.value_hash }
values = {
'initial_page' => @main_url,
'style_url' => style_url('', @options.css),
'title' => CGI.escapeHTML(@options.title),
'charset' => @options.charset,
'classes' => classes,
}
values['inline_source'] = @options.inline_source
classes = @classes.sort.map { |klass| klass.value_hash }
values = {
'main_page' => @main_page,
'initial_page' => main_url,
'style_url' => style_url('', @options.css),
'title' => CGI.escapeHTML(@options.title),
'charset' => @options.charset,
'classes' => classes,
}
values['inline_source'] = @options.inline_source
if main.respond_to? :write_on then
main.write_on f, @file_list, @class_list, @method_list, values
else
main.write_html_on f, values main.write_html_on f, values
end end
end end
end end
def index_to_links(collection) def index_to_links(output_path, collection)
collection.sort.map do |f| collection.sort.map do |f|
next unless f.document_self next unless f.document_self
{ "href" => f.path, "name" => f.index_name } { "href" => RDoc::Markup::ToHtml.gen_relative_url(output_path, f.path),
"name" => f.index_name }
end.compact end.compact
end end
@ -278,32 +335,48 @@ class RDoc::Generator::HTML
# Returns the url of the main page # Returns the url of the main page
def main_url def main_url
@main_page = @options.main_page main_page = @options.main_page
@main_page_ref = nil
if @main_page then #
@main_page_ref = RDoc::Generator::AllReferences[@main_page] # If a main page has been specified (--main), then search for it
# in the AllReferences array. This allows either files or classes
# to be used for the main page.
#
if main_page then
main_page_ref = RDoc::Generator::AllReferences[main_page]
if @main_page_ref then if main_page_ref then
@main_page_path = @main_page_ref.path return main_page_ref.path
else else
$stderr.puts "Could not find main page #{@main_page}" $stderr.puts "Could not find main page #{main_page}"
end end
end end
unless @main_page_path then #
file = @files.find { |context| context.document_self } # No main page has been specified, so just use the README.
@main_page_path = file.path if file #
@files.each do |file|
if file.name =~ /^README/ then
return file.path
end
end end
unless @main_page_path then #
$stderr.puts "Couldn't find anything to document" # There's no README (shame! shame!). Just use the first file
$stderr.puts "Perhaps you've used :stopdoc: in all classes" # that will be documented.
exit 1 #
@files.each do |file|
if file.document_self then
return file.path
end
end end
@main_page_path #
# There are no files to be documented... Something seems very wrong.
#
raise RDoc::Error, "Couldn't find anything to document (perhaps :stopdoc: has been used in all classes)!"
end end
private :main_url
end end
@ -349,12 +422,9 @@ class RDoc::Generator::HTMLInOne < RDoc::Generator::HTML
'charset' => @options.charset, 'charset' => @options.charset,
'files' => gen_into(@files), 'files' => gen_into(@files),
'classes' => gen_into(@classes), 'classes' => gen_into(@classes),
'title' => CGI.escapeHTML(@options.title), 'title' => CGI.escapeHTML(@options.title),
} }
# this method is defined in the template file
write_extra_pages if defined? write_extra_pages
template = RDoc::TemplatePage.new @template::ONE_PAGE template = RDoc::TemplatePage.new @template::ONE_PAGE
if @options.op_name if @options.op_name
@ -372,26 +442,4 @@ class RDoc::Generator::HTMLInOne < RDoc::Generator::HTML
end end
res res
end end
def gen_file_index
gen_an_index(@files, 'Files')
end
def gen_class_index
gen_an_index(@classes, 'Classes')
end
def gen_method_index
gen_an_index(RDoc::Generator::Method.all_methods, 'Methods')
end
def gen_an_index(collection, title)
return {
"entries" => index_to_links(collection),
'list_title' => title,
'index_url' => main_url,
}
end
end end

View File

@ -0,0 +1,24 @@
#
# The templates require further refactoring. In particular,
# * Some kind of HTML generation library should be used.
#
# Also, all of the templates require some TLC from a designer.
#
# Right now, this file contains some constants that are used by all
# of the templates.
#
module RDoc::Generator::HTML::Common
XHTML_STRICT_PREAMBLE = <<-EOF
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
EOF
XHTML_FRAME_PREAMBLE = <<-EOF
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
EOF
HTML_ELEMENT = <<-EOF
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
EOF
end

View File

@ -1,15 +1,16 @@
require 'rdoc/generator/html' require 'rdoc/generator/html/html'
require 'rdoc/generator/html/one_page_html'
## ##
# = CSS2 RDoc HTML template # = CSS2 RDoc HTML template
# #
# This is a template for RDoc that uses XHTML 1.0 Transitional and dictates a # This is a template for RDoc that uses XHTML 1.0 Strict and dictates a
# bit more of the appearance of the output to cascading stylesheets than the # bit more of the appearance of the output to cascading stylesheets than the
# default. It was designed for clean inline code display, and uses DHTMl to # default. It was designed for clean inline code display, and uses DHTMl to
# toggle the visbility of each method's source with each click on the '[source]' # toggle the visbility of each method's source with each click on the '[source]'
# link. # link.
# #
# Frameless basically is the html template without frames.
#
# == Authors # == Authors
# #
# * Michael Granger <ged@FaerieMUD.org> # * Michael Granger <ged@FaerieMUD.org>
@ -25,692 +26,47 @@ module RDoc::Generator::HTML::FRAMELESS
FRAMELESS = true FRAMELESS = true
FONTS = "Verdana,Arial,Helvetica,sans-serif" FONTS = RDoc::Generator::HTML::HTML::FONTS
STYLE = <<-EOF STYLE = RDoc::Generator::HTML::HTML::STYLE
body {
font-family: #{FONTS};
font-size: 90%;
margin: 0;
margin-left: 40px;
padding: 0;
background: white;
}
h1, h2, h3, h4 { HEADER = RDoc::Generator::HTML::HTML::HEADER
margin: 0;
color: #efefef;
background: transparent;
}
h1 {
font-size: 150%;
}
h2,h3,h4 {
margin-top: 1em;
}
:link, :visited {
background: #eef;
color: #039;
text-decoration: none;
}
:link:hover, :visited:hover {
background: #039;
color: #eef;
}
/* Override the base stylesheet's Anchor inside a table cell */
td > :link, td > :visited {
background: transparent;
color: #039;
text-decoration: none;
}
/* and inside a section title */
.section-title > :link, .section-title > :visited {
background: transparent;
color: #eee;
text-decoration: none;
}
/* === Structural elements =================================== */
.index {
margin: 0;
margin-left: -40px;
padding: 0;
font-size: 90%;
}
.index :link, .index :visited {
margin-left: 0.7em;
}
.index .section-bar {
margin-left: 0px;
padding-left: 0.7em;
background: #ccc;
font-size: small;
}
#classHeader, #fileHeader {
width: auto;
color: white;
padding: 0.5em 1.5em 0.5em 1.5em;
margin: 0;
margin-left: -40px;
border-bottom: 3px solid #006;
}
#classHeader :link, #fileHeader :link,
#classHeader :visited, #fileHeader :visited {
background: inherit;
color: white;
}
#classHeader td, #fileHeader td {
background: inherit;
color: white;
}
#fileHeader {
background: #057;
}
#classHeader {
background: #048;
}
.class-name-in-header {
font-size: 180%;
font-weight: bold;
}
#bodyContent {
padding: 0 1.5em 0 1.5em;
}
#description {
padding: 0.5em 1.5em;
background: #efefef;
border: 1px dotted #999;
}
#description h1, #description h2, #description h3,
#description h4, #description h5, #description h6 {
color: #125;
background: transparent;
}
#copyright {
color: #333;
background: #efefef;
font: 0.75em sans-serif;
margin-top: 5em;
margin-bottom: 0;
padding: 0.5em 2em;
}
/* === Classes =================================== */
table.header-table {
color: white;
font-size: small;
}
.type-note {
font-size: small;
color: #dedede;
}
.xxsection-bar {
background: #eee;
color: #333;
padding: 3px;
}
.section-bar {
color: #333;
border-bottom: 1px solid #999;
margin-left: -20px;
}
.section-title {
background: #79a;
color: #eee;
padding: 3px;
margin-top: 2em;
margin-left: -30px;
border: 1px solid #999;
}
.top-aligned-row {
vertical-align: top
}
.bottom-aligned-row {
vertical-align: bottom
}
/* --- Context section classes ----------------------- */
.context-row { }
.context-item-name {
font-family: monospace;
font-weight: bold;
color: black;
}
.context-item-value {
font-size: small;
color: #448;
}
.context-item-desc {
color: #333;
padding-left: 2em;
}
/* --- Method classes -------------------------- */
.method-detail {
background: #efefef;
padding: 0;
margin-top: 0.5em;
margin-bottom: 1em;
border: 1px dotted #ccc;
}
.method-heading {
color: black;
background: #ccc;
border-bottom: 1px solid #666;
padding: 0.2em 0.5em 0 0.5em;
}
.method-signature {
color: black;
background: inherit;
}
.method-name {
font-weight: bold;
}
.method-args {
font-style: italic;
}
.method-description {
padding: 0 0.5em 0 0.5em;
}
/* --- Source code sections -------------------- */
:link.source-toggle, :visited.source-toggle {
font-size: 90%;
}
div.method-source-code {
background: #262626;
color: #ffdead;
margin: 1em;
padding: 0.5em;
border: 1px dashed #999;
overflow: hidden;
}
div.method-source-code pre {
color: #ffdead;
overflow: hidden;
}
/* --- Ruby keyword styles --------------------- */
.standalone-code {
background: #221111;
color: #ffdead;
overflow: hidden;
}
.ruby-constant {
color: #7fffd4;
background: transparent;
}
.ruby-keyword {
color: #00ffff;
background: transparent;
}
.ruby-ivar {
color: #eedd82;
background: transparent;
}
.ruby-operator {
color: #00ffee;
background: transparent;
}
.ruby-identifier {
color: #ffdead;
background: transparent;
}
.ruby-node {
color: #ffa07a;
background: transparent;
}
.ruby-comment {
color: #b22222;
font-weight: bold;
background: transparent;
}
.ruby-regexp {
color: #ffa07a;
background: transparent;
}
.ruby-value {
color: #7fffd4;
background: transparent;
}
EOF
##
# Header template
XHTML_PREAMBLE = <<-EOF
<?xml version="1.0" encoding="<%= values["charset"] %>"?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
EOF
HEADER = XHTML_PREAMBLE + <<-EOF
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title><%= values["title"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
<meta http-equiv="Content-Script-Type" content="text/javascript" />
<link rel="stylesheet" href="<%= values["style_url"] %>" type="text/css" media="screen" />
<script type="text/javascript">
// <![CDATA[
function popupCode( url ) {
window.open(url, "Code", "resizable=yes,scrollbars=yes,toolbar=no,status=no,height=150,width=400")
}
function toggleCode( id ) {
if ( document.getElementById )
elem = document.getElementById( id );
else if ( document.all )
elem = eval( "document.all." + id );
else
return false;
elemStyle = elem.style;
if ( elemStyle.display != "block" ) {
elemStyle.display = "block"
} else {
elemStyle.display = "none"
}
return true;
}
// Make codeblocks hidden by default
document.writeln( "<style type=\\"text/css\\">div.method-source-code { display: none }</style>" )
// ]]>
</script>
</head>
<body>
EOF
##
# Context content template
CONTEXT_CONTENT = %{
}
##
# Footer template
FOOTER = <<-EOF FOOTER = <<-EOF
<div id="popupmenu" class="index"> <div id="popupmenu" class="index">
<ul> <br />
<li class="index-entries section-bar">Classes <h1 class="index-entries section-bar">Files</h1>
<ul>
<% values["class_list"].each do |klass| %>
<li><a href="<%= klass["href"] %>"><%= klass["name"] %></a>
<% end %>
</ul>
</li>
<li class="index-entries section-bar">Methods
<ul>
<% values["method_list"].each do |file| %>
<li><a href="<%= file["href"] %>"><%= file["name"] %></a>
<% end %>
</ul>
</li>
<li class="index-entries section-bar">Files
<ul> <ul>
<% values["file_list"].each do |file| %> <% values["file_list"].each do |file| %>
<li><a href="<%= file["href"] %>"><%= file["name"] %></a> <li><a href="<%= file["href"] %>"><%= file["name"] %></a></li>
<% end %> <% end %>
</ul> </ul>
</li>
</ul>
</li>
<br />
<h1 class="index-entries section-bar">Classes</h1>
<ul>
<% values["class_list"].each do |klass| %>
<li><a href="<%= klass["href"] %>"><%= klass["name"] %></a></li>
<% end %>
</ul>
<br />
<h1 class="index-entries section-bar">Methods</h1>
<ul>
<% values["method_list"].each do |method| %>
<li><a href="<%= method["href"] %>"><%= method["name"] %></a></li>
<% end %>
</ul>
</div>
</body> </body>
</html> </html>
EOF EOF
## FILE_PAGE = RDoc::Generator::HTML::HTML::FILE_PAGE
# File page header template
FILE_PAGE = <<-EOF CLASS_PAGE = RDoc::Generator::HTML::HTML::CLASS_PAGE
<div id="fileHeader">
<h1><%= values["short_name"] %></h1>
<table class="header-table"> METHOD_LIST = RDoc::Generator::HTML::HTML::METHOD_LIST
<tr class="top-aligned-row">
<td><strong>Path:</strong></td>
<td><%= values["full_path"] %>
<% if values["cvsurl"] then %>
&nbsp;(<a href="<%= values["cvsurl"] %>"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
<% end %>
</td>
</tr>
<tr class="top-aligned-row">
<td><strong>Last Update:</strong></td>
<td><%= values["dtm_modified"] %></td>
</tr>
</table>
</div>
EOF
##
# Class page header template
CLASS_PAGE = <<-EOF
<div id="classHeader">
<table class="header-table">
<tr class="top-aligned-row">
<td><strong><%= values["classmod"] %></strong></td>
<td class="class-name-in-header"><%= values["full_name"] %></td>
</tr>
<tr class="top-aligned-row">
<td><strong>In:</strong></td>
<td>
<% values["infiles"].each do |infiles| %>
<% if infiles["full_path_url"] then %>
<a href="<%= infiles["full_path_url"] %>">
<% end %>
<%= infiles["full_path"] %>
<% if infiles["full_path_url"] then %>
</a>
<% end %>
<% if infiles["cvsurl"] then %>
&nbsp;(<a href="<%= infiles["cvsurl"] %>"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
<% end %>
<br />
<% end %><%# values["infiles"] %>
</td>
</tr>
<% if values["parent"] then %>
<tr class="top-aligned-row">
<td><strong>Parent:</strong></td>
<td>
<% if values["par_url"] then %>
<a href="<%= values["par_url"] %>">
<% end %>
<%= values["parent"] %>
<% if values["par_url"] then %>
</a>
<% end %>
</td>
</tr>
<% end %>
</table>
</div>
EOF
##
# Method list template
METHOD_LIST = <<-EOF
<div id="contextContent">
<% if values["diagram"] then %>
<div id="diagram">
<%= values["diagram"] %>
</div>
<% end %>
<% if values["description"] then %>
<div id="description">
<%= values["description"] %>
</div>
<% end %>
<% if values["requires"] then %>
<div id="requires-list">
<h3 class="section-bar">Required files</h3>
<div class="name-list">
<% values["requires"].each do |requires| %>
<%= href requires["aref"], requires["name"] %>&nbsp;&nbsp;
<% end %><%# values["requires"] %>
</div>
</div>
<% end %>
<% if values["toc"] then %>
<div id="contents-list">
<h3 class="section-bar">Contents</h3>
<ul>
<% values["toc"].each do |toc| %>
<li><a href="#<%= values["href"] %>"><%= values["secname"] %></a></li>
<% end %><%# values["toc"] %>
</ul>
<% end %>
</div>
<% if values["methods"] then %>
<div id="method-list">
<h3 class="section-bar">Methods</h3>
<div class="name-list">
<% values["methods"].each do |methods| %>
<%= href methods["aref"], methods["name"] %>&nbsp;&nbsp;
<% end %><%# values["methods"] %>
</div>
</div>
<% end %>
</div>
<!-- if includes -->
<% if values["includes"] then %>
<div id="includes">
<h3 class="section-bar">Included Modules</h3>
<div id="includes-list">
<% values["includes"].each do |includes| %>
<span class="include-name"><%= href includes["aref"], includes["name"] %></span>
<% end %><%# values["includes"] %>
</div>
</div>
<% end %>
<% values["sections"].each do |sections| %>
<div id="section">
<% if sections["sectitle"] then %>
<h2 class="section-title"><a name="<%= sections["secsequence"] %>"><%= sections["sectitle"] %></a></h2>
<% if sections["seccomment"] then %>
<div class="section-comment">
<%= sections["seccomment"] %>
</div>
<% end %>
<% end %>
<% if values["classlist"] then %>
<div id="class-list">
<h3 class="section-bar">Classes and Modules</h3>
<%= values["classlist"] %>
</div>
<% end %>
<% if values["constants"] then %>
<div id="constants-list">
<h3 class="section-bar">Constants</h3>
<div class="name-list">
<table summary="Constants">
<% values["constants"].each do |constants| %>
<tr class="top-aligned-row context-row">
<td class="context-item-name"><%= constants["name"] %></td>
<td>=</td>
<td class="context-item-value"><%= constants["value"] %></td>
<% if values["desc"] then %>
<td width="3em">&nbsp;</td>
<td class="context-item-desc"><%= constants["desc"] %></td>
<% end %>
</tr>
<% end %><%# values["constants"] %>
</table>
</div>
</div>
<% end %>
<% if values["aliases"] then %>
<div id="aliases-list">
<h3 class="section-bar">External Aliases</h3>
<div class="name-list">
<table summary="aliases">
<% values["aliases"].each do |aliases| $stderr.puts({ :aliases => aliases }.inspect) %>
<tr class="top-aligned-row context-row">
<td class="context-item-name"><%= values["old_name"] %></td>
<td>-&gt;</td>
<td class="context-item-value"><%= values["new_name"] %></td>
</tr>
<% if values["desc"] then %>
<tr class="top-aligned-row context-row">
<td>&nbsp;</td>
<td colspan="2" class="context-item-desc"><%= values["desc"] %></td>
</tr>
<% end %>
<% end %><%# values["aliases"] %>
</table>
</div>
</div>
<% end %>
<% if values["attributes"] then %>
<div id="attribute-list">
<h3 class="section-bar">Attributes</h3>
<div class="name-list">
<table>
<% values["attributes"].each do |attributes| $stderr.puts({ :attributes => attributes }.inspect) %>
<tr class="top-aligned-row context-row">
<td class="context-item-name"><%= values["name"] %></td>
<% if values["rw"] then %>
<td class="context-item-value">&nbsp;[<%= values["rw"] %>]&nbsp;</td>
<% end %>
<% unless values["rw"] then %>
<td class="context-item-value">&nbsp;&nbsp;</td>
<% end %>
<td class="context-item-desc"><%= values["a_desc"] %></td>
</tr>
<% end %><%# values["attributes"] %>
</table>
</div>
</div>
<% end %>
<!-- if method_list -->
<% if sections["method_list"] then %>
<div id="methods">
<% sections["method_list"].each do |method_list| %>
<% if method_list["methods"] then %>
<h3 class="section-bar"><%= method_list["type"] %> <%= method_list["category"] %> methods</h3>
<% method_list["methods"].each do |methods| %>
<div id="method-<%= methods["aref"] %>" class="method-detail">
<a name="<%= methods["aref"] %>"></a>
<div class="method-heading">
<% if methods["codeurl"] then %>
<a href="<%= methods["codeurl"] %>" target="Code" class="method-signature"
onclick="popupCode('<%= methods["codeurl"] %>');return false;">
<% end %>
<% if methods["sourcecode"] then %>
<a href="#<%= methods["aref"] %>" class="method-signature">
<% end %>
<% if methods["callseq"] then %>
<span class="method-name"><%= methods["callseq"] %></span>
<% end %>
<% unless methods["callseq"] then %>
<span class="method-name"><%= methods["name"] %></span><span class="method-args"><%= methods["params"] %></span>
<% end %>
<% if methods["codeurl"] then %>
</a>
<% end %>
<% if methods["sourcecode"] then %>
</a>
<% end %>
</div>
<div class="method-description">
<% if methods["m_desc"] then %>
<%= methods["m_desc"] %>
<% end %>
<% if methods["sourcecode"] then %>
<p><a class="source-toggle" href="#"
onclick="toggleCode('<%= methods["aref"] %>-source');return false;">[Source]</a></p>
<div class="method-source-code" id="<%= methods["aref"] %>-source">
<pre>
<%= methods["sourcecode"] %>
</pre>
</div>
<% end %>
</div>
</div>
<% end %><%# method_list["methods"] %>
<% end %>
<% end %><%# sections["method_list"] %>
</div>
<% end %>
<% end %><%# values["sections"] %>
EOF
##
# Body template
BODY = HEADER + %{ BODY = HEADER + %{
@ -724,72 +80,13 @@ EOF
} + FOOTER } + FOOTER
## SRC_PAGE = RDoc::Generator::HTML::HTML::SRC_PAGE
# Source code template
SRC_PAGE = XHTML_PREAMBLE + <<-EOF FR_INDEX_BODY = RDoc::Generator::HTML::HTML::FR_INDEX_BODY
<html>
<head>
<title><%= values["title"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
<link rel="stylesheet" href="<%= values["style_url"] %>" type="text/css" media="screen" />
</head>
<body class="standalone-code">
<pre><%= values["code"] %></pre>
</body>
</html>
EOF
## FILE_INDEX = RDoc::Generator::HTML::HTML::FILE_INDEX
# Index file templates
FR_INDEX_BODY = %{ CLASS_INDEX = RDoc::Generator::HTML::HTML::CLASS_INDEX
<%= template_include %>
}
FILE_INDEX = XHTML_PREAMBLE + <<-EOF
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title><%= values["list_title"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
<link rel="stylesheet" href="<%= values["style_url"] %>" type="text/css" />
<base target="docwin" />
</head>
<body>
<div class="index">
<h1 class="section-bar"><%= values["list_title"] %></h1>
<div class="index-entries">
<% values["entries"].each do |entries| %>
<a href="<%= entries["href"] %>"><%= entries["name"] %></a><br />
<% end %><%# values["entries"] %>
</div>
</div>
</body>
</html>
EOF
CLASS_INDEX = FILE_INDEX
METHOD_INDEX = FILE_INDEX
INDEX = <<-EOF
<?xml version="1.0" encoding="<%= values["charset"] %>"?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title><%= values["title"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
</head>
<frameset rows="20%, 80%">
<frameset cols="45%,55%">
<frame src="fr_class_index.html" name="Classes" />
<frame src="fr_method_index.html" name="Methods" />
</frameset>
<frame src="<%= values["initial_page"] %>" name="docwin" />
</frameset>
</html>
EOF
METHOD_INDEX = RDoc::Generator::HTML::HTML::METHOD_INDEX
end end

View File

@ -1,16 +1,16 @@
require 'rdoc/generator/html' require 'rdoc/generator/html'
require 'rdoc/generator/html/html' require 'rdoc/generator/html/kilmerfactory'
module RDoc::Generator::HTML::HEFSS module RDoc::Generator::HTML::HEFSS
FONTS = "Verdana, Arial, Helvetica, sans-serif" FONTS = "Verdana, Arial, Helvetica, sans-serif"
STYLE = <<-EOF CENTRAL_STYLE = <<-EOF
body,p { font-family: Verdana, Arial, Helvetica, sans-serif; body,p { font-family: <%= values["fonts"] %>;
color: #000040; background: #BBBBBB; color: #000040; background: #BBBBBB;
} }
td { font-family: Verdana, Arial, Helvetica, sans-serif; td { font-family: <%= values["fonts"] %>;
color: #000040; color: #000040;
} }
@ -21,16 +21,20 @@ td { font-family: Verdana, Arial, Helvetica, sans-serif;
} }
.big-title-font { color: white; .big-title-font { color: white;
font-family: Verdana, Arial, Helvetica, sans-serif; font-family: <%= values["fonts"] %>;
font-size: large; font-size: large;
height: 50px} height: 50px}
.small-title-font { color: purple; .small-title-font { color: purple;
font-family: Verdana, Arial, Helvetica, sans-serif; font-family: <%= values["fonts"] %>;
font-size: small; } font-size: small; }
.aqua { color: purple } .aqua { color: purple }
#diagram img {
border: 0;
}
.method-name, attr-name { .method-name, attr-name {
font-family: monospace; font-weight: bold; font-family: monospace; font-weight: bold;
} }
@ -75,241 +79,6 @@ td { font-family: Verdana, Arial, Helvetica, sans-serif;
color: #0000AA; color: #0000AA;
} }
.column-title {
font-size: medium;
font-weight: bold;
text_decoration: none;
padding: 3px 3px 3px 20px;
color: #3333CC;
}
.variable-name {
font-family: monospace;
font-size: medium;
text_decoration: none;
padding: 3px 3px 3px 20px;
color: #0000AA;
}
.row-name {
font-size: medium;
font-weight: medium;
font-family: monospace;
text_decoration: none;
padding: 3px 3px 3px 20px;
}
.paramsig {
font-size: small;
}
.srcbut { float: right }
EOF
BODY = <<-EOF
<html><head>
<title><%= values["title"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>">
<link rel="stylesheet" href="<%= values["style_url"] %>" type="text/css" media="screen" />
<script type="text/javascript" language="JavaScript">
<!--
function popCode(url) {
parent.frames.source.location = url
}
//-->
</script>
</head>
<body bgcolor="#BBBBBB">
<%= template_include %> <!-- banner header -->
<% if values["diagram"] then %>
<table width="100%"><tr><td align="center">
<%= values["diagram"] %>
</td></tr></table>
<% end %>
<% if values["description"] then %>
<div class="description"><%= values["description"] %></div>
<% end %>
<% if values["requires"] then %>
<table cellpadding="5" width="100%">
<tr><td class="tablesubtitle">Required files</td></tr>
</table><br />
<div class="name-list">
<% values["requires"].each do |requires| %>
<%= href requires["aref"], requires["name"] %>
<% end %><%# values["requires"] %>
<% end %>
</div>
<% if values["sections"] then %>
<% values["sections"].each do |sections| %>
<% if sections["method_list"] then %>
<% sections["method_list"].each do |method_list| %>
<% if method_list["methods"] then %>
<table cellpadding="5" width="100%">
<tr><td class="tablesubtitle">Subroutines and Functions</td></tr>
</table><br />
<div class="name-list">
<% method_list["methods"].each do |methods| %>
<a href="<%= methods["codeurl"] %>" target="source"><%= methods["name"] %></a>
<% end %><%# values["methods"] %>
</div>
<% end %>
<% end %><%# values["method_list"] %>
<% end %>
<% if sections["attributes"] then %>
<table cellpadding="5" width="100%">
<tr><td class="tablesubtitle">Arguments</td></tr>
</table><br />
<table cellspacing="5">
<% sections["attributes"].each do |attributes| %>
<tr valign="top">
<% if attributes["rw"] then %>
<td align="center" class="attr-rw">&nbsp;[<%= attributes["rw"] %>]&nbsp;</td>
<% end %>
<% unless attributes["rw"] then %>
<td></td>
<% end %>
<td class="attr-name"><%= attributes["name"] %></td>
<td><%= attributes["a_desc"] %></td>
</tr>
<% end %><%# values["attributes"] %>
</table>
<% end %>
<% end %><%# values["sections"] %>
<% end %>
<% if values["classlist"] then %>
<table cellpadding="5" width="100%">
<tr><td class="tablesubtitle">Modules</td></tr>
</table><br />
<%= values["classlist"] %><br />
<% end %>
<%= template_include %> <!-- method descriptions -->
</body>
</html>
EOF
FILE_PAGE = <<-EOF
<table width="100%">
<tr class="title-row">
<td><table width="100%"><tr>
<td class="big-title-font" colspan="2"><font size="-3"><b>File</b><br /></font><%= values["short_name"] %></td>
<td align="right"><table cellspacing="0" cellpadding="2">
<tr>
<td class="small-title-font">Path:</td>
<td class="small-title-font"><%= values["full_path"] %>
<% if values["cvsurl"] then %>
&nbsp;(<a href="<%= values["cvsurl"] %>"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
<% end %>
</td>
</tr>
<tr>
<td class="small-title-font">Modified:</td>
<td class="small-title-font"><%= values["dtm_modified"] %></td>
</tr>
</table>
</td></tr></table></td>
</tr>
</table><br />
EOF
CLASS_PAGE = <<-EOF
<table width="100%" border="0" cellspacing="0">
<tr class="title-row">
<td class="big-title-font">
<font size="-3"><b><%= values["classmod"] %></b><br /></font><%= values["full_name"] %>
</td>
<td align="right">
<table cellspacing="0" cellpadding="2">
<tr valign="top">
<td class="small-title-font">In:</td>
<td class="small-title-font">
<% values["infiles"].each do |infiles| %>
<%= href infiles["full_path_url"], infiles["full_path"] %>
<% if infiles["cvsurl"] then %>
&nbsp;(<a href="<%= infiles["cvsurl"] %>"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
<% end %>
<% end %><%# values["infiles"] %>
</td>
</tr>
<% if values["parent"] then %>
<tr>
<td class="small-title-font">Parent:</td>
<td class="small-title-font">
<% if values["par_url"] then %>
<a href="<%= values["par_url"] %>" class="cyan">
<% end %>
<%= values["parent"] %>
<% if values["par_url"] then %>
</a>
<% end %>
</td>
</tr>
<% end %>
</table>
</td>
</tr>
</table><br />
EOF
METHOD_LIST = <<-EOF
<% if values["includes"] then %>
<div class="tablesubsubtitle">Uses</div><br />
<div class="name-list">
<% values["includes"].each do |includes| %>
<span class="method-name"><%= href includes["aref"], includes["name"] %></span>
<% end %><%# values["includes"] %>
</div>
<% end %>
<% if values["sections"] then %>
<% values["sections"].each do |sections| %>
<% if sections["method_list"] then %>
<% sections["method_list"].each do |method_list| %>
<% if method_list["methods"] then %>
<table cellpadding="5" width="100%">
<tr><td class="tablesubtitle"><%= method_list["type"] %> <%= method_list["category"] %> methods</td></tr>
</table>
<% method_list["methods"].each do |methods| %>
<table width="100%" cellspacing="0" cellpadding="5" border="0">
<tr><td class="methodtitle">
<a name="<%= methods["aref"] %>">
<b><%= methods["name"] %></b><%= methods["params"] %>
<% if methods["codeurl"] then %>
<a href="<%= methods["codeurl"] %>" target="source" class="srclink">src</a>
<% end %>
</a></td></tr>
</table>
<% if method_list["m_desc"] then %>
<div class="description">
<%= method_list["m_desc"] %>
</div>
<% end %>
<% end %><%# method_list["methods"] %>
<% end %>
<% end %><%# sections["method_list"] %>
<% end %>
<% end %><%# values["sections"] %>
<% end %>
EOF
SRC_PAGE = <<-EOF
<html>
<head><title><%= values["title"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>">
<style type="text/css">
.kw { color: #3333FF; font-weight: bold }
.cmt { color: green; font-style: italic }
.str { color: #662222; font-style: italic }
.re { color: #662222; }
.ruby-comment { color: green; font-style: italic } .ruby-comment { color: green; font-style: italic }
.ruby-constant { color: #4433aa; font-weight: bold; } .ruby-constant { color: #4433aa; font-weight: bold; }
.ruby-identifier { color: #222222; } .ruby-identifier { color: #222222; }
@ -319,35 +88,23 @@ td { font-family: Verdana, Arial, Helvetica, sans-serif;
.ruby-operator { color: #111111; } .ruby-operator { color: #111111; }
.ruby-regexp { color: #662222; } .ruby-regexp { color: #662222; }
.ruby-value { color: #662222; font-style: italic } .ruby-value { color: #662222; font-style: italic }
</style>
</head> .srcbut { float: right }
<body bgcolor="#BBBBBB">
<pre><%= values["code"] %></pre>
</body>
</html>
EOF EOF
FR_INDEX_BODY = %{ INDEX_STYLE = <<-EOF
<%= template_include %> body {
background-color: #bbbbbb;
font-family: #{FONTS};
font-size: 11px;
font-style: normal;
line-height: 14px;
color: #000040;
} }
FILE_INDEX = <<-EOF
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>">
<style type="text/css">
<!--
body {
background-color: #bbbbbb;
font-family: #{FONTS};
font-size: 11px;
font-style: normal;
line-height: 14px;
color: #000040;
}
div.banner { div.banner {
background: #bbbbcc; background: #bbbbcc;
color: white; color: white;
padding: 1; padding: 1;
margin: 0; margin: 0;
font-size: 90%; font-size: 90%;
@ -356,59 +113,38 @@ div.banner {
text-align: center; text-align: center;
width: 100%; width: 100%;
} }
EOF
--> FACTORY = RDoc::Generator::HTML::
</style> KilmerFactory.new(:central_css => CENTRAL_STYLE,
<base target="docwin"> :index_css => INDEX_STYLE,
</head> :method_list_heading => "Subroutines and Functions",
<body> :class_and_module_list_heading => "Classes and Modules",
<div class="banner"><%= values["list_title"] %></div> :attribute_list_heading => "Arguments")
<% values["entries"].each do |entries| %>
<a href="<%= entries["href"] %>"><%= entries["name"] %></a><br />
<% end %><%# values["entries"] %>
</body></html>
EOF
CLASS_INDEX = FILE_INDEX STYLE = FACTORY.get_STYLE()
METHOD_INDEX = FILE_INDEX
INDEX = <<-EOF METHOD_LIST = FACTORY.get_METHOD_LIST()
<html>
<head> BODY = FACTORY.get_BODY()
<title><%= values["title"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>"> FILE_PAGE = FACTORY.get_FILE_PAGE()
</head>
<frameset cols="20%,*"> CLASS_PAGE = FACTORY.get_CLASS_PAGE()
<frameset rows="15%,35%,50%">
<frame src="fr_file_index.html" title="Files" name="Files">
<frame src="fr_class_index.html" name="Modules">
<frame src="fr_method_index.html" name="Subroutines and Functions">
</frameset>
<frameset rows="80%,20%">
<frame src="<%= values["initial_page"] %>" name="docwin">
<frame src="blank.html" name="source">
</frameset>
<noframes>
<body bgcolor="#BBBBBB">
Click <a href="html/index.html">here</a> for a non-frames
version of this page.
</body>
</noframes>
</frameset>
</html> SRC_PAGE = FACTORY.get_SRC_PAGE()
EOF
# Blank page to use as a target FR_INDEX_BODY = FACTORY.get_FR_INDEX_BODY()
BLANK = %{
<html><body bgcolor="#BBBBBB"></body></html>
}
def write_extra_pages FILE_INDEX = FACTORY.get_FILE_INDEX()
template = TemplatePage.new(BLANK)
File.open("blank.html", "w") { |f| template.write_html_on(f, {}) } CLASS_INDEX = FACTORY.get_CLASS_INDEX()
METHOD_INDEX = FACTORY.get_METHOD_INDEX()
INDEX = FACTORY.get_INDEX()
def self.write_extra_pages(values)
FACTORY.write_extra_pages(values)
end end
end end

View File

@ -1,15 +1,17 @@
require 'rdoc/generator/html' require 'rdoc/generator/html'
require 'rdoc/generator/html/one_page_html' require 'rdoc/generator/html/common'
## ##
# = CSS2 RDoc HTML template # = CSS2 RDoc HTML template
# #
# This is a template for RDoc that uses XHTML 1.0 Transitional and dictates a # This is a template for RDoc that uses XHTML 1.0 Strict and dictates a
# bit more of the appearance of the output to cascading stylesheets than the # bit more of the appearance of the output to cascading stylesheets than the
# default. It was designed for clean inline code display, and uses DHTMl to # default. It was designed for clean inline code display, and uses DHTMl to
# toggle the visibility of each method's source with each click on the # toggle the visibility of each method's source with each click on the
# '[source]' link. # '[source]' link.
# #
# This template *also* forms the basis of the frameless template.
#
# == Authors # == Authors
# #
# * Michael Granger <ged@FaerieMUD.org> # * Michael Granger <ged@FaerieMUD.org>
@ -23,34 +25,54 @@ require 'rdoc/generator/html/one_page_html'
module RDoc::Generator::HTML::HTML module RDoc::Generator::HTML::HTML
include RDoc::Generator::HTML::Common
FONTS = "Verdana,Arial,Helvetica,sans-serif" FONTS = "Verdana,Arial,Helvetica,sans-serif"
STYLE = <<-EOF STYLE = <<-EOF
body { body {
font-family: Verdana,Arial,Helvetica,sans-serif; font-family: #{FONTS};
font-size: 90%; font-size: 90%;
margin: 0; margin: 0;
margin-left: 40px; margin-left: 40px;
padding: 0; padding: 0;
background: white; background: white;
color: black;
} }
h1,h2,h3,h4 { margin: 0; color: #efefef; background: transparent; } h1, h2, h3, h4 {
h1 { font-size: 150%; } margin: 0;
h2,h3,h4 { margin-top: 1em; } background: transparent;
}
a { background: #eef; color: #039; text-decoration: none; } h1 {
a:hover { background: #039; color: #eef; } font-size: 150%;
}
h2,h3,h4 {
margin-top: 1em;
}
:link, :visited {
background: #eef;
color: #039;
text-decoration: none;
}
:link:hover, :visited:hover {
background: #039;
color: #eef;
}
/* Override the base stylesheet's Anchor inside a table cell */ /* Override the base stylesheet's Anchor inside a table cell */
td > a { td > :link, td > :visited {
background: transparent; background: transparent;
color: #039; color: #039;
text-decoration: none; text-decoration: none;
} }
/* and inside a section title */ /* and inside a section title */
.section-title > a { .section-title > :link, .section-title > :visited {
background: transparent; background: transparent;
color: #eee; color: #eee;
text-decoration: none; text-decoration: none;
@ -58,181 +80,255 @@ td > a {
/* === Structural elements =================================== */ /* === Structural elements =================================== */
div#index { .index {
margin: 0; margin: 0;
margin-left: -40px; margin-left: -40px;
padding: 0; padding: 0;
font-size: 90%; font-size: 90%;
} }
.index :link, .index :visited {
div#index a { margin-left: 0.7em;
margin-left: 0.7em;
} }
div#index .section-bar { .index .section-bar {
margin-left: 0px; margin-left: 0px;
padding-left: 0.7em; padding-left: 0.7em;
background: #ccc; background: #ccc;
font-size: small; font-size: small;
} }
#classHeader, #fileHeader {
div#classHeader, div#fileHeader { width: auto;
width: auto; color: white;
color: white; padding: 0.5em 1.5em 0.5em 1.5em;
padding: 0.5em 1.5em 0.5em 1.5em; margin: 0;
margin: 0; margin-left: -40px;
margin-left: -40px; border-bottom: 3px solid #006;
border-bottom: 3px solid #006;
} }
div#classHeader a, div#fileHeader a { #classHeader :link, #fileHeader :link,
background: inherit; #classHeader :visited, #fileHeader :visited {
color: white; background: inherit;
color: white;
} }
div#classHeader td, div#fileHeader td { #classHeader td, #fileHeader td {
background: inherit; background: inherit;
color: white; color: white;
} }
#fileHeader {
div#fileHeader { background: #057;
background: #057;
} }
div#classHeader { #classHeader {
background: #048; background: #048;
} }
.class-name-in-header { .class-name-in-header {
font-size: 180%; font-size: 180%;
font-weight: bold; font-weight: bold;
} }
#bodyContent {
div#bodyContent { padding: 0 1.5em 0 1.5em;
padding: 0 1.5em 0 1.5em;
} }
div#description { #description {
padding: 0.5em 1.5em; padding: 0.5em 1.5em;
background: #efefef; background: #efefef;
border: 1px dotted #999; border: 1px dotted #999;
} }
div#description h1,h2,h3,h4,h5,h6 { #description h1, #description h2, #description h3,
color: #125;; #description h4, #description h5, #description h6 {
background: transparent; color: #125;
background: transparent;
} }
div#validator-badges { #validator-badges {
text-align: center; text-align: center;
}
div#validator-badges img { border: 0; }
div#copyright {
color: #333;
background: #efefef;
font: 0.75em sans-serif;
margin-top: 5em;
margin-bottom: 0;
padding: 0.5em 2em;
} }
#validator-badges img {
border: 0;
}
#copyright {
color: #333;
background: #efefef;
font: 0.75em sans-serif;
margin-top: 5em;
margin-bottom: 0;
padding: 0.5em 2em;
}
/* === Classes =================================== */ /* === Classes =================================== */
table.header-table { table.header-table {
color: white; color: white;
font-size: small; font-size: small;
} }
.type-note { .type-note {
font-size: small; font-size: small;
color: #DEDEDE; color: #dedede;
}
.xxsection-bar {
background: #eee;
color: #333;
padding: 3px;
} }
.section-bar { .section-bar {
color: #333; color: #333;
border-bottom: 1px solid #999; border-bottom: 1px solid #999;
margin-left: -20px; margin-left: -20px;
} }
.section-title { .section-title {
background: #79a; background: #79a;
color: #eee; color: #eee;
padding: 3px; padding: 3px;
margin-top: 2em; margin-top: 2em;
margin-left: -30px; margin-left: -30px;
border: 1px solid #999; border: 1px solid #999;
} }
.top-aligned-row { vertical-align: top } .top-aligned-row {
.bottom-aligned-row { vertical-align: bottom } vertical-align: top
}
.bottom-aligned-row {
vertical-align: bottom
}
#diagram img {
border: 0;
}
/* --- Context section classes ----------------------- */ /* --- Context section classes ----------------------- */
.context-row { } .context-row { }
.context-item-name { font-family: monospace; font-weight: bold; color: black; }
.context-item-value { font-size: small; color: #448; } .context-item-name {
.context-item-desc { color: #333; padding-left: 2em; } font-family: monospace;
font-weight: bold;
color: black;
}
.context-item-value {
font-size: small;
color: #448;
}
.context-item-desc {
color: #333;
padding-left: 2em;
}
/* --- Method classes -------------------------- */ /* --- Method classes -------------------------- */
.method-detail { .method-detail {
background: #efefef; background: #efefef;
padding: 0; padding: 0;
margin-top: 0.5em; margin-top: 0.5em;
margin-bottom: 1em; margin-bottom: 1em;
border: 1px dotted #ccc; border: 1px dotted #ccc;
} }
.method-heading { .method-heading {
color: black; color: black;
background: #ccc; background: #ccc;
border-bottom: 1px solid #666; border-bottom: 1px solid #666;
padding: 0.2em 0.5em 0 0.5em; padding: 0.2em 0.5em 0 0.5em;
} }
.method-signature { color: black; background: inherit; }
.method-name { font-weight: bold; } .method-signature {
.method-args { font-style: italic; } color: black;
.method-description { padding: 0 0.5em 0 0.5em; } background: inherit;
}
.method-name {
font-weight: bold;
}
.method-args {
font-style: italic;
}
.method-description {
padding: 0 0.5em 0 0.5em;
}
/* --- Source code sections -------------------- */ /* --- Source code sections -------------------- */
a.source-toggle { font-size: 90%; } :link.source-toggle, :visited.source-toggle {
div.method-source-code { font-size: 90%;
background: #262626;
color: #ffdead;
margin: 1em;
padding: 0.5em;
border: 1px dashed #999;
overflow: hidden;
} }
div.method-source-code pre { color: #ffdead; overflow: hidden; } div.method-source-code {
background: #262626;
color: #ffdead;
margin: 1em;
padding: 0.5em;
border: 1px dashed #999;
overflow: auto;
}
div.method-source-code pre {
color: #ffdead;
}
/* --- Ruby keyword styles --------------------- */ /* --- Ruby keyword styles --------------------- */
.standalone-code { background: #221111; color: #ffdead; overflow: hidden; } .standalone-code {
background: #221111;
color: #ffdead;
overflow: auto;
}
.ruby-constant { color: #7fffd4; background: transparent; } .ruby-constant {
.ruby-keyword { color: #00ffff; background: transparent; } color: #7fffd4;
.ruby-ivar { color: #eedd82; background: transparent; } background: transparent;
.ruby-operator { color: #00ffee; background: transparent; } }
.ruby-identifier { color: #ffdead; background: transparent; }
.ruby-node { color: #ffa07a; background: transparent; } .ruby-keyword {
.ruby-comment { color: #b22222; font-weight: bold; background: transparent; } color: #00ffff;
.ruby-regexp { color: #ffa07a; background: transparent; } background: transparent;
.ruby-value { color: #7fffd4; background: transparent; } }
.ruby-ivar {
color: #eedd82;
background: transparent;
}
.ruby-operator {
color: #00ffee;
background: transparent;
}
.ruby-identifier {
color: #ffdead;
background: transparent;
}
.ruby-node {
color: #ffa07a;
background: transparent;
}
.ruby-comment {
color: #b22222;
font-weight: bold;
background: transparent;
}
.ruby-regexp {
color: #ffa07a;
background: transparent;
}
.ruby-value {
color: #7fffd4;
background: transparent;
}
EOF EOF
@ -240,15 +336,7 @@ EOF
### H E A D E R T E M P L A T E ### H E A D E R T E M P L A T E
##################################################################### #####################################################################
XHTML_PREAMBLE = <<-EOF HEADER = XHTML_STRICT_PREAMBLE + HTML_ELEMENT + <<-EOF
<?xml version="1.0" encoding="<%= values["charset"] %>"?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
EOF
HEADER = XHTML_PREAMBLE + <<-EOF
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head> <head>
<title><%= values["title"] %></title> <title><%= values["title"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" /> <meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
@ -281,7 +369,7 @@ EOF
} }
// Make codeblocks hidden by default // Make codeblocks hidden by default
document.writeln( "<style type=\\"text/css\\">div.method-source-code { display: none }</style>" ) document.writeln( "<style type=\\"text/css\\">div.method-source-code { display: none }<\\/style>" )
// ]]> // ]]>
</script> </script>
@ -290,13 +378,6 @@ EOF
<body> <body>
EOF EOF
#####################################################################
### C O N T E X T C O N T E N T T E M P L A T E
#####################################################################
CONTEXT_CONTENT = %{
}
##################################################################### #####################################################################
### F O O T E R T E M P L A T E ### F O O T E R T E M P L A T E
##################################################################### #####################################################################
@ -480,8 +561,8 @@ EOF
<td class="context-item-name"><%= constants["name"] %></td> <td class="context-item-name"><%= constants["name"] %></td>
<td>=</td> <td>=</td>
<td class="context-item-value"><%= constants["value"] %></td> <td class="context-item-value"><%= constants["value"] %></td>
<% if sections["desc"] then %> <% if constants["desc"] then %>
<td width="3em">&nbsp;</td> <td>&nbsp;</td>
<td class="context-item-desc"><%= constants["desc"] %></td> <td class="context-item-desc"><%= constants["desc"] %></td>
<% end %> <% end %>
</tr> </tr>
@ -616,8 +697,7 @@ EOF
### S O U R C E C O D E T E M P L A T E ### S O U R C E C O D E T E M P L A T E
##################################################################### #####################################################################
SRC_PAGE = XHTML_PREAMBLE + <<-EOF SRC_PAGE = XHTML_STRICT_PREAMBLE + HTML_ELEMENT + <<-EOF
<html>
<head> <head>
<title><%= values["title"] %></title> <title><%= values["title"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" /> <meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
@ -634,25 +714,22 @@ EOF
### I N D E X F I L E T E M P L A T E S ### I N D E X F I L E T E M P L A T E S
##################################################################### #####################################################################
FR_INDEX_BODY = %{ FR_INDEX_BODY = %{<%= template_include %>}
<%= template_include %>
}
FILE_INDEX = XHTML_PREAMBLE + <<-EOF FILE_INDEX = XHTML_STRICT_PREAMBLE + HTML_ELEMENT + <<-EOF
<!-- <!--
<%= values["list_title"] %> <%= values["title"] %>
--> -->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head> <head>
<title><%= values["list_title"] %></title> <title><%= values["title"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" /> <meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
<link rel="stylesheet" href="<%= values["style_url"] %>" type="text/css" /> <link rel="stylesheet" href="<%= values["style_url"] %>" type="text/css" />
<base target="docwin" /> <base target="docwin" />
</head> </head>
<body> <body>
<div id="index"> <div class="index">
<h1 class="section-bar"><%= values["list_title"] %></h1> <h1 class="section-bar"><%= values["list_title"] %></h1>
<div id="index-entries"> <div id="index-entries">
<% values["entries"].each do |entries| %> <% values["entries"].each do |entries| %>
@ -667,18 +744,12 @@ EOF
CLASS_INDEX = FILE_INDEX CLASS_INDEX = FILE_INDEX
METHOD_INDEX = FILE_INDEX METHOD_INDEX = FILE_INDEX
INDEX = <<-EOF INDEX = XHTML_FRAME_PREAMBLE + HTML_ELEMENT + <<-EOF
<?xml version="1.0" encoding="<%= values["charset"] %>"?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
<!-- <!--
<%= values["title"] %> <%= values["title"] %>
--> -->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head> <head>
<title><%= values["title"] %></title> <title><%= values["title"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" /> <meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />

View File

@ -1,10 +1,11 @@
require 'rdoc/generator/html' require 'rdoc/generator/html'
require 'rdoc/generator/html/kilmerfactory'
module RDoc::Generator::HTML::KILMER module RDoc::Generator::HTML::KILMER
FONTS = "Verdana, Arial, Helvetica, sans-serif" FONTS = "Verdana, Arial, Helvetica, sans-serif"
STYLE = <<-EOF CENTRAL_STYLE = <<-EOF
body,td,p { font-family: <%= values["fonts"] %>; body,td,p { font-family: <%= values["fonts"] %>;
color: #000040; color: #000040;
} }
@ -30,6 +31,10 @@ body,td,p { font-family: <%= values["fonts"] %>;
.aqua { color: black } .aqua { color: black }
#diagram img {
border: 0;
}
.method-name, .attr-name { .method-name, .attr-name {
font-family: font-family: <%= values["fonts"] %>; font-family: font-family: <%= values["fonts"] %>;
font-weight: bold; font-weight: bold;
@ -67,7 +72,7 @@ body,td,p { font-family: <%= values["fonts"] %>;
font-weight: bold; font-weight: bold;
text-decoration: none; text-decoration: none;
color: #000033; color: #000033;
background-color: white; background: #ccc;
} }
.srclink { .srclink {
@ -78,232 +83,8 @@ body,td,p { font-family: <%= values["fonts"] %>;
background-color: white; background-color: white;
} }
.paramsig {
font-size: small;
}
.srcbut { float: right } .srcbut { float: right }
EOF
BODY = <<-EOF
<html><head>
<title><%= values["title"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>">
<link rel="stylesheet" href="<%= values["style_url"] %>" type="text/css" media="screen" />
<script type="text/javascript" language="JavaScript">
<!--
function popCode(url) {
parent.frames.source.location = url
}
//-->
</script>
</head>
<body bgcolor="white">
<%= template_include %> <!-- banner header -->
<% if values["diagram"] then %>
<table width="100%"><tr><td align="center">
<%= values["diagram"] %>
</td></tr></table>
<% end %>
<% if values["description"] then %>
<div class="description"><%= values["description"] %></div>
<% end %>
<% if values["requires"] then %>
<table cellpadding="5" width="100%">
<tr><td class="tablesubtitle">Required files</td></tr>
</table><br />
<div class="name-list">
<% values["requires"].each do |requires| %>
<%= href requires["aref"], requires["name"] %>
<% end %><%# values["requires"] %>
<% end %>
</div>
<% if values["methods"] then %>
<table cellpadding="5" width="100%">
<tr><td class="tablesubtitle">Methods</td></tr>
</table><br />
<div class="name-list">
<% values["methods"].each do |methods| %>
<%= href methods["aref"], methods["name"] %>,
<% end %><%# values["methods"] %>
</div>
<% end %>
<% values["sections"].each do |sections| %>
<div id="section">
<% if sections["sectitle"] then %>
<h2 class="section-title"><a name="<%= sections["secsequence"] %>"><%= sections["sectitle"] %></a></h2>
<% if sections["seccomment"] then %>
<div class="section-comment">
<%= sections["seccomment"] %>
</div>
<% end %>
<% end %>
<% if sections["attributes"] then %>
<table cellpadding="5" width="100%">
<tr><td class="tablesubtitle">Attributes</td></tr>
</table><br />
<table cellspacing="5">
<% sections["attributes"].each do |attributes| %>
<tr valign="top">
<% if attributes["rw"] then %>
<td align="center" class="attr-rw">&nbsp;[<%= attributes["rw"] %>]&nbsp;</td>
<% end %>
<% unless attributes["rw"] then %>
<td></td>
<% end %>
<td class="attr-name"><%= attributes["name"] %></td>
<td><%= attributes["a_desc"] %></td>
</tr>
<% end %><%# sections["attributes"] %>
</table>
<% end %>
<% if sections["classlist"] then %>
<table cellpadding="5" width="100%">
<tr><td class="tablesubtitle">Classes and Modules</td></tr>
</table><br />
<%= sections["classlist"] %><br />
<% end %>
<%= template_include %> <!-- method descriptions -->
<% end %><%# values["sections"] %>
</body>
</html>
EOF
FILE_PAGE = <<-EOF
<table width="100%">
<tr class="title-row">
<td><table width="100%"><tr>
<td class="big-title-font" colspan="2"><font size="-3"><b>File</b><br /></font><%= values["short_name"] %></td>
<td align="right"><table cellspacing="0" cellpadding="2">
<tr>
<td class="small-title-font">Path:</td>
<td class="small-title-font"><%= values["full_path"] %>
<% if values["cvsurl"] then %>
&nbsp;(<a href="<%= values["cvsurl"] %>"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
<% end %>
</td>
</tr>
<tr>
<td class="small-title-font">Modified:</td>
<td class="small-title-font"><%= values["dtm_modified"] %></td>
</tr>
</table>
</td></tr></table></td>
</tr>
</table><br />
EOF
CLASS_PAGE = <<-EOF
<table width="100%" border="0" cellspacing="0">
<tr class="title-row">
<td class="big-title-font">
<font size="-3"><b><%= values["classmod"] %></b><br /></font><%= values["full_name"] %>
</td>
<td align="right">
<table cellspacing="0" cellpadding="2">
<tr valign="top">
<td class="small-title-font">In:</td>
<td class="small-title-font">
<% values["infiles"].each do |infiles| %>
<%= href infiles["full_path_url"], infiles["full_path"] %>
<% if infiles["cvsurl"] then %>
&nbsp;(<a href="<%= infiles["cvsurl"] %>"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
<% end %>
<% end %><%# values["infiles"] %>
</td>
</tr>
<% if values["parent"] then %>
<tr>
<td class="small-title-font">Parent:</td>
<td class="small-title-font">
<% if values["par_url"] then %>
<a href="<%= values["par_url"] %>" class="cyan">
<% end %>
<%= values["parent"] %>
<% if values["par_url"] then %>
</a>
<% end %>
</td>
</tr>
<% end %>
</table>
</td>
</tr>
</table><br />
EOF
METHOD_LIST = <<-EOF
<% if values["includes"] then %>
<div class="tablesubsubtitle">Included modules</div><br />
<div class="name-list">
<% values["includes"].each do |includes| %>
<span class="method-name"><%= href includes["aref"], includes["name"] %></span>
<% end %><%# values["includes"] %>
</div>
<% end %>
<% if values["method_list"] then %>
<% values["method_list"].each do |method_list| $stderr.puts({ :method_list => method_list }.inspect) %>
<% if values["methods"] then %>
<table cellpadding=5 width="100%">
<tr><td class="tablesubtitle"><%= values["type"] %> <%= values["category"] %> methods</td></tr>
</table>
<% values["methods"].each do |methods| $stderr.puts({ :methods => methods }.inspect) %>
<table width="100%" cellspacing="0" cellpadding="5" border="0">
<tr><td class="methodtitle">
<a name="<%= values["aref"] %>">
<% if values["callseq"] then %>
<b><%= values["callseq"] %></b>
<% end %>
<% unless values["callseq"] then %>
<b><%= values["name"] %></b><%= values["params"] %>
<% end %>
<% if values["codeurl"] then %>
<a href="<%= values["codeurl"] %>" target="source" class="srclink">src</a>
<% end %>
</a></td></tr>
</table>
<% if values["m_desc"] then %>
<div class="description">
<%= values["m_desc"] %>
</div>
<% end %>
<% if values["aka"] then %>
<div class="aka">
This method is also aliased as
<% values["aka"].each do |aka| $stderr.puts({ :aka => aka }.inspect) %>
<a href="<%= values["aref"] %>"><%= values["name"] %></a>
<% end %><%# values["aka"] %>
</div>
<% end %>
<% if values["sourcecode"] then %>
<pre class="source">
<%= values["sourcecode"] %>
</pre>
<% end %>
<% end %><%# values["methods"] %>
<% end %>
<% end %><%# values["method_list"] %>
<% end %>
EOF
SRC_PAGE = <<-EOF
<html>
<head><title><%= values["title"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>">
<style type="text/css">
.ruby-comment { color: green; font-style: italic } .ruby-comment { color: green; font-style: italic }
.ruby-constant { color: #4433aa; font-weight: bold; } .ruby-constant { color: #4433aa; font-weight: bold; }
.ruby-identifier { color: #222222; } .ruby-identifier { color: #222222; }
@ -313,28 +94,9 @@ This method is also aliased as
.ruby-operator { color: #111111; } .ruby-operator { color: #111111; }
.ruby-regexp { color: #662222; } .ruby-regexp { color: #662222; }
.ruby-value { color: #662222; font-style: italic } .ruby-value { color: #662222; font-style: italic }
.kw { color: #3333FF; font-weight: bold }
.cmt { color: green; font-style: italic }
.str { color: #662222; font-style: italic }
.re { color: #662222; }
</style>
</head>
<body bgcolor="white">
<pre><%= values["code"] %></pre>
</body>
</html>
EOF EOF
FR_INDEX_BODY = %{ INDEX_STYLE = <<-EOF
<%= template_include %>
}
FILE_INDEX = <<-EOF
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>">
<style>
<!--
body { body {
background-color: #ddddff; background-color: #ddddff;
font-family: #{FONTS}; font-family: #{FONTS};
@ -355,64 +117,35 @@ div.banner {
text-align: center; text-align: center;
width: 100%; width: 100%;
} }
EOF
--> FACTORY = RDoc::Generator::HTML::
</style> KilmerFactory.new(:central_css => CENTRAL_STYLE,
<base target="docwin"> :index_css => INDEX_STYLE)
</head>
<body>
<div class="banner"><%= values["list_title"] %></div>
<% values["entries"].each do |entries| %>
<a href="<%= entries["href"] %>"><%= entries["name"] %></a><br />
<% end %><%# values["entries"] %>
</body></html>
EOF
CLASS_INDEX = FILE_INDEX STYLE = FACTORY.get_STYLE()
METHOD_INDEX = FILE_INDEX
INDEX = <<-EOF METHOD_LIST = FACTORY.get_METHOD_LIST()
<html>
<head> BODY = FACTORY.get_BODY()
<title><%= values["title"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>"> FILE_PAGE = FACTORY.get_FILE_PAGE()
</head>
<frameset cols="20%,*"> CLASS_PAGE = FACTORY.get_CLASS_PAGE()
<frameset rows="15%,35%,50%">
<frame src="fr_file_index.html" title="Files" name="Files">
<frame src="fr_class_index.html" name="Classes">
<frame src="fr_method_index.html" name="Methods">
</frameset>
<% if values["inline_source"] then %>
<frame src="<%= values["initial_page"] %>" name="docwin">
<% end %>
<% unless values["inline_source"] then %>
<frameset rows="80%,20%">
<frame src="<%= values["initial_page"] %>" name="docwin">
<frame src="blank.html" name="source">
</frameset>
<% end %>
<noframes>
<body bgcolor="white">
Click <a href="html/index.html">here</a> for a non-frames
version of this page.
</body>
</noframes>
</frameset>
</html> SRC_PAGE = FACTORY.get_SRC_PAGE()
EOF
# A blank page to use as a target FR_INDEX_BODY = FACTORY.get_FR_INDEX_BODY()
BLANK = %{
<html><body bgcolor="white"></body></html>
}
def write_extra_pages FILE_INDEX = FACTORY.get_FILE_INDEX()
template = TemplatePage.new(BLANK)
File.open("blank.html", "w") { |f| template.write_html_on(f, {}) } CLASS_INDEX = FACTORY.get_CLASS_INDEX()
METHOD_INDEX = FACTORY.get_METHOD_INDEX()
INDEX = FACTORY.get_INDEX()
def self.write_extra_pages(values)
FACTORY.write_extra_pages(values)
end end
end end

View File

@ -0,0 +1,427 @@
require 'rdoc/generator/html'
require 'rdoc/generator/html/common'
#
# This class generates Kilmer-style templates. Right now,
# rdoc is shipped with two such templates:
# * kilmer
# * hefss
#
# Kilmer-style templates use frames. The left side of the page has
# three frames stacked on top of each other: one lists
# files, one lists classes, and one lists methods. If source code
# is not inlined, an additional frame runs across the bottom of
# the page and will be used to display method source code.
# The central (and largest frame) display class and file
# pages.
#
# The constructor of this class accepts a Hash containing stylistic
# attributes. Then, a get_BLAH instance method of this class returns a
# value for the template's BLAH constant. get_BODY, for instance, returns
# the value of the template's BODY constant.
#
class RDoc::Generator::HTML::KilmerFactory
include RDoc::Generator::HTML::Common
#
# The contents of the stylesheet that should be used for the
# central frame (for the class and file pages).
#
# This must be specified in the Hash passed to the constructor.
#
attr_reader :central_css
#
# The contents of the stylesheet that should be used for the
# index pages.
#
# This must be specified in the Hash passed to the constructor.
#
attr_reader :index_css
#
# The heading that should be displayed before listing methods.
#
# If not supplied, this defaults to "Methods".
#
attr_reader :method_list_heading
#
# The heading that should be displayed before listing classes and
# modules.
#
# If not supplied, this defaults to "Classes and Modules".
#
attr_reader :class_and_module_list_heading
#
# The heading that should be displayed before listing attributes.
#
# If not supplied, this defaults to "Attributes".
#
attr_reader :attribute_list_heading
#
# ====Description:
# This method constructs a KilmerFactory instance, which
# can be used to build Kilmer-style template classes.
# The +style_attributes+ argument is a Hash that contains the
# values of the classes attributes (Symbols mapped to Strings).
#
# ====Parameters:
# [style_attributes]
# A Hash describing the appearance of the Kilmer-style.
#
def initialize(style_attributes)
@central_css = style_attributes[:central_css]
if(!@central_css)
raise ArgumentError, "did not specify a value for :central_css"
end
@index_css = style_attributes[:index_css]
if(!@index_css)
raise ArgumentError, "did not specify a value for :index_css"
end
@method_list_heading = style_attributes[:method_list_heading]
if(!@method_list_heading)
@method_list_heading = "Methods"
end
@class_and_module_list_heading = style_attributes[:class_and_module_list_heading]
if(!@class_and_module_list_heading)
@class_and_module_list_heading = "Classes and Modules"
end
@attribute_list_heading = style_attributes[:attribute_list_heading]
if(!@attribute_list_heading)
@attribute_list_heading = "Attributes"
end
end
def get_STYLE
return @central_css
end
def get_METHOD_LIST
return %{
<% if values["diagram"] then %>
<div id="diagram">
<table width="100%"><tr><td align="center">
<%= values["diagram"] %>
</td></tr></table>
</div>
<% end %>
<% if values["description"] then %>
<div class="description"><%= values["description"] %></div>
<% end %>
<% if values["requires"] then %>
<table cellpadding="5" width="100%">
<tr><td class="tablesubtitle">Required files</td></tr>
</table><br />
<div class="name-list">
<% values["requires"].each do |requires| %>
<%= href requires["aref"], requires["name"] %>
<% end %><%# values["requires"] %>
</div>
<% end %>
<% if values["methods"] then %>
<table cellpadding="5" width="100%">
<tr><td class="tablesubtitle">#{@method_list_heading}</td></tr>
</table><br />
<div class="name-list">
<% values["methods"].each do |methods| %>
<%= href methods["aref"], methods["name"] %>,
<% end %><%# values["methods"] %>
</div>
<% end %>
<% if values["includes"] then %>
<div class="tablesubsubtitle">Included modules</div><br />
<div class="name-list">
<% values["includes"].each do |includes| %>
<span class="method-name"><%= href includes["aref"], includes["name"] %></span>
<% end %><%# values["includes"] %>
</div>
<% end %>
<% values["sections"].each do |sections| %>
<div id="section">
<% if sections["sectitle"] then %>
<h2 class="section-title"><a name="<%= sections["secsequence"] %>"><%= sections["sectitle"] %></a></h2>
<% if sections["seccomment"] then %>
<div class="section-comment">
<%= sections["seccomment"] %>
</div>
<% end %>
<% end %>
<% if sections["attributes"] then %>
<table cellpadding="5" width="100%">
<tr><td class="tablesubtitle">#{@attribute_list_heading}</td></tr>
</table><br />
<table cellspacing="5">
<% sections["attributes"].each do |attributes| %>
<tr valign="top">
<% if attributes["rw"] then %>
<td align="center" class="attr-rw">&nbsp;[<%= attributes["rw"] %>]&nbsp;</td>
<% end %>
<% unless attributes["rw"] then %>
<td></td>
<% end %>
<td class="attr-name"><%= attributes["name"] %></td>
<td><%= attributes["a_desc"] %></td>
</tr>
<% end %><%# sections["attributes"] %>
</table>
<% end %>
<% if sections["classlist"] then %>
<table cellpadding="5" width="100%">
<tr><td class="tablesubtitle">#{@class_and_module_list_heading}</td></tr>
</table><br />
<%= sections["classlist"] %><br />
<% end %>
<% if sections["method_list"] then %>
<% sections["method_list"].each do |method_list| %>
<% if method_list["methods"] then %>
<table cellpadding="5" width="100%">
<tr><td class="tablesubtitle"><%= method_list["type"] %> <%= method_list["category"] %> methods</td></tr>
</table>
<% method_list["methods"].each do |methods| %>
<table width="100%" cellspacing="0" cellpadding="5" border="0">
<tr><td class="methodtitle">
<a name="<%= methods["aref"] %>">
<% if methods["callseq"] then %>
<b><%= methods["callseq"] %></b>
<% end %>
<% unless methods["callseq"] then %>
<b><%= methods["name"] %></b><%= methods["params"] %>
<% end %>
</a>
<% if methods["codeurl"] then %>
<a href="<%= methods["codeurl"] %>" target="source" class="srclink">src</a>
<% end %>
</td></tr>
</table>
<% if methods["m_desc"] then %>
<div class="description">
<%= methods["m_desc"] %>
</div>
<% end %>
<% if methods["aka"] then %>
<div class="aka">
This method is also aliased as
<% methods["aka"].each do |aka| %>
<a href="<%= methods["aref"] %>"><%= methods["name"] %></a>
<% end %><%# methods["aka"] %>
</div>
<% end %>
<% if methods["sourcecode"] then %>
<pre class="source">
<%= methods["sourcecode"] %>
</pre>
<% end %>
<% end %><%# method_list["methods"] %>
<% end %>
<% end %><%# sections["method_list"] %>
<% end %>
<% end %><%# values["sections"] %>
</div>
}
end
def get_BODY
return XHTML_STRICT_PREAMBLE + HTML_ELEMENT + %{
<head>
<title><%= values["title"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
<link rel="stylesheet" href="<%= values["style_url"] %>" type="text/css" media="screen" />
<script type="text/javascript">
<!--
function popCode(url) {
parent.frames.source.location = url
}
//-->
</script>
</head>
<body>
<div class="bodyContent">
<%= template_include %> <!-- banner header -->
#{get_METHOD_LIST()}
</div>
</body>
</html>
}
end
def get_FILE_PAGE
return %{
<table width="100%">
<tr class="title-row">
<td><table width="100%"><tr>
<td class="big-title-font" colspan="2">File<br /><%= values["short_name"] %></td>
<td align="right"><table cellspacing="0" cellpadding="2">
<tr>
<td class="small-title-font">Path:</td>
<td class="small-title-font"><%= values["full_path"] %>
<% if values["cvsurl"] then %>
&nbsp;(<a href="<%= values["cvsurl"] %>"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
<% end %>
</td>
</tr>
<tr>
<td class="small-title-font">Modified:</td>
<td class="small-title-font"><%= values["dtm_modified"] %></td>
</tr>
</table>
</td></tr></table></td>
</tr>
</table><br />
}
end
def get_CLASS_PAGE
return %{
<table width="100%" border="0" cellspacing="0">
<tr class="title-row">
<td class="big-title-font">
<%= values["classmod"] %><br /><%= values["full_name"] %>
</td>
<td align="right">
<table cellspacing="0" cellpadding="2">
<tr valign="top">
<td class="small-title-font">In:</td>
<td class="small-title-font">
<% values["infiles"].each do |infiles| %>
<%= href infiles["full_path_url"], infiles["full_path"] %>
<% if infiles["cvsurl"] then %>
&nbsp;(<a href="<%= infiles["cvsurl"] %>"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
<% end %>
<% end %><%# values["infiles"] %>
</td>
</tr>
<% if values["parent"] then %>
<tr>
<td class="small-title-font">Parent:</td>
<td class="small-title-font">
<% if values["par_url"] then %>
<a href="<%= values["par_url"] %>" class="cyan">
<% end %>
<%= values["parent"] %>
<% if values["par_url"] then %>
</a>
<% end %>
</td>
</tr>
<% end %>
</table>
</td>
</tr>
</table><br />
}
end
def get_SRC_PAGE
return XHTML_STRICT_PREAMBLE + HTML_ELEMENT + %{
<head><title><%= values["title"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
<link rel="stylesheet" href="<%= values["style_url"] %>" type="text/css" media="screen" />
</head>
<body>
<pre><%= values["code"] %></pre>
</body>
</html>
}
end
def get_FR_INDEX_BODY
return %{<%= template_include %>}
end
def get_FILE_INDEX
return XHTML_STRICT_PREAMBLE + HTML_ELEMENT + %{
<head>
<title><%= values["title"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
<style type="text/css">
<!--
#{@index_css}
-->
</style>
<base target="docwin" />
</head>
<body>
<div class="index">
<div class="banner"><%= values["list_title"] %></div>
<% values["entries"].each do |entries| %>
<a href="<%= entries["href"] %>"><%= entries["name"] %></a><br />
<% end %><%# values["entries"] %>
</div>
</body></html>
}
end
def get_CLASS_INDEX
return get_FILE_INDEX
end
def get_METHOD_INDEX
return get_FILE_INDEX
end
def get_INDEX
return XHTML_FRAME_PREAMBLE + HTML_ELEMENT + %{
<head>
<title><%= values["title"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
</head>
<frameset cols="20%,*">
<frameset rows="15%,35%,50%">
<frame src="fr_file_index.html" title="Files" name="Files" />
<frame src="fr_class_index.html" name="Classes" />
<frame src="fr_method_index.html" name="Methods" />
</frameset>
<% if values["inline_source"] then %>
<frame src="<%= values["initial_page"] %>" name="docwin" />
<% end %>
<% unless values["inline_source"] then %>
<frameset rows="80%,20%">
<frame src="<%= values["initial_page"] %>" name="docwin" />
<frame src="blank.html" name="source" />
</frameset>
<% end %>
</frameset>
</html>
}
end
def get_BLANK
# This will be displayed in the source code frame before
# any source code has been selected.
return XHTML_STRICT_PREAMBLE + HTML_ELEMENT + %{
<head>
<title>Source Code Frame <%= values["title_suffix"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
<link rel="stylesheet" href="<%= values["style_url"] %>" type="text/css" media="screen" />
</head>
<body>
</body>
</html>
}
end
def write_extra_pages(values)
template = RDoc::TemplatePage.new(get_BLANK())
File.open("blank.html", "w") { |f| template.write_html_on(f, values) }
end
end

View File

@ -1,7 +1,10 @@
require 'rdoc/generator/html' require 'rdoc/generator/html'
require 'rdoc/generator/html/common'
module RDoc::Generator::HTML::ONE_PAGE_HTML module RDoc::Generator::HTML::ONE_PAGE_HTML
include RDoc::Generator::HTML::Common
CONTENTS_XML = <<-EOF CONTENTS_XML = <<-EOF
<% if defined? classes and classes["description"] then %> <% if defined? classes and classes["description"] then %>
<%= classes["description"] %> <%= classes["description"] %>
@ -76,16 +79,14 @@ module RDoc::Generator::HTML::ONE_PAGE_HTML
<% end %> <% end %>
EOF EOF
ONE_PAGE = %{ ONE_PAGE = XHTML_STRICT_PREAMBLE + HTML_ELEMENT + %{
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head> <head>
<title><%= values["title"] %></title> <title><%= values["title"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" /> <meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
</head> </head>
<body> <body>
<% values["files"].each do |files| %> <% values["files"].each do |files| %>
<h2>File: <%= files["short_name"] %></h2> <h2>File: <a name="<%= files["href"] %>"><%= files["short_name"] %></a></h2>
<table> <table>
<tr><td>Path:</td><td><%= files["full_path"] %></td></tr> <tr><td>Path:</td><td><%= files["full_path"] %></td></tr>
<tr><td>Modified:</td><td><%= files["dtm_modified"] %></td></tr> <tr><td>Modified:</td><td><%= files["dtm_modified"] %></td></tr>
@ -97,7 +98,7 @@ module RDoc::Generator::HTML::ONE_PAGE_HTML
<h2>Classes</h2> <h2>Classes</h2>
<% values["classes"].each do |classes| %> <% values["classes"].each do |classes| %>
<% if classes["parent"] then %> <% if classes["parent"] then %>
<h3><%= classes["classmod"] %> <%= classes["full_name"] %> &lt; <%= href classes["par_url"], classes["parent"] %></h3> <h3><%= classes["classmod"] %> <a name="<%= classes["href"] %>"><%= classes["full_name"] %></a> &lt; <%= href classes["par_url"], classes["parent"] %></h3>
<% end %> <% end %>
<% unless classes["parent"] then %> <% unless classes["parent"] then %>
<h3><%= classes["classmod"] %> <%= classes["full_name"] %></h3> <h3><%= classes["classmod"] %> <%= classes["full_name"] %></h3>

View File

@ -3,13 +3,10 @@ require 'rdoc/generator'
require 'rdoc/markup/to_texinfo' require 'rdoc/markup/to_texinfo'
module RDoc module RDoc
RDoc::GENERATORS['texinfo'] = RDoc::Generator.new("rdoc/generator/texinfo",
:Texinfo,
'texinfo')
module Generator module Generator
# This generates Texinfo files for viewing with GNU Info or Emacs # This generates Texinfo files for viewing with GNU Info or Emacs
# from RDoc extracted from Ruby source files. # from RDoc extracted from Ruby source files.
class Texinfo class TEXINFO
# What should the .info file be named by default? # What should the .info file be named by default?
DEFAULT_INFO_FILENAME = 'rdoc.info' DEFAULT_INFO_FILENAME = 'rdoc.info'
@ -26,8 +23,8 @@ module RDoc
# Generate the +texinfo+ files # Generate the +texinfo+ files
def generate(toplevels) def generate(toplevels)
@toplevels = toplevels @toplevels = toplevels
@files, @classes = ::RDoc::Generator::Context.build_indicies(@toplevels, @files, @classes = ::RDoc::Generator::Context.build_indices(@toplevels,
@options) @options)
(@files + @classes).each { |x| x.value_hash } (@files + @classes).each { |x| x.value_hash }

View File

@ -38,7 +38,7 @@ Methods
<% (method_list["methods"] || []).uniq.each do |method| %> <% (method_list["methods"] || []).uniq.each do |method| %>
<%= TexinfoTemplate.new(@v.merge({'method' => method, 'list' => method_list}), <%= TexinfoTemplate.new(@v.merge({'method' => method, 'list' => method_list}),
'method.texinfo.erb').render %><% end %> 'method.texinfo.erb').render %><% end %>
<% end # section["method_list"] %> <% end %>
<% end %> <% end # if section["method_list"] %>
<% end # @v['class']["sections"] %> <% end # @v['class']["sections"] %>
<% end %> <% end %>

View File

@ -34,15 +34,15 @@ class RDoc::Generator::XML < RDoc::Generator::HTML
## ##
# Generate: # Generate:
# #
# * a list of HtmlFile objects for each TopLevel object. # * a list of File objects for each TopLevel object.
# * a list of HtmlClass objects for each first level # * a list of Class objects for each first level
# class or module in the TopLevel objects # class or module in the TopLevel objects
# * a complete list of all hyperlinkable terms (file, # * a complete list of all hyperlinkable terms (file,
# class, module, and method names) # class, module, and method names)
def build_indices def build_indices
@info.each do |toplevel| @info.each do |toplevel|
@files << RDoc::Generator::HtmlFile.new(toplevel, @options, RDoc::Generator::FILE_DIR) @files << RDoc::Generator::File.new(toplevel, @options, RDoc::Generator::FILE_DIR)
end end
RDoc::TopLevel.all_classes_and_modules.each do |cls| RDoc::TopLevel.all_classes_and_modules.each do |cls|
@ -51,7 +51,7 @@ class RDoc::Generator::XML < RDoc::Generator::HTML
end end
def build_class_list(from, html_file, class_dir) def build_class_list(from, html_file, class_dir)
@classes << RDoc::Generator::HtmlClass.new(from, html_file, class_dir, @options) @classes << RDoc::Generator::Class.new(from, html_file, class_dir, @options)
from.each_classmodule do |mod| from.each_classmodule do |mod|
build_class_list(mod, html_file, class_dir) build_class_list(mod, html_file, class_dir)
end end
@ -68,9 +68,6 @@ class RDoc::Generator::XML < RDoc::Generator::HTML
'classes' => gen_into(@classes) 'classes' => gen_into(@classes)
} }
# this method is defined in the template file
write_extra_pages if defined? write_extra_pages
template = RDoc::TemplatePage.new @template::ONE_PAGE template = RDoc::TemplatePage.new @template::ONE_PAGE
if @options.op_name if @options.op_name

View File

@ -17,11 +17,23 @@ module RDoc::Generator::XML::XML
href="<%= requires["aref"] %>" href="<%= requires["aref"] %>"
<% end %> <% end %>
/> />
<% end # files["requires"] %> <% end %><%# files["requires"] %>
</required-file-list> </required-file-list>
<% end %> <% end %>
<% if defined? classes and classes["sections"] then %> <% if defined? classes and classes["sections"] then %>
<% classes["sections"].each do |sections| %> <% classes["sections"].each do |sections| %>
<% if sections["constants"] then %>
<constant-list>
<% sections["constants"].each do |constant| %>
<constant name="<%= constant["name"] %>">
<% if constant["value"] then %>
<value><%= constant["value"] %></value>
<% end %>
<description><%= constant["a_desc"] %></description>
</constant>
<% end %><%# sections["constants"] %>
</constant-list>
<% end %>
<% if sections["attributes"] then %> <% if sections["attributes"] then %>
<attribute-list> <attribute-list>
<% sections["attributes"].each do |attributes| %> <% sections["attributes"].each do |attributes| %>
@ -31,7 +43,7 @@ module RDoc::Generator::XML::XML
<% end %> <% end %>
<description><%= attributes["a_desc"] %></description> <description><%= attributes["a_desc"] %></description>
</attribute> </attribute>
<% end # sections["attributes"] %> <% end %><%# sections["attributes"] %>
</attribute-list> </attribute-list>
<% end %> <% end %>
<% if sections["method_list"] then %> <% if sections["method_list"] then %>
@ -52,12 +64,12 @@ module RDoc::Generator::XML::XML
</source-code-listing> </source-code-listing>
<% end %> <% end %>
</method> </method>
<% end # method_list["methods"] %> <% end %><%# method_list["methods"] %>
<% end %> <% end %>
<% end # sections["method_list"] %> <% end %><%# sections["method_list"] %>
</method-list> </method-list>
<% end %> <% end %>
<% end # classes["sections"] %> <% end %><%# classes["sections"] %>
<% end %> <% end %>
<% if defined? classes and classes["includes"] then %> <% if defined? classes and classes["includes"] then %>
<included-module-list> <included-module-list>
@ -67,7 +79,7 @@ module RDoc::Generator::XML::XML
href="<%= includes["aref"] %>" href="<%= includes["aref"] %>"
<% end %> <% end %>
/> />
<% end # classes["includes"] %> <% end %><%# classes["includes"] %>
</included-module-list> </included-module-list>
<% end %> <% end %>
</contents> </contents>
@ -84,7 +96,7 @@ module RDoc::Generator::XML::XML
</file-info> </file-info>
} + CONTENTS_XML + %{ } + CONTENTS_XML + %{
</file> </file>
<% end # values["files"] %> <% end %><%# values["files"] %>
</file-list> </file-list>
<class-module-list> <class-module-list>
<% values["classes"].each do |classes| %> <% values["classes"].each do |classes| %>
@ -94,7 +106,7 @@ module RDoc::Generator::XML::XML
<infiles> <infiles>
<% classes["infiles"].each do |infiles| %> <% classes["infiles"].each do |infiles| %>
<infile><%= href infiles["full_path_url"], infiles["full_path"] %></infile> <infile><%= href infiles["full_path_url"], infiles["full_path"] %></infile>
<% end # classes["infiles"] %> <% end %><%# classes["infiles"] %>
</infiles> </infiles>
<% end %> <% end %>
<% if classes["parent"] then %> <% if classes["parent"] then %>
@ -103,7 +115,7 @@ module RDoc::Generator::XML::XML
</classmod-info> </classmod-info>
} + CONTENTS_XML + %{ } + CONTENTS_XML + %{
</<%= classes["classmod"] %>> </<%= classes["classmod"] %>>
<% end # values["classes"] %> <% end %><%# values["classes"] %>
</class-module-list> </class-module-list>
</rdoc> </rdoc>
} }

View File

@ -20,101 +20,6 @@ require 'rdoc'
# RDoc::Markup could be the basis for formatting RDoc style comment blocks, # RDoc::Markup could be the basis for formatting RDoc style comment blocks,
# Wiki entries, and online FAQs. # Wiki entries, and online FAQs.
# #
# = Basic Formatting
#
# * RDoc::Markup looks for a document's natural left margin. This is
# used as the initial margin for the document.
#
# * Consecutive lines starting at this margin are considered to be a
# paragraph.
#
# * If a paragraph starts with a "*", "-", or with "<digit>.", then it is
# taken to be the start of a list. The margin in increased to be the first
# non-space following the list start flag. Subsequent lines should be
# indented to this \new margin until the list ends. For example:
#
# * this is a list with three paragraphs in
# the first item. This is the first paragraph.
#
# And this is the second paragraph.
#
# 1. This is an indented, numbered list.
# 2. This is the second item in that list
#
# This is the third conventional paragraph in the
# first list item.
#
# * This is the second item in the original list
#
# * You can also construct labeled lists, sometimes called description
# or definition lists. Do this by putting the label in square brackets
# and indenting the list body:
#
# [cat] a small furry mammal
# that seems to sleep a lot
#
# [ant] a little insect that is known
# to enjoy picnics
#
# A minor variation on labeled lists uses two colons to separate the
# label from the list body:
#
# cat:: a small furry mammal
# that seems to sleep a lot
#
# ant:: a little insect that is known
# to enjoy picnics
#
# This latter style guarantees that the list bodies' left margins are
# aligned: think of them as a two column table.
#
# * Any line that starts to the right of the current margin is treated
# as verbatim text. This is useful for code listings. The example of a
# list above is also verbatim text.
#
# * A line starting with an equals sign (=) is treated as a
# heading. Level one headings have one equals sign, level two headings
# have two,and so on.
#
# * A line starting with three or more hyphens (at the current indent)
# generates a horizontal rule. The more hyphens, the thicker the rule
# (within reason, and if supported by the output device)
#
# * You can use markup within text (except verbatim) to change the
# appearance of parts of that text. Out of the box, RDoc::Markup
# supports word-based and general markup.
#
# Word-based markup uses flag characters around individual words:
#
# [\*word*] displays word in a *bold* font
# [\_word_] displays word in an _emphasized_ font
# [\+word+] displays word in a +code+ font
#
# General markup affects text between a start delimiter and and end
# delimiter. Not surprisingly, these delimiters look like HTML markup.
#
# [\<b>text...</b>] displays word in a *bold* font
# [\<em>text...</em>] displays word in an _emphasized_ font
# [\<i>text...</i>] displays word in an _emphasized_ font
# [\<tt>text...</tt>] displays word in a +code+ font
#
# Unlike conventional Wiki markup, general markup can cross line
# boundaries. You can turn off the interpretation of markup by
# preceding the first character with a backslash, so \\\<b>bold
# text</b> and \\\*bold* produce \<b>bold text</b> and \*bold*
# respectively.
#
# * Hyperlinks to the web starting http:, mailto:, ftp:, or www. are
# recognized. An HTTP url that references an external image file is
# converted into an inline <IMG..>. Hyperlinks starting 'link:' are
# assumed to refer to local files whose path is relative to the --op
# directory.
#
# Hyperlinks can also be of the form <tt>label</tt>[url], in which
# case the label is used in the displayed text, and <tt>url</tt> is
# used as the target. If <tt>label</tt> contains multiple words,
# put it in braces: <em>{multi word label}[</em>url<em>]</em>.
#
# == Synopsis # == Synopsis
# #
# This code converts +input_string+ to HTML. The conversion takes place in # This code converts +input_string+ to HTML. The conversion takes place in

View File

@ -47,7 +47,7 @@ class RDoc::Markup
class AttrChanger class AttrChanger
def to_s def to_s
"Attr: +#{Attribute.as_string(@turn_on)}/-#{Attribute.as_string(@turn_on)}" "Attr: +#{Attribute.as_string(turn_on)}/-#{Attribute.as_string(turn_on)}"
end end
end end

View File

@ -57,7 +57,7 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
## ##
# Generate a hyperlink for url, labeled with text. Handle the # Generate a hyperlink for url, labeled with text. Handle the
# special cases for img: and link: described under handle_special_HYPEDLINK # special cases for img: and link: described under handle_special_HYPERLINK
def gen_url(url, text) def gen_url(url, text)
if url =~ /([A-Za-z]+):(.*)/ then if url =~ /([A-Za-z]+):(.*)/ then
@ -304,9 +304,12 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
# some of these patterns are taken from SmartyPants... # some of these patterns are taken from SmartyPants...
def convert_string_fancy(item) def convert_string_fancy(item)
# convert -- to em-dash, (-- to en-dash) # convert ampersand before doing anything else
item.gsub(/---?/, '&#8212;'). #gsub(/--/, '&#8211;'). item.gsub(/&/, '&amp;').
# convert -- to em-dash, (-- to en-dash)
gsub(/---?/, '&#8212;'). #gsub(/--/, '&#8211;').
# convert ... to elipsis (and make sure .... becomes .<elipsis>) # convert ... to elipsis (and make sure .... becomes .<elipsis>)
gsub(/\.\.\.\./, '.&#8230;').gsub(/\.\.\./, '&#8230;'). gsub(/\.\.\.\./, '.&#8230;').gsub(/\.\.\./, '&#8230;').
@ -318,15 +321,15 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
gsub(/'/, '&#8216;'). gsub(/'/, '&#8216;').
# convert double closing quote # convert double closing quote
gsub(%r{([^ \t\r\n\[\{\(])\'(?=\W)}, '\1&#8221;'). # } gsub(%r{([^ \t\r\n\[\{\(])\"(?=\W)}, '\1&#8221;'). # }
# convert double opening quote # convert double opening quote
gsub(/'/, '&#8220;'). gsub(/"/, '&#8220;').
# convert copyright # convert copyright
gsub(/\(c\)/, '&#169;'). gsub(/\(c\)/, '&#169;').
# convert and registered trademark # convert registered trademark
gsub(/\(r\)/, '&#174;') gsub(/\(r\)/, '&#174;')
end end

View File

@ -9,6 +9,68 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
attr_accessor :context attr_accessor :context
# Regular expressions to match class and method references.
#
# 1.) There can be a '\' in front of text to suppress
# any cross-references (note, however, that the single '\'
# is written as '\\\\' in order to escape it twice, once
# in the Ruby String literal and once in the regexp).
# 2.) There can be a '::' in front of class names to reference
# from the top-level namespace.
# 3.) The method can be followed by parenthesis,
# which may or may not have things inside (this
# apparently is allowed for Fortran 95, but I also think that this
# is a good idea for Ruby, as it is very reasonable to want to
# reference a call with arguments).
#
# NOTE: In order to support Fortran 95 properly, the [A-Z] below
# should be changed to [A-Za-z]. This slows down rdoc significantly,
# however, and the Fortran 95 support is broken in any case due to
# the return in handle_special_CROSSREF if the token consists
# entirely of lowercase letters.
#
# The markup/cross-referencing engine needs a rewrite for
# Fortran 95 to be supported properly.
CLASS_REGEXP_STR = '\\\\?((?:\:{2})?[A-Z]\w*(?:\:\:\w+)*)'
METHOD_REGEXP_STR = '(\w+[!?=]?)(?:\([\.\w+\*\/\+\-\=\<\>]*\))?'
# Regular expressions matching text that should potentially have
# cross-reference links generated are passed to add_special.
# Note that these expressions are meant to pick up text for which
# cross-references have been suppressed, since the suppression
# characters are removed by the code that is triggered.
CROSSREF_REGEXP = /(
# A::B::C.meth
#{CLASS_REGEXP_STR}[\.\#]#{METHOD_REGEXP_STR}
# Stand-alone method (proceeded by a #)
| \\?\##{METHOD_REGEXP_STR}
# A::B::C
# The stuff after CLASS_REGEXP_STR is a
# nasty hack. CLASS_REGEXP_STR unfortunately matches
# words like dog and cat (these are legal "class"
# names in Fortran 95). When a word is flagged as a
# potential cross-reference, limitations in the markup
# engine suppress other processing, such as typesetting.
# This is particularly noticeable for contractions.
# In order that words like "can't" not
# be flagged as potential cross-references, only
# flag potential class cross-references if the character
# after the cross-referece is a space or sentence
# punctuation.
| #{CLASS_REGEXP_STR}(?=[\s\)\.\?\!\,\;]|\z)
# Things that look like filenames
# The key thing is that there must be at least
# one special character (period, slash, or
# underscore).
| [\/\w]+[_\/\.][\w\/\.]+
# Things that have markup suppressed
| \\[^\s]
)/x
## ##
# We need to record the html path of our caller so we can generate # We need to record the html path of our caller so we can generate
# correct relative paths for any hyperlinks that we find # correct relative paths for any hyperlinks that we find
@ -17,18 +79,7 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
raise ArgumentError, 'from_path cannot be nil' if from_path.nil? raise ArgumentError, 'from_path cannot be nil' if from_path.nil?
super() super()
# class names, variable names, or instance variables @markup.add_special(CROSSREF_REGEXP, :CROSSREF)
@markup.add_special(/(
# A::B.meth(**) (for operator in Fortran95)
\w+(::\w+)*[.\#]\w+(\([\.\w+\*\/\+\-\=\<\>]+\))?
# meth(**) (for operator in Fortran95)
| \#\w+(\([.\w\*\/\+\-\=\<\>]+\))?
| \b([A-Z]\w*(::\w+)*[.\#]\w+) # A::B.meth
| \b([A-Z]\w+(::\w+)*) # A::B
| \#\w+[!?=]? # #meth_name
| \\?\b\w+([_\/\.]+\w+)*[!?=]? # meth_name
)/x,
:CROSSREF)
@from_path = from_path @from_path = from_path
@context = context @context = context
@ -48,6 +99,9 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
def handle_special_CROSSREF(special) def handle_special_CROSSREF(special)
name = special.text name = special.text
# This ensures that words entirely consisting of lowercase letters will
# not have cross-references generated (to suppress lots of
# erroneous cross-references to "new" in text, for instance)
return name if name =~ /\A[a-z]*\z/ return name if name =~ /\A[a-z]*\z/
return @seen[name] if @seen.include? name return @seen[name] if @seen.include? name
@ -70,14 +124,7 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
# (in which case it would match the last pattern, which just checks # (in which case it would match the last pattern, which just checks
# whether the string as a whole is a known symbol). # whether the string as a whole is a known symbol).
if /([A-Z][\w:]*)[.\#](\w+[!?=]?)/ =~ lookup then if /#{CLASS_REGEXP_STR}[\.\#]#{METHOD_REGEXP_STR}/ =~ lookup then
container = $1
method = $2
ref = @context.find_symbol container, method
end
if !ref and
/([A-Za-z][\w:]*)[.\#](\w+(\([\.\w+\*\/\+\-\=\<\>]+\))?)/ =~ lookup then
container = $1 container = $1
method = $2 method = $2
ref = @context.find_symbol container, method ref = @context.find_symbol container, method
@ -99,4 +146,3 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
end end
end end

View File

@ -30,7 +30,7 @@ class RDoc::Markup::ToTexInfo < RDoc::Markup::Formatter
def accept_heading(attributes, text) def accept_heading(attributes, text)
heading = ['@majorheading', '@chapheading'][text.head_level - 1] || '@heading' heading = ['@majorheading', '@chapheading'][text.head_level - 1] || '@heading'
@text << "#{heading}{#{format(text)}}" @text << "#{heading} #{format(text)}"
end end
def accept_list_start(attributes, text) def accept_list_start(attributes, text)

View File

@ -184,7 +184,7 @@ class RDoc::Options
@css = nil @css = nil
@webcvs = nil @webcvs = nil
@charset = 'iso-8859-1' @charset = 'utf-8'
end end
## ##
@ -196,6 +196,7 @@ class RDoc::Options
opts = OptionParser.new do |opt| opts = OptionParser.new do |opt|
opt.program_name = File.basename $0 opt.program_name = File.basename $0
opt.version = RDoc::VERSION opt.version = RDoc::VERSION
opt.release = nil
opt.summary_indent = ' ' * 4 opt.summary_indent = ' ' * 4
opt.banner = <<-EOF opt.banner = <<-EOF
Usage: #{opt.program_name} [options] [names...] Usage: #{opt.program_name} [options] [names...]
@ -257,7 +258,7 @@ Usage: #{opt.program_name} [options] [names...]
opt.separator nil opt.separator nil
opt.on("--charset=CHARSET", "-c", opt.on("--charset=CHARSET", "-c",
"Specifies the HTML character-set.") do |value| "Specifies the output HTML character-set.") do |value|
@charset = value @charset = value
end end
@ -283,9 +284,7 @@ Usage: #{opt.program_name} [options] [names...]
opt.on("--exclude=PATTERN", "-x", Regexp, opt.on("--exclude=PATTERN", "-x", Regexp,
"Do not process files or directories", "Do not process files or directories",
"matching PATTERN. Files given explicitly", "matching PATTERN.") do |value|
"on the command line will never be",
"excluded.") do |value|
@exclude << value @exclude << value
end end

View File

@ -62,11 +62,32 @@ class RDoc::Parser
true true
end end
##
# Shamelessly stolen from the ptools gem (since RDoc cannot depend on
# the gem).
def self.binary?(file)
s = (File.read(file, File.stat(file).blksize) || "").split(//)
((s.size - s.grep(" ".."~").size) / s.size.to_f) > 0.30
end
private_class_method :binary?
## ##
# Return a parser that can handle a particular extension # Return a parser that can handle a particular extension
def self.can_parse(file_name) def self.can_parse(file_name)
RDoc::Parser.parsers.find { |regexp, parser| regexp =~ file_name }.last parser = RDoc::Parser.parsers.find { |regexp,| regexp =~ file_name }.last
#
# The default parser should *NOT* parse binary files.
#
if parser == RDoc::Parser::Simple then
if binary? file_name then
return nil
end
end
return parser
end end
## ##

View File

@ -120,7 +120,7 @@ class RDoc::Parser::C < RDoc::Parser
@stats.add_alias as @stats.add_alias as
end end
end end
def do_classes def do_classes
@content.scan(/(\w+)\s* = \s*rb_define_module\s*\(\s*"(\w+)"\s*\)/mx) do @content.scan(/(\w+)\s* = \s*rb_define_module\s*\(\s*"(\w+)"\s*\)/mx) do
@ -155,7 +155,7 @@ class RDoc::Parser::C < RDoc::Parser
\( \(
\s*(\w+), \s*(\w+),
\s*"(\w+)", \s*"(\w+)",
\s*(\w+)\s* \s*([\w\*\s\(\)\.\->]+)\s* # for SWIG
\s*\)/mx) do |var_name, in_module, class_name, parent| \s*\)/mx) do |var_name, in_module, class_name, parent|
handle_class_module(var_name, "class", class_name, parent, in_module) handle_class_module(var_name, "class", class_name, parent, in_module)
end end
@ -251,7 +251,7 @@ class RDoc::Parser::C < RDoc::Parser
handle_method("method", "rb_mFileTest", meth_name, meth_body, param_count) handle_method("method", "rb_mFileTest", meth_name, meth_body, param_count)
handle_method("singleton_method", "rb_cFile", meth_name, meth_body, param_count) handle_method("singleton_method", "rb_cFile", meth_name, meth_body, param_count)
end end
end end
def find_attr_comment(attr_name) def find_attr_comment(attr_name)
if @content =~ %r{((?>/\*.*?\*/\s+)) if @content =~ %r{((?>/\*.*?\*/\s+))
@ -267,10 +267,10 @@ class RDoc::Parser::C < RDoc::Parser
## ##
# Find the C code corresponding to a Ruby method # Find the C code corresponding to a Ruby method
def find_body(meth_name, meth_obj, body, quiet = false) def find_body(class_name, meth_name, meth_obj, body, quiet = false)
case body case body
when %r"((?>/\*.*?\*/\s*))(?:static\s+)?VALUE\s+#{meth_name} when %r"((?>/\*.*?\*/\s*)*)(?:(?:static|SWIGINTERN)\s+)?(?:intern\s+)?VALUE\s+#{meth_name}
\s*(\([^)]*\))\s*\{.*?^\}"xm \s*(\([^)]*\))([^;]|$)"xm
comment, params = $1, $2 comment, params = $1, $2
body_text = $& body_text = $&
@ -279,9 +279,7 @@ class RDoc::Parser::C < RDoc::Parser
# see if we can find the whole body # see if we can find the whole body
re = Regexp.escape(body_text) + '[^(]*^\{.*?^\}' re = Regexp.escape(body_text) + '[^(]*^\{.*?^\}'
if Regexp.new(re, Regexp::MULTILINE).match(body) body_text = $& if /#{re}/m =~ body
body_text = $&
end
# The comment block may have been overridden with a 'Document-method' # The comment block may have been overridden with a 'Document-method'
# block. This happens in the interpreter when multiple methods are # block. This happens in the interpreter when multiple methods are
@ -289,7 +287,7 @@ class RDoc::Parser::C < RDoc::Parser
# distinct (for example Kernel.hash and Kernel.object_id share the same # distinct (for example Kernel.hash and Kernel.object_id share the same
# implementation # implementation
override_comment = find_override_comment(meth_obj.name) override_comment = find_override_comment(class_name, meth_obj.name)
comment = override_comment if override_comment comment = override_comment if override_comment
find_modifiers(comment, meth_obj) if comment find_modifiers(comment, meth_obj) if comment
@ -300,18 +298,18 @@ class RDoc::Parser::C < RDoc::Parser
meth_obj.comment = mangle_comment(comment) meth_obj.comment = mangle_comment(comment)
when %r{((?>/\*.*?\*/\s*))^\s*\#\s*define\s+#{meth_name}\s+(\w+)}m when %r{((?>/\*.*?\*/\s*))^\s*\#\s*define\s+#{meth_name}\s+(\w+)}m
comment = $1 comment = $1
find_body($2, meth_obj, body, true) find_body(class_name, $2, meth_obj, body, true)
find_modifiers(comment, meth_obj) find_modifiers(comment, meth_obj)
meth_obj.comment = mangle_comment(comment) + meth_obj.comment meth_obj.comment = mangle_comment(comment) + meth_obj.comment
when %r{^\s*\#\s*define\s+#{meth_name}\s+(\w+)}m when %r{^\s*\#\s*define\s+#{meth_name}\s+(\w+)}m
unless find_body($1, meth_obj, body, true) unless find_body(class_name, $1, meth_obj, body, true)
warn "No definition for #{meth_name}" unless @options.quiet warn "No definition for #{meth_name}" unless @options.quiet
return false return false
end end
else else
# No body, but might still have an override comment # No body, but might still have an override comment
comment = find_override_comment(meth_obj.name) comment = find_override_comment(class_name, meth_obj.name)
if comment if comment
find_modifiers(comment, meth_obj) find_modifiers(comment, meth_obj)
@ -367,10 +365,10 @@ class RDoc::Parser::C < RDoc::Parser
def find_class_comment(class_name, class_meth) def find_class_comment(class_name, class_meth)
comment = nil comment = nil
if @content =~ %r{((?>/\*.*?\*/\s+)) if @content =~ %r{((?>/\*.*?\*/\s+))
(static\s+)?void\s+Init_#{class_name}\s*(?:_\(\s*)?\(\s*(?:void\s*)\)}xmi (static\s+)?void\s+Init_#{class_name}\s*(?:_\(\s*)?\(\s*(?:void\s*)\)}xmi then
comment = $1
elsif @content =~ %r{Document-(?:class|module):\s#{class_name}\s*?(?:<\s+[:,\w]+)?\n((?>.*?\*/))}m
comment = $1 comment = $1
elsif @content =~ %r{Document-(class|module):\s#{class_name}\s*?\n((?>.*?\*/))}m
comment = $2
else else
if @content =~ /rb_define_(class|module)/m then if @content =~ /rb_define_(class|module)/m then
class_name = class_name.split("::").last class_name = class_name.split("::").last
@ -424,9 +422,11 @@ class RDoc::Parser::C < RDoc::Parser
end end
end end
def find_override_comment(meth_name) def find_override_comment(class_name, meth_name)
name = Regexp.escape(meth_name) name = Regexp.escape(meth_name)
if @content =~ %r{Document-method:\s#{name}\s*?\n((?>.*?\*/))}m if @content =~ %r{Document-method:\s+#{class_name}(?:\.|::)#{name}\s*?\n((?>.*?\*/))}m then
$1
elsif @content =~ %r{Document-method:\s#{name}\s*?\n((?>.*?\*/))}m then
$1 $1
end end
end end
@ -480,6 +480,10 @@ class RDoc::Parser::C < RDoc::Parser
end end
if class_mod == "class" then if class_mod == "class" then
full_name = enclosure.full_name.to_s + "::#{class_name}"
if @content =~ %r{Document-class:\s+#{full_name}\s*<\s+([:,\w]+)} then
parent_name = $1
end
cm = enclosure.add_class RDoc::NormalClass, class_name, parent_name cm = enclosure.add_class RDoc::NormalClass, class_name, parent_name
@stats.add_class cm @stats.add_class cm
else else
@ -562,18 +566,16 @@ class RDoc::Parser::C < RDoc::Parser
return unless class_name return unless class_name
class_obj = find_class(var_name, class_name) class_obj = find_class var_name, class_name
if class_obj if class_obj then
if meth_name == "initialize" if meth_name == "initialize" then
meth_name = "new" meth_name = "new"
type = "singleton_method" type = "singleton_method"
end end
meth_obj = RDoc::AnyMethod.new("", meth_name)
meth_obj.singleton =
%w{singleton_method module_function}.include?(type)
@stats.add_method meth_obj meth_obj = RDoc::AnyMethod.new '', meth_name
meth_obj.singleton = %w[singleton_method module_function].include? type
p_count = (Integer(param_count) rescue -1) p_count = (Integer(param_count) rescue -1)
@ -585,14 +587,16 @@ class RDoc::Parser::C < RDoc::Parser
meth_obj.params = "(" + (1..p_count).map{|i| "p#{i}"}.join(", ") + ")" meth_obj.params = "(" + (1..p_count).map{|i| "p#{i}"}.join(", ") + ")"
end end
if source_file if source_file then
file_name = File.join(@file_dir, source_file) file_name = File.join(@file_dir, source_file)
body = (@@known_bodies[source_file] ||= File.read(file_name)) body = (@@known_bodies[source_file] ||= File.read(file_name))
else else
body = @content body = @content
end end
if find_body(meth_body, meth_obj, body) and meth_obj.document_self
class_obj.add_method(meth_obj) if find_body(class_name, meth_body, meth_obj, body) and meth_obj.document_self then
class_obj.add_method meth_obj
@stats.add_method meth_obj
end end
end end
end end
@ -628,8 +632,8 @@ class RDoc::Parser::C < RDoc::Parser
end end
def remove_private_comments(comment) def remove_private_comments(comment)
comment.gsub!(/\/?\*--(.*?)\/?\*\+\+/m, '') comment.gsub!(/\/?\*--\n(.*?)\/?\*\+\+/m, '')
comment.sub!(/\/?\*--.*/m, '') comment.sub!(/\/?\*--\n.*/m, '')
end end
## ##

165
lib/rdoc/parser/perl.rb Normal file
View File

@ -0,0 +1,165 @@
require 'rdoc/parser'
##
#
# This is an attamept to write a basic parser for Perl's
# POD (Plain old Documentation) format. Ruby code must
# co-exist with Perl, and some tasks are easier in Perl
# than Ruby because of existing libraries.
#
# One difficult is that Perl POD has no means of identifying
# the classes (packages) and methods (subs) with which it
# is associated, it is more like literate programming in so
# far as it just happens to be in the same place as the code,
# but need not be.
#
# We would like to support all the markup the POD provides
# so that it will convert happily to HTML. At the moment
# I don't think I can do that: time constraints.
#
class RDoc::Parser::PerlPOD < RDoc::Parser
parse_files_matching(/.p[lm]$/)
##
# Prepare to parse a perl file
def initialize(top_level, file_name, content, options, stats)
super
preprocess = RDoc::Markup::PreProcess.new @file_name, @options.rdoc_include
preprocess.handle @content do |directive, param|
warn "Unrecognized directive '#{directive}' in #{@file_name}"
end
end
##
# Extract the Pod(-like) comments from the code.
# At its most basic there will ne no need to distinguish
# between the different types of header, etc.
#
# This uses a simple finite state machine, in a very
# procedural pattern. I could "replace case with polymorphism"
# but I think it would obscure the intent, scatter the
# code all over tha place. This machine is necessary
# because POD requires that directives be preceded by
# blank lines, so reading line by line is necessary,
# and preserving state about what is seen is necesary.
def scan
@top_level.comment ||= ""
state=:code_blank
line_number = 0
line = nil
# This started out as a really long nested case statement,
# which also led to repetitive code. I'd like to avoid that
# so I'm using a "table" instead.
# Firstly we need some procs to do the transition and processing
# work. Because these are procs they are closures, and they can
# use variables in the local scope.
#
# First, the "nothing to see here" stuff.
code_noop = lambda do
if line =~ /^\s+$/
state = :code_blank
end
end
pod_noop = lambda do
if line =~ /^\s+$/
state = :pod_blank
end
@top_level.comment += filter(line)
end
begin_noop = lambda do
if line =~ /^\s+$/
state = :begin_blank
end
@top_level.comment += filter(line)
end
# Now for the blocks that process code and comments...
transit_to_pod = lambda do
case line
when /^=(?:pod|head\d+)/
state = :pod_no_blank
@top_level.comment += filter(line)
when /^=over/
state = :over_no_blank
@top_level.comment += filter(line)
when /^=(?:begin|for)/
state = :begin_no_blank
end
end
process_pod = lambda do
case line
when /^\s*$/
state = :pod_blank
@top_level.comment += filter(line)
when /^=cut/
state = :code_no_blank
when /^=end/
$stderr.puts "'=end' unexpected at #{line_number} in #{@file_name}"
else
@top_level.comment += filter(line)
end
end
process_begin = lambda do
case line
when /^\s*$/
state = :begin_blank
@top_level.comment += filter(line)
when /^=end/
state = :code_no_blank
when /^=cut/
$stderr.puts "'=cut' unexpected at #{line_number} in #{@file_name}"
else
@top_level.comment += filter(line)
end
end
transitions = { :code_no_blank => code_noop,
:code_blank => transit_to_pod,
:pod_no_blank => pod_noop,
:pod_blank => process_pod,
:begin_no_blank => begin_noop,
:begin_blank => process_begin}
@content.each_line do |l|
line = l
line_number += 1
transitions[state].call
end # each line
@top_level
end
# Filter the perl markup that does the same as the rdoc
# filtering. Only basic for now. Will probably need a
# proper parser to cope with C<<...>> etc
def filter(comment)
return '' if comment =~ /^=pod\s*$/
comment.gsub!(/^=pod/, '==')
comment.gsub!(/^=head(\d+)/) do
"=" * $1.to_i
end
comment.gsub!(/=item/, '');
comment.gsub!(/C<(.*?)>/, '<tt>\1</tt>');
comment.gsub!(/I<(.*?)>/, '<i>\1</i>');
comment.gsub!(/B<(.*?)>/, '<b>\1</b>');
comment
end
end

View File

@ -134,7 +134,7 @@ module RDoc::RubyToken
TokenDefinitions = [ TokenDefinitions = [
[:TkCLASS, TkKW, "class", EXPR_CLASS], [:TkCLASS, TkKW, "class", EXPR_CLASS],
[:TkMODULE, TkKW, "module", EXPR_BEG], [:TkMODULE, TkKW, "module", EXPR_CLASS],
[:TkDEF, TkKW, "def", EXPR_FNAME], [:TkDEF, TkKW, "def", EXPR_FNAME],
[:TkUNDEF, TkKW, "undef", EXPR_FNAME], [:TkUNDEF, TkKW, "undef", EXPR_FNAME],
[:TkBEGIN, TkKW, "begin", EXPR_BEG], [:TkBEGIN, TkKW, "begin", EXPR_BEG],
@ -1945,9 +1945,9 @@ class RDoc::Parser::Ruby < RDoc::Parser
case tk case tk
when TkSEMICOLON when TkSEMICOLON
break break
when TkLPAREN, TkfLPAREN when TkLPAREN, TkfLPAREN, TkLBRACE, TkLBRACK, TkDO
nest += 1 nest += 1
when TkRPAREN when TkRPAREN, TkRBRACE, TkRBRACK, TkEND
nest -= 1 nest -= 1
when TkCOMMENT when TkCOMMENT
if nest <= 0 && @scanner.lex_state == EXPR_END if nest <= 0 && @scanner.lex_state == EXPR_END
@ -1955,7 +1955,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
break break
end end
when TkNL when TkNL
if (@scanner.lex_state == EXPR_END and nest <= 0) || !@scanner.continue if (nest <= 0) && ((@scanner.lex_state == EXPR_END) || (!@scanner.continue))
unget_tk(tk) unget_tk(tk)
break break
end end
@ -2683,8 +2683,8 @@ class RDoc::Parser::Ruby < RDoc::Parser
end end
def remove_private_comments(comment) def remove_private_comments(comment)
comment.gsub!(/^#--.*?^#\+\+/m, '') comment.gsub!(/^#--\n.*?^#\+\+/m, '')
comment.sub!(/^#--.*/m, '') comment.sub!(/^#--\n.*/m, '')
end end
def remove_token_listener(obj) def remove_token_listener(obj)

View File

@ -31,7 +31,7 @@ class RDoc::Parser::Simple < RDoc::Parser
end end
def remove_private_comments(comment) def remove_private_comments(comment)
comment.gsub(/^--[^-].*?^\+\+/m, '').sub(/^--.*/m, '') comment.gsub(/^--\n.*?^\+\+/m, '').sub(/^--\n.*/m, '')
end end
end end

View File

@ -7,6 +7,7 @@ require 'rdoc/parser/simple'
require 'rdoc/parser/ruby' require 'rdoc/parser/ruby'
require 'rdoc/parser/c' require 'rdoc/parser/c'
require 'rdoc/parser/f95' require 'rdoc/parser/f95'
require 'rdoc/parser/perl'
require 'rdoc/stats' require 'rdoc/stats'
require 'rdoc/options' require 'rdoc/options'
@ -189,11 +190,11 @@ module RDoc
def parse_files(options) def parse_files(options)
@stats = Stats.new options.verbosity @stats = Stats.new options.verbosity
files = options.files files = options.files
files = ["."] if files.empty? files = ["."] if files.empty?
file_list = normalized_file_list(options, files, true) file_list = normalized_file_list(options, files, true, options.exclude)
return [] if file_list.empty? return [] if file_list.empty?
@ -288,6 +289,5 @@ module RDoc
end end
end end
end end
end end

View File

@ -14,7 +14,7 @@ class RDoc::RI::ClassEntry
@inferior_classes = [] @inferior_classes = []
end end
# We found this class in more tha one place, so add # We found this class in more than one place, so add
# in the name from there. # in the name from there.
def add_path(path) def add_path(path)
@path_names << path @path_names << path
@ -37,10 +37,10 @@ class RDoc::RI::ClassEntry
if name =~ /^(.*?)-(c|i).yaml$/ if name =~ /^(.*?)-(c|i).yaml$/
external_name = $1 external_name = $1
is_class_method = $2 == "c" is_class_method = $2 == "c"
internal_name = RiWriter.external_to_internal(external_name) internal_name = RDoc::RI::Writer.external_to_internal(external_name)
list = is_class_method ? @class_methods : @instance_methods list = is_class_method ? @class_methods : @instance_methods
path = File.join(dir, name) path = File.join(dir, name)
list << MethodEntry.new(path, internal_name, is_class_method, self) list << RDoc::RI::MethodEntry.new(path, internal_name, is_class_method, self)
else else
full_name = File.join(dir, name) full_name = File.join(dir, name)
if File.directory?(full_name) if File.directory?(full_name)
@ -48,7 +48,7 @@ class RDoc::RI::ClassEntry
if inf_class if inf_class
inf_class.add_path(full_name) inf_class.add_path(full_name)
else else
inf_class = ClassEntry.new(full_name, name, self) inf_class = RDoc::RI::ClassEntry.new(full_name, name, self)
@inferior_classes << inf_class @inferior_classes << inf_class
end end
inf_class.load_from(full_name) inf_class.load_from(full_name)
@ -168,7 +168,7 @@ class RDoc::RI::MethodEntry
end end
## ##
# We represent everything know about all 'ri' files accessible to this program # We represent everything known about all 'ri' files accessible to this program
class RDoc::RI::Cache class RDoc::RI::Cache
@ -185,4 +185,3 @@ class RDoc::RI::Cache
end end
end end

View File

@ -77,7 +77,9 @@ end
class RDoc::RI::ModuleDescription < RDoc::RI::Description class RDoc::RI::ModuleDescription < RDoc::RI::Description
attr_accessor :class_methods attr_accessor :class_methods
attr_accessor :class_method_extensions
attr_accessor :instance_methods attr_accessor :instance_methods
attr_accessor :instance_method_extensions
attr_accessor :attributes attr_accessor :attributes
attr_accessor :constants attr_accessor :constants
attr_accessor :includes attr_accessor :includes
@ -148,6 +150,7 @@ class RDoc::RI::MethodDescription < RDoc::RI::Description
attr_accessor :aliases attr_accessor :aliases
attr_accessor :is_alias_for attr_accessor :is_alias_for
attr_accessor :params attr_accessor :params
attr_accessor :source_path
end end

View File

@ -1,5 +1,15 @@
require 'rdoc/ri' require 'rdoc/ri'
# readline support might not be present, so be careful
# when requiring it.
begin
require('readline')
require('abbrev')
CAN_USE_READLINE = true
rescue
CAN_USE_READLINE = false
end
## ##
# This is a kind of 'flag' module. If you want to write your own 'ri' display # This is a kind of 'flag' module. If you want to write your own 'ri' display
# module (perhaps because you're writing an IDE), you write a class which # module (perhaps because you're writing an IDE), you write a class which
@ -41,7 +51,7 @@ class RDoc::RI::DefaultDisplay
# Display information about +klass+. Fetches additional information from # Display information about +klass+. Fetches additional information from
# +ri_reader+ as necessary. # +ri_reader+ as necessary.
def display_class_info(klass, ri_reader) def display_class_info(klass)
page do page do
superclass = klass.superclass_string superclass = klass.superclass_string
@ -61,17 +71,11 @@ class RDoc::RI::DefaultDisplay
@formatter.blankline @formatter.blankline
@formatter.display_heading("Includes:", 2, "") @formatter.display_heading("Includes:", 2, "")
incs = [] incs = []
klass.includes.each do |inc| klass.includes.each do |inc|
inc_desc = ri_reader.find_class_by_name(inc.name) incs << inc.name
if inc_desc end
str = inc.name + "("
str << inc_desc.instance_methods.map{|m| m.name}.join(", ")
str << ")"
incs << str
else
incs << inc.name
end
end
@formatter.wrap(incs.sort.join(', ')) @formatter.wrap(incs.sort.join(', '))
end end
@ -82,42 +86,19 @@ class RDoc::RI::DefaultDisplay
constants = klass.constants.sort_by { |constant| constant.name } constants = klass.constants.sort_by { |constant| constant.name }
constants.each do |constant| constants.each do |constant|
@formatter.wrap "#{constant.name} = #{constant.value}"
if constant.comment then if constant.comment then
@formatter.wrap "#{constant.name}:"
@formatter.indent do @formatter.indent do
@formatter.display_flow constant.comment @formatter.display_flow constant.comment
end end
else else
@formatter.wrap constant.name @formatter.break_to_newline
end end
end end
end end
class_data = [
:class_methods,
:class_method_extensions,
:instance_methods,
:instance_method_extensions,
]
class_data.each do |data_type|
data = klass.send data_type
unless data.empty? then
@formatter.blankline
heading = data_type.to_s.split('_').join(' ').capitalize << ':'
@formatter.display_heading heading, 2, ''
data = data.map { |item| item.name }.sort.join ', '
@formatter.wrap data
end
end
unless klass.attributes.empty? then unless klass.attributes.empty? then
@formatter.blankline @formatter.blankline
@formatter.display_heading 'Attributes:', 2, '' @formatter.display_heading 'Attributes:', 2, ''
attributes = klass.attributes.sort_by { |attribute| attribute.name } attributes = klass.attributes.sort_by { |attribute| attribute.name }
@ -130,11 +111,119 @@ class RDoc::RI::DefaultDisplay
end end
else else
@formatter.wrap "#{attribute.name} (#{attribute.rw})" @formatter.wrap "#{attribute.name} (#{attribute.rw})"
@formatter.break_to_newline
end end
end end
end end
return display_class_method_list(klass)
end end
end end
##
# Given a Hash mapping a class' methods to method types (returned by
# display_class_method_list), this method allows the user to
# choose one of the methods.
def get_class_method_choice(method_map)
if CAN_USE_READLINE
# prepare abbreviations for tab completion
abbreviations = method_map.keys.abbrev
Readline.completion_proc = proc do |string|
abbreviations.values.uniq.grep(/^#{string}/)
end
end
@formatter.raw_print_line "\nEnter the method name you want.\n"
@formatter.raw_print_line "Class methods can be preceeded by '::' and instance methods by '#'.\n"
if CAN_USE_READLINE
@formatter.raw_print_line "You can use tab to autocomplete.\n"
@formatter.raw_print_line "Enter a blank line to exit.\n"
choice_string = Readline.readline(">> ").strip
else
@formatter.raw_print_line "Enter a blank line to exit.\n"
@formatter.raw_print_line ">> "
choice_string = $stdin.gets.strip
end
if choice_string == ''
return nil
else
class_or_instance = method_map[choice_string]
if class_or_instance
# If the user's choice is not preceeded by a '::' or a '#', figure
# out whether they want a class or an instance method and decorate
# the choice appropriately.
if(choice_string =~ /^[a-zA-Z]/)
if(class_or_instance == :class)
choice_string = "::#{choice_string}"
else
choice_string = "##{choice_string}"
end
end
return choice_string
else
@formatter.raw_print_line "No method matched '#{choice_string}'.\n"
return nil
end
end
end
##
# Display methods on +klass+
# Returns a hash mapping method name to method contents (HACK?)
def display_class_method_list(klass)
method_map = {}
class_data = [
:class_methods,
:class_method_extensions,
:instance_methods,
:instance_method_extensions,
]
class_data.each do |data_type|
data = klass.send data_type
unless data.nil? or data.empty? then
@formatter.blankline
heading = data_type.to_s.split('_').join(' ').capitalize << ':'
@formatter.display_heading heading, 2, ''
method_names = []
data.each do |item|
method_names << item.name
if(data_type == :class_methods ||
data_type == :class_method_extensions) then
method_map["::#{item.name}"] = :class
method_map[item.name] = :class
else
#
# Since we iterate over instance methods after class methods,
# an instance method always will overwrite the unqualified
# class method entry for a class method of the same name.
#
method_map["##{item.name}"] = :instance
method_map[item.name] = :instance
end
end
method_names.sort!
@formatter.wrap method_names.join(',')
end
end
method_map
end
private :display_class_method_list
## ##
# Display an Array of RDoc::Markup::Flow objects, +flow+. # Display an Array of RDoc::Markup::Flow objects, +flow+.
@ -172,10 +261,42 @@ class RDoc::RI::DefaultDisplay
def display_method_list(methods) def display_method_list(methods)
page do page do
@formatter.wrap "More than one method matched your request. You can refine your search by asking for information on one of:" @formatter.wrap "More than one method matched your request. You can refine your search by asking for information on one of:"
@formatter.blankline @formatter.blankline
@formatter.wrap methods.map { |m| m.full_name }.join(", ") methods.each do |method|
@formatter.raw_print_line "#{method.full_name} [#{method.source_path}]\n"
end
end
end
##
# Display a list of +methods+ and allow the user to select one of them.
def display_method_list_choice(methods)
page do
@formatter.wrap "More than one method matched your request. Please choose one of the possible matches."
@formatter.blankline
methods.each_with_index do |method, index|
@formatter.raw_print_line "%3d %s [%s]\n" % [index + 1, method.full_name, method.source_path]
end
@formatter.raw_print_line ">> "
choice = $stdin.gets.strip!
if(choice == '')
return
end
choice = choice.to_i
if ((choice == 0) || (choice > methods.size)) then
@formatter.raw_print_line "Invalid choice!\n"
else
method = methods[choice - 1]
display_method_info(method)
end
end end
end end
@ -198,10 +319,8 @@ class RDoc::RI::DefaultDisplay
@formatter.break_to_newline @formatter.break_to_newline
end end
if method.source_path then @formatter.blankline
@formatter.blankline @formatter.wrap("From #{method.source_path}")
@formatter.wrap("Extension from #{method.source_path}")
end
end end
## ##

View File

@ -11,29 +11,33 @@ require 'rdoc/markup/to_flow'
class RDoc::RI::Driver class RDoc::RI::Driver
class Hash < ::Hash #
def self.convert(hash) # This class offers both Hash and OpenStruct functionality.
hash = new.update hash # We convert from the Core Hash to this before calling any of
# the display methods, in order to give the display methods
# a cleaner API for accessing the data.
#
class OpenStructHash < Hash
#
# This method converts from a Hash to an OpenStructHash.
#
def self.convert(object)
case object
when Hash then
new_hash = new # Convert Hash -> OpenStructHash
hash.each do |key, value| object.each do |key, value|
hash[key] = case value new_hash[key] = convert(value)
when ::Hash then end
convert value
when Array then new_hash
value = value.map do |v| when Array then
::Hash === v ? convert(v) : v object.map do |element|
end convert(element)
value end
else else
value object
end
end end
hash
end
def method_missing method, *args
self[method.to_s]
end end
def merge_enums(other) def merge_enums(other)
@ -57,6 +61,10 @@ class RDoc::RI::Driver
end end
end end
end end
def method_missing method, *args
self[method.to_s]
end
end end
class Error < RDoc::RI::Error; end class Error < RDoc::RI::Error; end
@ -69,25 +77,31 @@ class RDoc::RI::Driver
attr_accessor :homepath # :nodoc: attr_accessor :homepath # :nodoc:
def self.process_args(argv) def self.default_options
options = {} options = {}
options[:use_stdout] = !$stdout.tty? options[:use_stdout] = !$stdout.tty?
options[:width] = 72 options[:width] = 72
options[:formatter] = RDoc::RI::Formatter.for 'plain' options[:formatter] = RDoc::RI::Formatter.for 'plain'
options[:list_classes] = false options[:interactive] = false
options[:list_names] = false options[:use_cache] = true
# By default all paths are used. If any of these are true, only those # By default all standard paths are used.
# directories are used. options[:use_system] = true
use_system = false options[:use_site] = true
use_site = false options[:use_home] = true
use_home = false options[:use_gems] = true
use_gems = false options[:extra_doc_dirs] = []
doc_dirs = []
return options
end
def self.process_args(argv)
options = default_options
opts = OptionParser.new do |opt| opts = OptionParser.new do |opt|
opt.program_name = File.basename $0 opt.program_name = File.basename $0
opt.version = RDoc::VERSION opt.version = RDoc::VERSION
opt.release = nil
opt.summary_indent = ' ' * 4 opt.summary_indent = ' ' * 4
directories = [ directories = [
@ -142,30 +156,6 @@ Options may also be set in the 'RI' environment variable.
opt.separator "Options:" opt.separator "Options:"
opt.separator nil opt.separator nil
opt.on("--classes", "-c",
"Display the names of classes and modules we",
"know about.") do |value|
options[:list_classes] = value
end
opt.separator nil
opt.on("--doc-dir=DIRNAME", "-d", Array,
"List of directories to search for",
"documentation. If not specified, we search",
"the standard rdoc/ri directories. May be",
"repeated.") do |value|
value.each do |dir|
unless File.directory? dir then
raise OptionParser::InvalidArgument, "#{dir} is not a directory"
end
end
doc_dirs.concat value
end
opt.separator nil
opt.on("--fmt=FORMAT", "--format=FORMAT", "-f", opt.on("--fmt=FORMAT", "--format=FORMAT", "-f",
RDoc::RI::Formatter::FORMATTERS.keys, RDoc::RI::Formatter::FORMATTERS.keys,
"Format to use when displaying output:", "Format to use when displaying output:",
@ -179,49 +169,101 @@ Options may also be set in the 'RI' environment variable.
opt.separator nil opt.separator nil
unless RDoc::RI::Paths::GEMDIRS.empty? then opt.on("--doc-dir=DIRNAME", "-d", Array,
opt.on("--[no-]gems", "List of directories from which to source",
"Include documentation from RubyGems.") do |value| "documentation in addition to the standard",
use_gems = value "directories. May be repeated.") do |value|
value.each do |dir|
unless File.directory? dir then
raise OptionParser::InvalidArgument, "#{dir} is not a directory"
end
options[:extra_doc_dirs] << File.expand_path(dir)
end end
end end
opt.separator nil opt.separator nil
opt.on("--[no-]home", opt.on("--[no-]use-cache",
"Include documentation stored in ~/.rdoc.") do |value| "Whether or not to use ri's cache.",
use_home = value "True by default.") do |value|
options[:use_cache] = value
end end
opt.separator nil opt.separator nil
opt.on("--[no-]list-names", "-l", opt.on("--no-standard-docs",
"List all the names known to RDoc, one per", "Do not include documentation from",
"line.") do |value| "the Ruby standard library, site_lib,",
options[:list_names] = value "installed gems, or ~/.rdoc.",
end "Equivalent to specifying",
"the options --no-system, --no-site, --no-gems,",
opt.separator nil "and --no-home") do
options[:use_system] = false
opt.on("--no-pager", "-T", options[:use_site] = false
"Send output directly to stdout.") do |value| options[:use_gems] = false
options[:use_stdout] = !value options[:use_home] = false
end
opt.separator nil
opt.on("--[no-]site",
"Include documentation from libraries",
"installed in site_lib.") do |value|
use_site = value
end end
opt.separator nil opt.separator nil
opt.on("--[no-]system", opt.on("--[no-]system",
"Include documentation from Ruby's standard", "Include documentation from Ruby's standard",
"library.") do |value| "library. Defaults to true.") do |value|
use_system = value options[:use_system] = value
end
opt.separator nil
opt.on("--[no-]site",
"Include documentation from libraries",
"installed in site_lib.",
"Defaults to true.") do |value|
options[:use_site] = value
end
opt.separator nil
opt.on("--[no-]gems",
"Include documentation from RubyGems.",
"Defaults to true.") do |value|
options[:use_gems] = value
end
opt.separator nil
opt.on("--[no-]home",
"Include documentation stored in ~/.rdoc.",
"Defaults to true.") do |value|
options[:use_home] = value
end
opt.separator nil
opt.on("--list-doc-dirs",
"List the directories from which ri will",
"source documentation on stdout and exit.") do
options[:list_doc_dirs] = true
end
opt.separator nil
opt.on("--no-pager", "-T",
"Send output directly to stdout,",
"rather than to a pager.") do
options[:use_stdout] = true
end
opt.on("--interactive", "-i",
"This makes ri go into interactive mode.",
"When ri is in interactive mode it will",
"allow the user to disambiguate lists of",
"methods in case multiple methods match",
"against a method search string. It also",
"will allow the user to enter in a method",
"name (with auto-completion, if readline",
"is supported) when viewing a class.") do
options[:interactive] = true
end end
opt.separator nil opt.separator nil
@ -238,10 +280,10 @@ Options may also be set in the 'RI' environment variable.
options[:names] = argv options[:names] = argv
options[:path] = RDoc::RI::Paths.path(use_system, use_site, use_home, options[:formatter] ||= RDoc::RI::Formatter.for('plain')
use_gems, *doc_dirs) options[:use_stdout] ||= !$stdout.tty?
options[:raw_path] = RDoc::RI::Paths.raw_path(use_system, use_site, options[:use_stdout] ||= options[:interactive]
use_home, use_gems, *doc_dirs) options[:width] ||= 72
options options
@ -258,22 +300,30 @@ Options may also be set in the 'RI' environment variable.
ri.run ri.run
end end
def initialize(options={}) def initialize(initial_options={})
options[:formatter] ||= RDoc::RI::Formatter.for('plain') options = self.class.default_options.update(initial_options)
options[:use_stdout] ||= !$stdout.tty?
options[:width] ||= 72
@names = options[:names]
@names = options[:names]
@class_cache_name = 'classes' @class_cache_name = 'classes'
@all_dirs = RDoc::RI::Paths.path(true, true, true, true)
@doc_dirs = RDoc::RI::Paths.path(options[:use_system],
options[:use_site],
options[:use_home],
options[:use_gems],
options[:extra_doc_dirs])
@homepath = RDoc::RI::Paths.raw_path(false, false, true, false).first @homepath = RDoc::RI::Paths.raw_path(false, false, true, false).first
@homepath = @homepath.sub(/\.rdoc/, '.ri') @homepath = @homepath.sub(/\.rdoc/, '.ri')
@sys_dirs = RDoc::RI::Paths.raw_path(true, false, false, false) @sys_dir = RDoc::RI::Paths.raw_path(true, false, false, false).first
@list_doc_dirs = options[:list_doc_dirs]
FileUtils.mkdir_p cache_file_path unless File.directory? cache_file_path FileUtils.mkdir_p cache_file_path unless File.directory? cache_file_path
@cache_doc_dirs_path = File.join cache_file_path, ".doc_dirs"
@use_cache = options[:use_cache]
@class_cache = nil @class_cache = nil
@interactive = options[:interactive]
@display = RDoc::RI::DefaultDisplay.new(options[:formatter], @display = RDoc::RI::DefaultDisplay.new(options[:formatter],
options[:width], options[:width],
options[:use_stdout]) options[:use_stdout])
@ -282,158 +332,67 @@ Options may also be set in the 'RI' environment variable.
def class_cache def class_cache
return @class_cache if @class_cache return @class_cache if @class_cache
newest = map_dirs('created.rid', :all) do |f| # Get the documentation directories used to make the cache in order to see
# whether the cache is valid for the current ri instantiation.
if(File.readable?(@cache_doc_dirs_path))
cache_doc_dirs = IO.read(@cache_doc_dirs_path).split("\n")
else
cache_doc_dirs = []
end
newest = map_dirs('created.rid') do |f|
File.mtime f if test ?f, f File.mtime f if test ?f, f
end.max end.max
# An up to date cache file must have been created more recently than
# the last modification of any of the documentation directories. It also
# must have been created with the same documentation directories
# as those from which ri currently is sourcing documentation.
up_to_date = (File.exist?(class_cache_file_path) and up_to_date = (File.exist?(class_cache_file_path) and
newest and newest < File.mtime(class_cache_file_path)) newest and newest < File.mtime(class_cache_file_path) and
(cache_doc_dirs == @doc_dirs))
@class_cache = if up_to_date then if up_to_date and @use_cache then
load_cache_for @class_cache_name open class_cache_file_path, 'rb' do |fp|
else begin
class_cache = RDoc::RI::Driver::Hash.new @class_cache = Marshal.load fp.read
rescue
#
# This shouldn't be necessary, since the up_to_date logic above
# should force the cache to be recreated when a new version of
# rdoc is installed. This seems like a worthwhile enhancement
# to ri's robustness, however.
#
$stderr.puts "Error reading the class cache; recreating the class cache!"
@class_cache = create_class_cache
end
end
else
@class_cache = create_class_cache
end
classes = map_dirs('**/cdesc*.yaml', :sys) { |f| Dir[f] }
populate_class_cache class_cache, classes
classes = map_dirs('**/cdesc*.yaml') { |f| Dir[f] }
warn "Updating class cache with #{classes.size} classes..."
populate_class_cache class_cache, classes, true
write_cache class_cache, class_cache_file_path
end
@class_cache = RDoc::RI::Driver::Hash.convert @class_cache
@class_cache @class_cache
end end
def class_cache_file_path def create_class_cache
File.join cache_file_path, @class_cache_name class_cache = OpenStructHash.new
end
def cache_file_for(klassname) if(@use_cache)
File.join cache_file_path, klassname.gsub(/:+/, "-") # Dump the documentation directories to a file in the cache, so that
end # we only will use the cache for future instantiations with identical
# documentation directories.
def cache_file_path File.open @cache_doc_dirs_path, "wb" do |fp|
File.join @homepath, 'cache' fp << @doc_dirs.join("\n")
end
def display_class(name)
klass = class_cache[name]
klass = RDoc::RI::Driver::Hash.convert klass
@display.display_class_info klass, class_cache
end
def get_info_for(arg)
@names = [arg]
run
end
def load_cache_for(klassname)
path = cache_file_for klassname
cache = nil
if File.exist? path and
File.mtime(path) >= File.mtime(class_cache_file_path) then
open path, 'rb' do |fp|
cache = Marshal.load fp.read
end end
else
class_cache = nil
open class_cache_file_path, 'rb' do |fp|
class_cache = Marshal.load fp.read
end
klass = class_cache[klassname]
return nil unless klass
method_files = klass["sources"]
cache = RDoc::RI::Driver::Hash.new
sys_dir = @sys_dirs.first
method_files.each do |f|
system_file = f.index(sys_dir) == 0
Dir[File.join(File.dirname(f), "*")].each do |yaml|
next unless yaml =~ /yaml$/
next if yaml =~ /cdesc-[^\/]+yaml$/
method = read_yaml yaml
name = method["full_name"]
ext_path = f
ext_path = "gem #{$1}" if f =~ %r%gems/[\d.]+/doc/([^/]+)%
method["source_path"] = ext_path unless system_file
cache[name] = RDoc::RI::Driver::Hash.convert method
end
end
write_cache cache, path
end end
RDoc::RI::Driver::Hash.convert cache classes = map_dirs('**/cdesc*.yaml') { |f| Dir[f] }
end warn "Updating class cache with #{classes.size} classes..."
populate_class_cache class_cache, classes
## write_cache class_cache, class_cache_file_path
# Finds the next ancestor of +orig_klass+ after +klass+.
def lookup_ancestor(klass, orig_klass) class_cache
cache = class_cache[orig_klass]
return nil unless cache
ancestors = [orig_klass]
ancestors.push(*cache.includes.map { |inc| inc['name'] })
ancestors << cache.superclass
ancestor = ancestors[ancestors.index(klass) + 1]
return ancestor if ancestor
lookup_ancestor klass, cache.superclass
end
##
# Finds the method
def lookup_method(name, klass)
cache = load_cache_for klass
return nil unless cache
method = cache[name.gsub('.', '#')]
method = cache[name.gsub('.', '::')] unless method
method
end
def map_dirs(file_name, system=false)
dirs = if system == :all then
@all_dirs
else
if system then
@sys_dirs
else
@all_dirs - @sys_dirs
end
end
dirs.map { |dir| yield File.join(dir, file_name) }.flatten.compact
end
##
# Extract the class and method name parts from +name+ like Foo::Bar#baz
def parse_name(name)
parts = name.split(/(::|\#|\.)/)
if parts[-2] != '::' or parts.last !~ /^[A-Z]/ then
meth = parts.pop
parts.pop
end
klass = parts.join
[klass, meth]
end end
def populate_class_cache(class_cache, classes, extension = false) def populate_class_cache(class_cache, classes, extension = false)
@ -455,64 +414,222 @@ Options may also be set in the 'RI' environment variable.
desc["class_method_extensions"] = desc.delete "class_methods" desc["class_method_extensions"] = desc.delete "class_methods"
end end
klass = RDoc::RI::Driver::Hash.convert klass
klass.merge_enums desc klass.merge_enums desc
klass["sources"] << cdesc klass["sources"] << cdesc
end end
end end
end end
def class_cache_file_path
File.join cache_file_path, @class_cache_name
end
def cache_file_for(klassname)
File.join cache_file_path, klassname.gsub(/:+/, "-")
end
def cache_file_path
File.join @homepath, 'cache'
end
def display_class(name)
klass = class_cache[name]
@display.display_class_info klass
end
def display_method(method)
@display.display_method_info method
end
def get_info_for(arg)
@names = [arg]
run
end
def load_cache_for(klassname)
path = cache_file_for klassname
cache = nil
if File.exist? path and
File.mtime(path) >= File.mtime(class_cache_file_path) and
@use_cache then
open path, 'rb' do |fp|
begin
cache = Marshal.load fp.read
rescue
#
# The cache somehow is bad. Recreate the cache.
#
$stderr.puts "Error reading the cache for #{klassname}; recreating the cache!"
cache = create_cache_for klassname, path
end
end
else
cache = create_cache_for klassname, path
end
cache
end
def create_cache_for(klassname, path)
klass = class_cache[klassname]
return nil unless klass
method_files = klass["sources"]
cache = OpenStructHash.new
method_files.each do |f|
system_file = f.index(@sys_dir) == 0
Dir[File.join(File.dirname(f), "*")].each do |yaml|
next unless yaml =~ /yaml$/
next if yaml =~ /cdesc-[^\/]+yaml$/
method = read_yaml yaml
if system_file then
method["source_path"] = "Ruby #{RDoc::RI::Paths::VERSION}"
else
if(f =~ %r%gems/[\d.]+/doc/([^/]+)%) then
ext_path = "gem #{$1}"
else
ext_path = f
end
method["source_path"] = ext_path
end
name = method["full_name"]
cache[name] = method
end
end
write_cache cache, path
end
##
# Finds the next ancestor of +orig_klass+ after +klass+.
def lookup_ancestor(klass, orig_klass)
# This is a bit hacky, but ri will go into an infinite
# loop otherwise, since Object has an Object ancestor
# for some reason. Depending on the documentation state, I've seen
# Kernel as an ancestor of Object and not as an ancestor of Object.
if ((orig_klass == "Object") &&
((klass == "Kernel") || (klass == "Object")))
return nil
end
cache = class_cache[orig_klass]
return nil unless cache
ancestors = [orig_klass]
ancestors.push(*cache.includes.map { |inc| inc['name'] })
ancestors << cache.superclass
ancestor_index = ancestors.index(klass)
if ancestor_index
ancestor = ancestors[ancestors.index(klass) + 1]
return ancestor if ancestor
end
lookup_ancestor klass, cache.superclass
end
##
# Finds the method
def lookup_method(name, klass)
cache = load_cache_for klass
return nil unless cache
method = cache[name.gsub('.', '#')]
method = cache[name.gsub('.', '::')] unless method
method
end
def map_dirs(file_name)
@doc_dirs.map { |dir| yield File.join(dir, file_name) }.flatten.compact
end
##
# Extract the class and method name parts from +name+ like Foo::Bar#baz
def parse_name(name)
parts = name.split(/(::|\#|\.)/)
if parts[-2] != '::' or parts.last !~ /^[A-Z]/ then
meth = parts.pop
parts.pop
end
klass = parts.join
[klass, meth]
end
def read_yaml(path) def read_yaml(path)
data = File.read path data = File.read path
# Necessary to be backward-compatible with documentation generated
# by earliar RDoc versions.
data = data.gsub(/ \!ruby\/(object|struct):(RDoc::RI|RI).*/, '') data = data.gsub(/ \!ruby\/(object|struct):(RDoc::RI|RI).*/, '')
data = data.gsub(/ \!ruby\/(object|struct):SM::(\S+)/, data = data.gsub(/ \!ruby\/(object|struct):SM::(\S+)/,
' !ruby/\1:RDoc::Markup::\2') ' !ruby/\1:RDoc::Markup::\2')
YAML.load data OpenStructHash.convert(YAML.load(data))
end end
def run def run
if @names.empty? then if(@list_doc_dirs)
puts @doc_dirs.join("\n")
elsif @names.empty? then
@display.list_known_classes class_cache.keys.sort @display.list_known_classes class_cache.keys.sort
else else
@names.each do |name| @names.each do |name|
case name if class_cache.key? name then
when /::|\#|\./ then method_map = display_class name
if class_cache.key? name then if(@interactive)
display_class name method_name = @display.get_class_method_choice(method_map)
else
klass, = parse_name name
orig_klass = klass if(method_name != nil)
orig_name = name method = lookup_method "#{name}#{method_name}", name
display_method method
until klass == 'Kernel' do
method = lookup_method name, klass
break method if method
ancestor = lookup_ancestor klass, orig_klass
break unless ancestor
name = name.sub klass, ancestor
klass = ancestor
end end
raise NotFoundError, orig_name unless method
@display.display_method_info method
end end
else elsif name =~ /::|\#|\./ then
if class_cache.key? name then klass, = parse_name name
display_class name
else
methods = select_methods(/^#{name}/)
if methods.size == 0 orig_klass = klass
raise NotFoundError, name orig_name = name
elsif methods.size == 1
@display.display_method_info methods.first loop do
method = lookup_method name, klass
break method if method
ancestor = lookup_ancestor klass, orig_klass
break unless ancestor
name = name.sub klass, ancestor
klass = ancestor
end
raise NotFoundError, orig_name unless method
display_method method
else
methods = select_methods(/#{name}/)
if methods.size == 0
raise NotFoundError, name
elsif methods.size == 1
display_method methods[0]
else
if(@interactive)
@display.display_method_list_choice methods
else else
@display.display_method_list methods @display.display_method_list methods
end end
@ -540,12 +657,13 @@ Options may also be set in the 'RI' environment variable.
end end
def write_cache(cache, path) def write_cache(cache, path)
File.open path, "wb" do |cache_file| if(@use_cache)
Marshal.dump cache, cache_file File.open path, "wb" do |cache_file|
Marshal.dump cache, cache_file
end
end end
cache cache
end end
end end

View File

@ -93,7 +93,7 @@ class RDoc::RI::Formatter
end end
def raw_print_line(txt) def raw_print_line(txt)
@output.puts txt @output.print txt
end end
## ##

View File

@ -26,9 +26,9 @@ module RDoc::RI::Paths
DOC_DIR = "doc/rdoc" DOC_DIR = "doc/rdoc"
version = RbConfig::CONFIG['ruby_version'] VERSION = RbConfig::CONFIG['ruby_version']
base = File.join(RbConfig::CONFIG['datadir'], "ri", version) base = File.join(RbConfig::CONFIG['datadir'], "ri", VERSION)
SYSDIR = File.join(base, "system") SYSDIR = File.join(base, "system")
SITEDIR = File.join(base, "site") SITEDIR = File.join(base, "site")
homedir = ENV['HOME'] || ENV['USERPROFILE'] || ENV['HOMEPATH'] homedir = ENV['HOME'] || ENV['USERPROFILE'] || ENV['HOMEPATH']
@ -39,9 +39,6 @@ module RDoc::RI::Paths
HOMEDIR = nil HOMEDIR = nil
end end
# This is the search path for 'ri'
PATH = [ SYSDIR, SITEDIR, HOMEDIR ].find_all {|p| p && File.directory?(p)}
begin begin
require 'rubygems' unless defined?(Gem) and defined?(Gem::Enable) and require 'rubygems' unless defined?(Gem) and defined?(Gem::Enable) and
Gem::Enable Gem::Enable
@ -67,7 +64,6 @@ module RDoc::RI::Paths
end end
GEMDIRS = ri_paths.map { |k,v| v.last }.sort GEMDIRS = ri_paths.map { |k,v| v.last }.sort
GEMDIRS.each { |dir| PATH << dir }
rescue LoadError rescue LoadError
GEMDIRS = [] GEMDIRS = []
end end
@ -85,9 +81,6 @@ module RDoc::RI::Paths
# found. # found.
def self.raw_path(use_system, use_site, use_home, use_gems, *extra_dirs) def self.raw_path(use_system, use_site, use_home, use_gems, *extra_dirs)
return PATH unless use_system or use_site or use_home or use_gems or
not extra_dirs.empty?
path = [] path = []
path << extra_dirs unless extra_dirs.empty? path << extra_dirs unless extra_dirs.empty?
path << SYSDIR if use_system path << SYSDIR if use_system
@ -97,6 +90,4 @@ module RDoc::RI::Paths
return path.flatten.compact return path.flatten.compact
end end
end end

View File

@ -45,7 +45,7 @@ class RDoc::RI::Reader
def get_method(method_entry) def get_method(method_entry)
path = method_entry.path_name path = method_entry.path_name
File.open(path) { |f| RI::Description.deserialize(f) } File.open(path) { |f| RDoc::RI::Description.deserialize(f) }
end end
## ##
@ -54,8 +54,8 @@ class RDoc::RI::Reader
def get_class(class_entry) def get_class(class_entry)
result = nil result = nil
for path in class_entry.path_names for path in class_entry.path_names
path = RiWriter.class_desc_path(path, class_entry) path = RDoc::RI::Writer.class_desc_path(path, class_entry)
desc = File.open(path) {|f| RI::Description.deserialize(f) } desc = File.open(path) {|f| RDoc::RI::Description.deserialize(f) }
if result if result
result.merge_in(desc) result.merge_in(desc)
else else

View File

@ -1,7 +1,5 @@
require 'rdoc/ri' require 'rdoc/ri'
class RDoc::RI::Error < RuntimeError; end
## ##
# Break argument into its constituent class or module names, an # Break argument into its constituent class or module names, an
# optional method type, and a method name # optional method type, and a method name

BIN
test/rdoc/binary.dat Normal file

Binary file not shown.

View File

@ -0,0 +1,31 @@
#
# This file is parsed by test_rdoc_markup_to_html_crossref.rb
# during its tests.
#
class Ref_Class1
end
class Ref_Class2
class Ref_Class3
def method
end
class Helper1
def method?
end
end
end
end
class Ref_Class3
class Helper1
end
class Helper2
end
end
class Ref_Class4
class Ref_Class4
end
end

View File

@ -0,0 +1,73 @@
require 'test/unit'
require 'rdoc/markup/attribute_manager'
class TestAttributeManager < Test::Unit::TestCase
def setup
@am = RDoc::Markup::AttributeManager.new
@klass = RDoc::Markup::AttributeManager
end
def teardown
silently do
@klass.const_set(:MATCHING_WORD_PAIRS, {})
@klass.const_set(:WORD_PAIR_MAP, {})
@klass.const_set(:HTML_TAGS, {})
end
end
def test_initial_word_pairs
word_pairs = @klass::MATCHING_WORD_PAIRS
assert word_pairs.is_a?(Hash)
assert_equal(3, word_pairs.size)
end
def test_initial_html
html_tags = @klass::HTML_TAGS
assert html_tags.is_a?(Hash)
assert_equal(5, html_tags.size)
end
def test_add_matching_word_pair
@am.add_word_pair("x","x", :TEST)
word_pairs = @klass::MATCHING_WORD_PAIRS
assert_equal(4,word_pairs.size)
assert(word_pairs.has_key?("x"))
end
def test_add_invalid_word_pair
assert_raise ArgumentError do
@am.add_word_pair("<", "<", :TEST)
end
end
def test_add_word_pair_map
@am.add_word_pair("x", "y", :TEST)
word_pair_map = @klass::WORD_PAIR_MAP
assert_equal(1,word_pair_map.size)
assert_equal(word_pair_map. keys.first.source, "(x)(\\S+)(y)")
end
def test_add_html_tag
@am.add_html("Test", :TEST)
tags = @klass::HTML_TAGS
assert_equal(6, tags.size)
assert(tags.has_key?("test"))
end
def test_add_special
@am.add_special("WikiWord", :WIKIWORD)
specials = @klass::SPECIAL
assert_equal(1,specials.size)
assert(specials.has_key?("WikiWord"))
end
def silently(&block)
warn_level = $VERBOSE
$VERBOSE = nil
result = block.call
$VERBOSE = warn_level
result
end
end

View File

@ -5,7 +5,7 @@ require 'test/unit'
require 'rdoc/generator/texinfo' require 'rdoc/generator/texinfo'
# From chapter 18 of the Pickaxe 3rd ed. and the TexInfo manual. # From chapter 18 of the Pickaxe 3rd ed. and the TexInfo manual.
class TestRdocInfoFormatting < Test::Unit::TestCase class TestRDocInfoFormatting < Test::Unit::TestCase
def setup def setup
@output_dir = File.join Dir.tmpdir, "test_rdoc_info_formatting_#{$$}" @output_dir = File.join Dir.tmpdir, "test_rdoc_info_formatting_#{$$}"
@output_file = File.join @output_dir, 'rdoc.texinfo' @output_file = File.join @output_dir, 'rdoc.texinfo'
@ -19,7 +19,7 @@ class TestRdocInfoFormatting < Test::Unit::TestCase
end end
def teardown def teardown
FileUtils.rm_rf @output_dir # FileUtils.rm_rf @output_dir
end end
# Make sure tags like *this* do not make HTML # Make sure tags like *this* do not make HTML
@ -73,10 +73,10 @@ class TestRdocInfoFormatting < Test::Unit::TestCase
# === Everything deeper becomes a regular @heading # === Everything deeper becomes a regular @heading
# ====== Regardless of its nesting level # ====== Regardless of its nesting level
def test_headings def test_headings
assert_match(/@majorheading\{Huge heading should be a @@majorheading\}/) assert_match(/@majorheading Huge heading should be a @@majorheading/)
assert_match(/@chapheading\{There is also @@chapheading\}/) assert_match(/@chapheading There is also @@chapheading/)
assert_match(/@heading\{Everything deeper becomes a regular @@heading\}/) assert_match(/@heading Everything deeper becomes a regular @@heading/)
assert_match(/@heading\{Regardless of its nesting level\}/) assert_match(/@heading Regardless of its nesting level/)
end end
# * list item # * list item

View File

@ -6,10 +6,10 @@ require 'tmpdir'
require 'rdoc/generator/texinfo' require 'rdoc/generator/texinfo'
# give us access to check this stuff before it's rendered # give us access to check this stuff before it's rendered
class RDoc::Generator::Texinfo; attr_reader :files, :classes; end class RDoc::Generator::TEXINFO; attr_reader :files, :classes; end
class RDoc::RDoc; attr_reader :options; attr_reader :gen; end class RDoc::RDoc; attr_reader :options; attr_reader :gen; end
class TestRdocInfoSections < Test::Unit::TestCase class TestRDocInfoSections < Test::Unit::TestCase
def setup def setup
@output_dir = File.join Dir.tmpdir, "test_rdoc_info_sections_#{$$}" @output_dir = File.join Dir.tmpdir, "test_rdoc_info_sections_#{$$}"

View File

@ -1,5 +1,6 @@
require "test/unit" require "test/unit"
require "rdoc/markup/inline" require "rdoc/markup/inline"
require "rdoc/markup/to_html_crossref"
class TestRDocMarkupAttributeManager < Test::Unit::TestCase class TestRDocMarkupAttributeManager < Test::Unit::TestCase
@ -201,24 +202,23 @@ class TestRDocMarkupAttributeManager < Test::Unit::TestCase
end end
def test_special def test_special
# class names, variable names, file names, or instance variables @am.add_special(RDoc::Markup::ToHtmlCrossref::CROSSREF_REGEXP, :CROSSREF)
@am.add_special(/(
\b([A-Z]\w+(::\w+)*)
| \#\w+[!?=]?
| \b\w+([_\/\.]+\w+)+[!?=]?
)/x,
:CROSSREF)
assert_equal(["cat"], @am.flow("cat")) #
# The apostrophes in "cats'" and "dogs'" suppress the flagging of these
# words as potential cross-references, which is necessary for the unit
# tests. Unfortunately, the markup engine right now does not actually
# check whether a cross-reference is valid before flagging it.
#
assert_equal(["cats'"], @am.flow("cats'"))
assert_equal(["cat ", crossref("#fred"), " dog"].flatten, assert_equal(["cats' ", crossref("#fred"), " dogs'"].flatten,
@am.flow("cat #fred dog")) @am.flow("cats' #fred dogs'"))
assert_equal([crossref("#fred"), " dog"].flatten, assert_equal([crossref("#fred"), " dogs'"].flatten,
@am.flow("#fred dog")) @am.flow("#fred dogs'"))
assert_equal(["cat ", crossref("#fred")].flatten, @am.flow("cat #fred")) assert_equal(["cats' ", crossref("#fred")].flatten, @am.flow("cats' #fred"))
end end
end end

View File

@ -2,7 +2,7 @@ require 'test/unit'
require 'rdoc/markup' require 'rdoc/markup'
require 'rdoc/markup/to_html' require 'rdoc/markup/to_html'
class TestRdocMarkupToHtml < Test::Unit::TestCase class TestRDocMarkupToHtml < Test::Unit::TestCase
def setup def setup
@am = RDoc::Markup::AttributeManager.new @am = RDoc::Markup::AttributeManager.new
@ -10,11 +10,23 @@ class TestRdocMarkupToHtml < Test::Unit::TestCase
end end
def test_tt_formatting def test_tt_formatting
assert_equal "<p>\n<tt>--</tt> &#8212; <tt>(c)</tt> &#169;\n</p>\n", assert_equal "<p>\n<tt>--</tt> &#8212; <tt>cats'</tt> cats&#8217;\n</p>\n",
util_format("<tt>--</tt> -- <tt>(c)</tt> (c)") util_format("<tt>--</tt> -- <tt>cats'</tt> cats'")
assert_equal "<p>\n<b>&#8212;</b>\n</p>\n", util_format("<b>--</b>") assert_equal "<p>\n<b>&#8212;</b>\n</p>\n", util_format("<b>--</b>")
end end
def test_convert_string_fancy
#
# The HTML typesetting is broken in a number of ways, but I have fixed
# the most glaring issues for single and double quotes. Note that
# "strange" symbols (periods or dashes) need to be at the end of the
# test case strings in order to suppress cross-references.
#
assert_equal "<p>\n&#8220;cats&#8221;.\n</p>\n", util_format("\"cats\".")
assert_equal "<p>\n&#8216;cats&#8217;.\n</p>\n", util_format("\'cats\'.")
assert_equal "<p>\ncat&#8217;s-\n</p>\n", util_format("cat\'s-")
end
def util_fragment(text) def util_fragment(text)
RDoc::Markup::Fragment.new 0, nil, nil, text RDoc::Markup::Fragment.new 0, nil, nil, text
end end

View File

@ -2,17 +2,287 @@ require 'test/unit'
require 'rdoc/generator' require 'rdoc/generator'
require 'rdoc/markup/to_html_crossref' require 'rdoc/markup/to_html_crossref'
class TestRdocMarkupToHtmlCrossref < Test::Unit::TestCase require 'pathname'
def setup class TestRDocMarkupToHtmlCrossref < Test::Unit::TestCase
@xref = RDoc::Markup::ToHtmlCrossref.new 'from_path', nil, nil
#
# This method parses a source file and returns a Hash mapping
# class names (Strings) to RDoc::Generator::Class instances
# (classes), which can be used to create RDoc::Markup::ToHtmlCrossref
# instances. The unit tests only test against classes starting with
# Ref_, so this method only includes such classes in the Hash.
#
def create_class_hash
# The relative gem would help here...
# @source_file_name must be cleaned because rdoc does not deal
# well with paths containing "." or "..".
curr_file = Pathname.new(__FILE__)
@source_file_name = curr_file.dirname + "rdoc_markup_to_html_crossref_reference.rb"
@source_file_name = @source_file_name.cleanpath.to_s
RDoc::TopLevel.reset
# Reset RDoc::Generator::Method so that the method sequence number starts
# at 1, making the method sequence numbers for the methods in the Ref_
# predicable.
RDoc::Generator::Method.reset
top_level = RDoc::TopLevel.new @source_file_name
options = RDoc::Options.new
options.quiet = true
# If this is false, then RDoc::Generator::Method will attempt to create
# an HTML file containing the method source code when being instantiated,
# which does not work in the context of this unit test.
#
# RDoc::Generator::Method needs to be refactored so that this does *not*
# happen as part of instantiation.
options.inline_source = true
stats = RDoc::Stats.new 0
parser = RDoc::Parser::Ruby.new(top_level,
@source_file_name,
IO.read(@source_file_name),
options,
stats)
top_levels = []
top_levels.push(parser.scan())
files, classes = RDoc::Generator::Context.build_indices(top_levels, options)
class_hash = {}
classes.each do |klass|
if(klass.name.include?("Ref_"))
class_hash[klass.name] = klass
end
end
return class_hash
end
#
# This method uses xref to cross-reference String reference and
# asserts that xref.convert(reference) is equal
# to String expected_result.
#
def verify_convert(xref, reference, expected_result)
# Everything converted in the tests will be within paragraph markup, so
# add paragraph markup to the expected result.
actual_expected_result = "<p>\n#{expected_result}\n</p>\n"
result = xref.convert(reference)
# RDoc::Markup::ToHtml word-wraps lines. It is tricky to predict where
# a line will be wrapped except that it will happen on a space, so replace
# all newlines with spaces in order to not have to worry about this.
actual_expected_result.gsub!(/\n/, " ")
result.gsub!(/\n/, " ")
assert_equal actual_expected_result, result
end
#
# This method verifies that xref generates no cross-reference link for
# String reference.
#
def verify_no_crossref(xref, reference)
if(reference[0, 1] == "\\") # Remove the markup suppression character
expected_result = reference[1, reference.length() - 1]
else
expected_result = reference
end
verify_convert(xref, reference, expected_result)
end
#
# This method verifies that xref generates a cross-reference link to
# class_name (String) for String reference.
#
def verify_class_crossref(xref, reference, class_name)
class_file_name = class_name.gsub(/::/, "/")
result = "<a href=\"../classes/#{class_file_name}.html\">#{reference}</a>"
verify_convert xref, reference, result
end
#
# This method verifies that xref generates a cross-reference link to method
# method_seq (String, e.g, "M000001") in class_name (String) for
# String reference.
#
def verify_method_crossref(xref, reference, class_name, method_seq)
class_file_name = class_name.gsub(/::/, "/")
result = "<a href=\"../classes/#{class_file_name}.html##{method_seq}\">#{reference}</a>"
verify_convert xref, reference, result
end
#
# This method verifies that xref generates a cross-reference link to
# file_name (String) for String reference.
#
def verify_file_crossref(xref, reference, file_name)
generated_document_path = Pathname.new("../files/#{file_name.gsub(/\./, '_')}.html").cleanpath.to_s
result = "<a href=\"#{generated_document_path}\">#{reference}</a>"
verify_convert xref, reference, result
end
#
# This method verifies that several invariant cross-references are
# (or are not) generated.
#
def verify_invariant_crossrefs(xref)
# bogus does not exist and so no cross-reference should be generated.
verify_no_crossref xref, "bogus"
verify_no_crossref xref, "\\bogus"
# Ref_Class1 is in the top-level namespace, and so a cross-reference always
# should be generated, unless markup is suppressed.
verify_class_crossref xref, "Ref_Class1", "Ref_Class1"
verify_no_crossref xref, "\\Ref_Class1"
# Ref_Class2 is in the top-level namespace, and so a cross-reference always
# should be generated for it and for its nested classes.
verify_class_crossref xref, "Ref_Class2", "Ref_Class2"
verify_class_crossref xref, "Ref_Class2::Ref_Class3", "Ref_Class2::Ref_Class3"
verify_method_crossref xref, "Ref_Class2::Ref_Class3#method", "Ref_Class2::Ref_Class3", "M000001"
verify_method_crossref xref, "Ref_Class2::Ref_Class3#method()", "Ref_Class2::Ref_Class3", "M000001"
verify_method_crossref xref, "Ref_Class2::Ref_Class3.method()", "Ref_Class2::Ref_Class3", "M000001"
verify_method_crossref xref, "Ref_Class2::Ref_Class3.method(*)", "Ref_Class2::Ref_Class3", "M000001"
verify_class_crossref xref, "Ref_Class2::Ref_Class3::Helper1", "Ref_Class2::Ref_Class3::Helper1"
verify_method_crossref xref, "Ref_Class2::Ref_Class3::Helper1#method?", "Ref_Class2::Ref_Class3::Helper1", "M000002"
# The hyphen character is not a valid class/method separator character, so
# rdoc just generates a class cross-reference (perhaps it should not
# generate anything?).
result = "<a href=\"../classes/Ref_Class2/Ref_Class3.html\">Ref_Class2::Ref_Class3</a>;method(*)"
verify_convert xref, "Ref_Class2::Ref_Class3;method(*)", result
# There is one Ref_Class3 nested in Ref_Class2 and one defined in the
# top-level namespace; regardless, ::Ref_Class3 (Ref_Class3 relative
# to the top-level namespace) always should generate a link to the
# top-level Ref_Class3 (unless of course cross-references are suppressed).
verify_class_crossref xref, "::Ref_Class3", "Ref_Class3"
verify_no_crossref xref, "\\::Ref_Class3"
verify_class_crossref xref, "::Ref_Class3::Helper1", "Ref_Class3::Helper1"
verify_class_crossref xref, "::Ref_Class3::Helper2", "Ref_Class3::Helper2"
#
# Ref_Class3::Helper1 does not have method method.
#
verify_no_crossref xref, "::Ref_Class3::Helper1#method"
verify_no_crossref xref, "\\::Ref_Class3::Helper1#method"
# References to Ref_Class2 relative to the top-level namespace always should
# generate links to Ref_Class2.
verify_method_crossref xref, "::Ref_Class2::Ref_Class3#method", "Ref_Class2::Ref_Class3", "M000001"
verify_method_crossref xref, "::Ref_Class2::Ref_Class3#method()", "Ref_Class2::Ref_Class3", "M000001"
verify_method_crossref xref, "::Ref_Class2::Ref_Class3#method(*)", "Ref_Class2::Ref_Class3", "M000001"
verify_class_crossref xref, "::Ref_Class2::Ref_Class3::Helper1", "Ref_Class2::Ref_Class3::Helper1"
verify_no_crossref xref, "\\::Ref_Class2::Ref_Class3#method(*)"
# Suppressing cross-references always should suppress the generation of
# links.
verify_no_crossref xref, "\\#method"
verify_no_crossref xref, "\\#method()"
verify_no_crossref xref, "\\#method(*)"
# Links never should be generated for words solely consisting of lowercase
# letters, because too many links would get generated by mistake (i.e., the
# word "new" always would be a link).
verify_no_crossref xref, "method"
# A link always should be generated for a file name.
verify_file_crossref xref, @source_file_name, @source_file_name
# References should be generated correctly for a class scoped within
# a class of the same name.
verify_class_crossref xref, "Ref_Class4::Ref_Class4", "Ref_Class4::Ref_Class4"
end end
def test_handle_special_CROSSREF_no_underscore def test_handle_special_CROSSREF_no_underscore
out = @xref.convert 'foo' class_hash = create_class_hash
assert_equal "<p>\nfoo\n</p>\n", out # Note that we instruct the ToHtmlCrossref instance to show hashes so that
# an exception won't have to be made for words starting with a '#'.
# I'm also not convinced that the current behavior of the rdoc code
# is correct since, without this, it strips the leading # from all
# words, whether or not they end up as cross-references.
#
# After the behavior has been sorted out, this can be changed.
#
# Create a variety of RDoc::Markup::ToHtmlCrossref instances, for
# different classes, and test the cross-references generated by
# each.
klass = class_hash["Ref_Class1"]
xref = RDoc::Markup::ToHtmlCrossref.new 'from_path', klass, true
verify_invariant_crossrefs xref
verify_class_crossref xref, "Ref_Class3", "Ref_Class3"
verify_no_crossref xref, "Ref_Class3#method"
verify_no_crossref xref, "#method"
verify_class_crossref xref, "Ref_Class3::Helper1", "Ref_Class3::Helper1"
verify_class_crossref xref, "Ref_Class3::Helper2", "Ref_Class3::Helper2"
verify_no_crossref xref, "Helper1"
verify_class_crossref xref, "Ref_Class4", "Ref_Class4"
klass = class_hash["Ref_Class2"]
xref = RDoc::Markup::ToHtmlCrossref.new 'from_path', klass, true
verify_invariant_crossrefs xref
verify_class_crossref xref, "Ref_Class3", "Ref_Class2::Ref_Class3"
verify_method_crossref xref, "Ref_Class3#method", "Ref_Class2::Ref_Class3", "M000001"
verify_no_crossref xref, "#method"
verify_class_crossref xref, "Ref_Class3::Helper1", "Ref_Class2::Ref_Class3::Helper1"
verify_class_crossref xref, "Ref_Class4", "Ref_Class4"
# This one possibly is an rdoc bug...
# Ref_Class2 has a nested Ref_Class3, but
# Ref_Class2::Ref_Class3::Helper2 does not exist.
# On the other hand, there is a Ref_Class3::Helper2
# in the top-level namespace... Should rdoc stop
# looking if it finds one class match?
verify_no_crossref xref, "Ref_Class3::Helper2"
verify_no_crossref xref, "Helper1"
klass = class_hash["Ref_Class2::Ref_Class3"]
xref = RDoc::Markup::ToHtmlCrossref.new 'from_path', klass, true
verify_invariant_crossrefs xref
verify_class_crossref xref, "Ref_Class3", "Ref_Class2::Ref_Class3"
verify_method_crossref xref, "Ref_Class3#method", "Ref_Class2::Ref_Class3", "M000001"
verify_method_crossref xref, "#method", "Ref_Class2::Ref_Class3", "M000001"
verify_class_crossref xref, "Ref_Class3::Helper1", "Ref_Class2::Ref_Class3::Helper1"
verify_no_crossref xref, "Ref_Class3::Helper2"
verify_class_crossref xref, "Helper1", "Ref_Class2::Ref_Class3::Helper1"
verify_class_crossref xref, "Ref_Class4", "Ref_Class4"
klass = class_hash["Ref_Class3"]
xref = RDoc::Markup::ToHtmlCrossref.new 'from_path', klass, true
verify_invariant_crossrefs xref
verify_class_crossref xref, "Ref_Class3", "Ref_Class3"
verify_no_crossref xref, "Ref_Class3#method"
verify_no_crossref xref, "#method"
verify_class_crossref xref, "Ref_Class3::Helper1", "Ref_Class3::Helper1"
verify_class_crossref xref, "Ref_Class3::Helper2", "Ref_Class3::Helper2"
verify_class_crossref xref, "Helper1", "Ref_Class3::Helper1"
verify_class_crossref xref, "Ref_Class4", "Ref_Class4"
klass = class_hash["Ref_Class4"]
xref = RDoc::Markup::ToHtmlCrossref.new 'from_path', klass, true
verify_invariant_crossrefs xref
# A Ref_Class4 reference inside a Ref_Class4 class containing a
# Ref_Class4 class should resolve to the contained class.
verify_class_crossref xref, "Ref_Class4", "Ref_Class4::Ref_Class4"
klass = class_hash["Ref_Class4::Ref_Class4"]
xref = RDoc::Markup::ToHtmlCrossref.new 'from_path', klass, true
verify_invariant_crossrefs xref
# A Ref_Class4 reference inside a Ref_Class4 class contained within
# a Ref_Class4 class should resolve to the inner Ref_Class4 class.
verify_class_crossref xref, "Ref_Class4", "Ref_Class4::Ref_Class4"
end end
end end

View File

@ -0,0 +1,18 @@
require 'rdoc/parser'
class TestRDocParser < Test::Unit::TestCase
def test_can_parse
assert_equal(RDoc::Parser.can_parse(__FILE__), RDoc::Parser::Ruby)
readme_file_name = File.join(File.dirname(__FILE__), "..", "README.txt")
unless File.exist? readme_file_name then
readme_file_name = File.join File.dirname(__FILE__), '..', '..', 'README'
end
assert_equal(RDoc::Parser.can_parse(readme_file_name), RDoc::Parser::Simple)
binary_file_name = File.join(File.dirname(__FILE__), "binary.dat")
assert_equal(RDoc::Parser.can_parse(binary_file_name), nil)
end
end

View File

@ -10,7 +10,7 @@ class RDoc::Parser::C
public :do_classes, :do_constants public :do_classes, :do_constants
end end
class TestRdocParserC < Test::Unit::TestCase class TestRDocParserC < Test::Unit::TestCase
def setup def setup
@tempfile = Tempfile.new self.class.name @tempfile = Tempfile.new self.class.name
@ -244,9 +244,36 @@ Init_Foo(void) {
assert_equal " \n a comment for class Foo on Init\n \n", klass.comment assert_equal " \n a comment for class Foo on Init\n \n", klass.comment
end end
def test_define_method
content = <<-EOF
/*Method Comment! */
static VALUE
rb_io_s_read(argc, argv, io)
int argc;
VALUE *argv;
VALUE io;
{
}
void
Init_IO(void) {
/*
* a comment for class Foo on rb_define_class
*/
VALUE rb_cIO = rb_define_class("IO", rb_cObject);
rb_define_singleton_method(rb_cIO, "read", rb_io_s_read, -1);
}
EOF
klass = util_get_class content, 'rb_cIO'
read_method = klass.method_list.first
assert_equal "read", read_method.name
assert_equal " Method Comment! \n", read_method.comment
end
def util_get_class(content, name) def util_get_class(content, name)
parser = util_parser content parser = util_parser content
parser.do_classes parser.scan
parser.classes[name] parser.classes[name]
end end

View File

@ -0,0 +1,72 @@
require 'stringio'
require 'tempfile'
require 'test/unit'
require 'rdoc/options'
require 'rdoc/parser/perl'
class TestRdocParserPerlPOD < Test::Unit::TestCase
def setup
@tempfile = Tempfile.new self.class.name
filename = @tempfile.path
@top_level = RDoc::TopLevel.new filename
@fn = filename
@options = RDoc::Options.new
@stats = RDoc::Stats.new 0
end
def teardown
@tempfile.close
end
def test_uncommented_perl
content = <<-EOF
while (<>) {
tr/a-z/A-Z;
print
}
EOF
comment = util_get_comment content
assert_equal "", comment
end
def test_perl_without_pod
content = <<-EOF
#!/usr/local/bin/perl
#
#This is a pointless perl program because it does -p.
#
while(<>) {print;}:
EOF
comment = util_get_comment content
assert_equal "", comment
end
def test_simple_pod_no_structure
content = <<-EOF
=begin pod
This just contains plain old documentation
=end
EOF
comment = util_get_comment content
assert_equal "\nThis just contains plain old documentation\n\n", comment
end
# Get the comment of the @top_level when it has processed the input.
def util_get_comment(content)
parser = util_parser content
parser.scan.comment
end
# create a new parser with the supplied content.
def util_parser(content)
RDoc::Parser::PerlPOD.new @top_level, @fn, content, @options, @stats
end
end

View File

@ -6,12 +6,16 @@ require 'rdoc/options'
require 'rdoc/parser/ruby' require 'rdoc/parser/ruby'
require 'rdoc/stats' require 'rdoc/stats'
class TestRdocParserRuby < Test::Unit::TestCase class TestRDocParserRuby < Test::Unit::TestCase
def setup def setup
@tempfile = Tempfile.new self.class.name @tempfile = Tempfile.new self.class.name
@filename = @tempfile.path @filename = @tempfile.path
# Some tests need two paths.
@tempfile2 = Tempfile.new self.class.name
@filename2 = @tempfile2.path
util_toplevel util_toplevel
@options = RDoc::Options.new @options = RDoc::Options.new
@options.quiet = true @options.quiet = true
@ -20,6 +24,7 @@ class TestRdocParserRuby < Test::Unit::TestCase
def teardown def teardown
@tempfile.close @tempfile.close
@tempfile2.close
end end
def test_look_for_directives_in_commented def test_look_for_directives_in_commented
@ -158,6 +163,105 @@ class TestRdocParserRuby < Test::Unit::TestCase
assert_equal 'Super', bar.superclass assert_equal 'Super', bar.superclass
end end
def test_parse_module
comment = "##\n# my module\n"
util_parser 'module Foo; end'
tk = @parser.get_tk
@parser.parse_module @top_level, RDoc::Parser::Ruby::NORMAL, tk, comment
foo = @top_level.modules.first
assert_equal 'Foo', foo.full_name
assert_equal comment, foo.comment
end
def test_parse_class_mistaken_for_module
#
# The code below is not strictly legal Ruby (Foo must have been defined
# before Foo::Bar is encountered), but RDoc might encounter Foo::Bar before
# Foo if they live in different files.
#
code = <<-EOF
class Foo::Bar
end
module Foo::Baz
end
class Foo
end
EOF
util_parser code
@parser.scan()
assert(@top_level.modules.empty?)
foo = @top_level.classes.first
assert_equal 'Foo', foo.full_name
bar = foo.classes.first
assert_equal 'Foo::Bar', bar.full_name
baz = foo.modules.first
assert_equal 'Foo::Baz', baz.full_name
end
def test_parse_class_definition_encountered_after_class_reference
#
# The code below is not strictly legal Ruby (Foo must have been defined
# before Foo.bar is encountered), but RDoc might encounter Foo.bar before
# Foo if they live in different files.
#
code = <<-EOF
def Foo.bar
end
class Foo < IO
end
EOF
util_parser code
@parser.scan()
assert(@top_level.modules.empty?)
foo = @top_level.classes.first
assert_equal 'Foo', foo.full_name
assert_equal 'IO', foo.superclass
bar = foo.method_list.first
assert_equal 'bar', bar.name
end
def test_parse_module_relative_to_top_level_namespace
comment = <<-EOF
#
# Weirdly named module
#
EOF
code = comment + <<-EOF
module ::Foo
class Helper
end
end
EOF
util_parser code
@parser.scan()
foo = @top_level.modules.first
assert_equal 'Foo', foo.full_name
assert_equal comment, foo.comment
helper = foo.classes.first
assert_equal 'Foo::Helper', helper.full_name
end
def test_parse_comment def test_parse_comment
content = <<-EOF content = <<-EOF
class Foo class Foo
@ -416,9 +520,107 @@ end
@parser.parse_statements @top_level, RDoc::Parser::Ruby::NORMAL, nil, '' @parser.parse_statements @top_level, RDoc::Parser::Ruby::NORMAL, nil, ''
foo = @top_level.classes.first.method_list[0]
assert_equal 'foo', foo.name
foo2 = @top_level.classes.first.method_list.last foo2 = @top_level.classes.first.method_list.last
assert_equal 'foo2', foo2.name assert_equal 'foo2', foo2.name
assert_equal 'foo', foo2.is_alias_for.name assert_equal 'foo', foo2.is_alias_for.name
assert @top_level.classes.first.aliases.empty?
end
def test_parse_statements_identifier_alias_method_before_original_method
# This is not strictly legal Ruby code, but it simulates finding an alias
# for a method before finding the original method, which might happen
# to rdoc if the alias is in a different file than the original method
# and rdoc processes the alias' file first.
content = <<-EOF
class Foo
alias_method :foo2, :foo
alias_method :foo3, :foo
def foo()
end
alias_method :foo4, :foo
alias_method :foo5, :unknown
end
EOF
util_parser content
@parser.parse_statements @top_level, RDoc::Parser::Ruby::NORMAL, nil, ''
foo = @top_level.classes.first.method_list[0]
assert_equal 'foo', foo.name
foo2 = @top_level.classes.first.method_list[1]
assert_equal 'foo2', foo2.name
assert_equal 'foo', foo2.is_alias_for.name
foo3 = @top_level.classes.first.method_list[2]
assert_equal 'foo3', foo3.name
assert_equal 'foo', foo3.is_alias_for.name
foo4 = @top_level.classes.first.method_list.last
assert_equal 'foo4', foo4.name
assert_equal 'foo', foo4.is_alias_for.name
assert_equal 'unknown', @top_level.classes.first.aliases[0].old_name
end
def test_parse_statements_identifier_constant
content = <<-EOF
class Foo
FIRST_CONSTANT = 5
SECOND_CONSTANT = [
1,
2,
3
]
THIRD_CONSTANT = {
:foo => 'bar',
:x => 'y'
}
FOURTH_CONSTANT = SECOND_CONSTANT.map do |element|
element + 1
element + 2
end
FIFTH_CONSTANT = SECOND_CONSTANT.map { |element| element + 1 }
end
EOF
util_parser content
@parser.parse_statements @top_level, RDoc::Parser::Ruby::NORMAL, nil, ''
constants = @top_level.classes.first.constants
constant = constants[0]
assert_equal 'FIRST_CONSTANT', constant.name
assert_equal '5', constant.value
constant = constants[1]
assert_equal 'SECOND_CONSTANT', constant.name
assert_equal '[ 1, 2, 3 ]', constant.value
constant = constants[2]
assert_equal 'THIRD_CONSTANT', constant.name
assert_equal "{ :foo => 'bar', :x => 'y' }", constant.value
constant = constants[3]
assert_equal 'FOURTH_CONSTANT', constant.name
assert_equal 'SECOND_CONSTANT.map do |element| element + 1 element + 2 end', constant.value
constant = constants.last
assert_equal 'FIFTH_CONSTANT', constant.name
assert_equal 'SECOND_CONSTANT.map { |element| element + 1 }', constant.value
end end
def test_parse_statements_identifier_attr def test_parse_statements_identifier_attr
@ -530,9 +732,17 @@ end
@stats @stats
end end
def util_two_parsers(first_file_content, second_file_content)
util_parser first_file_content
@parser2 = RDoc::Parser::Ruby.new @top_level2, @filename,
second_file_content, @options, @stats
end
def util_toplevel def util_toplevel
RDoc::TopLevel.reset RDoc::TopLevel.reset
@top_level = RDoc::TopLevel.new @filename @top_level = RDoc::TopLevel.new @filename
@top_level2 = RDoc::TopLevel.new @filename2
end end
end end

View File

@ -4,7 +4,7 @@ require 'rdoc/ri/formatter'
require 'rdoc/ri/display' require 'rdoc/ri/display'
require 'rdoc/ri/driver' require 'rdoc/ri/driver'
class TestRdocRiDefaultDisplay < Test::Unit::TestCase class TestRDocRiDefaultDisplay < Test::Unit::TestCase
def setup def setup
@output = StringIO.new @output = StringIO.new
@ -27,7 +27,6 @@ class TestRdocRiDefaultDisplay < Test::Unit::TestCase
end end
def test_display_class_info def test_display_class_info
ri_reader = nil
klass = h \ klass = h \
'attributes' => [ 'attributes' => [
{ 'name' => 'attribute', 'rw' => 'RW', { 'name' => 'attribute', 'rw' => 'RW',
@ -43,9 +42,9 @@ class TestRdocRiDefaultDisplay < Test::Unit::TestCase
], ],
'comment' => [RDoc::Markup::Flow::P.new('SomeClass comment')], 'comment' => [RDoc::Markup::Flow::P.new('SomeClass comment')],
'constants' => [ 'constants' => [
{ 'name' => 'CONSTANT', 'value' => '"value"', { 'name' => 'CONSTANT', 'value' => '"value1"',
'comment' => [RDoc::Markup::Flow::P.new('CONSTANT value')] }, 'comment' => [RDoc::Markup::Flow::P.new('CONSTANT value')] },
{ 'name' => 'CONSTANT_NOCOMMENT', 'value' => '"value"', { 'name' => 'CONSTANT_NOCOMMENT', 'value' => '"value2"',
'comment' => nil }, 'comment' => nil },
], ],
'display_name' => 'Class', 'display_name' => 'Class',
@ -59,7 +58,7 @@ class TestRdocRiDefaultDisplay < Test::Unit::TestCase
], ],
'superclass_string' => 'Object' 'superclass_string' => 'Object'
@dd.display_class_info klass, ri_reader @dd.display_class_info klass
expected = <<-EOF expected = <<-EOF
---------------------------------------------------- Class: SomeClass < Object ---------------------------------------------------- Class: SomeClass < Object
@ -71,10 +70,19 @@ class TestRdocRiDefaultDisplay < Test::Unit::TestCase
Constants: Constants:
---------- ----------
CONSTANT: CONSTANT = "value1"
CONSTANT value CONSTANT value
CONSTANT_NOCOMMENT CONSTANT_NOCOMMENT = "value2"
Attributes:
-----------
attribute (RW):
attribute comment
attribute_no_comment (RW)
Class methods: Class methods:
@ -99,15 +107,6 @@ Instance method extensions:
--------------------------- ---------------------------
instance_method_extension instance_method_extension
Attributes:
-----------
attribute (RW):
attribute comment
attribute_no_comment (RW)
EOF EOF
assert_equal expected, @output.string assert_equal expected, @output.string
@ -140,7 +139,7 @@ Attributes:
-------------------------------------------------------- SomeClass#some_method -------------------------------------------------------- SomeClass#some_method
some_method(arg1, arg2) {|block_param| ...} some_method(arg1, arg2) {|block_param| ...}
Extension from /nonexistent From /nonexistent
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
some comment some comment
@ -152,7 +151,7 @@ Attributes:
end end
def test_display_method_info_singleton def test_display_method_info_singleton
method = RDoc::RI::Driver::Hash.new.update \ method = RDoc::RI::Driver::OpenStructHash.new.update \
'aliases' => [], 'aliases' => [],
'block_params' => nil, 'block_params' => nil,
'comment' => nil, 'comment' => nil,
@ -167,6 +166,8 @@ Attributes:
expected = <<-EOF expected = <<-EOF
------------------------------------------------------- SomeClass::some_method ------------------------------------------------------- SomeClass::some_method
SomeClass::some_method(arg1, arg2) SomeClass::some_method(arg1, arg2)
From
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
[no description] [no description]
EOF EOF
@ -176,7 +177,7 @@ Attributes:
def test_display_method_list def test_display_method_list
methods = [ methods = [
RDoc::RI::Driver::Hash.new.update( RDoc::RI::Driver::OpenStructHash.new.update(
"aliases" => [], "aliases" => [],
"block_params" => nil, "block_params" => nil,
"comment" => nil, "comment" => nil,
@ -186,7 +187,7 @@ Attributes:
"params" => "()", "params" => "()",
"visibility" => "public" "visibility" => "public"
), ),
RDoc::RI::Driver::Hash.new.update( RDoc::RI::Driver::OpenStructHash.new.update(
"aliases" => [], "aliases" => [],
"block_params" => nil, "block_params" => nil,
"comment" => nil, "comment" => nil,
@ -204,7 +205,8 @@ Attributes:
More than one method matched your request. You can refine your search by More than one method matched your request. You can refine your search by
asking for information on one of: asking for information on one of:
SomeClass#some_method, SomeClass#some_other_method SomeClass#some_method []
SomeClass#some_other_method []
EOF EOF
assert_equal expected, @output.string assert_equal expected, @output.string
@ -216,7 +218,7 @@ Attributes:
expected = <<-EOF expected = <<-EOF
some_method(arg1, arg2) {|block_param| ...} some_method(arg1, arg2) {|block_param| ...}
Extension from /nonexistent From /nonexistent
EOF EOF
assert_equal expected, @output.string assert_equal expected, @output.string
@ -234,7 +236,7 @@ some_method(start, length)
some_method(index) some_method(index)
some_method(start, length) some_method(start, length)
Extension from /nonexistent From /nonexistent
EOF EOF
assert_equal expected, @output.string assert_equal expected, @output.string
@ -249,7 +251,7 @@ some_method(start, length)
expected = <<-EOF expected = <<-EOF
SomeClass::some_method(arg1, arg2) {|block_param| ...} SomeClass::some_method(arg1, arg2) {|block_param| ...}
Extension from /nonexistent From /nonexistent
EOF EOF
assert_equal expected, @output.string assert_equal expected, @output.string
@ -289,8 +291,7 @@ install an additional package, or ask the packager to enable ri generation.
end end
def h(hash) def h(hash)
RDoc::RI::Driver::Hash.convert hash RDoc::RI::Driver::OpenStructHash.convert hash
end end
end end

View File

@ -14,7 +14,7 @@ class TestRDocRIDriver < Test::Unit::TestCase
FileUtils.mkdir_p @home_ri FileUtils.mkdir_p @home_ri
FileUtils.mkdir_p @cache_dir FileUtils.mkdir_p @cache_dir
@driver = RDoc::RI::Driver.new @driver = RDoc::RI::Driver.new(RDoc::RI::Driver.process_args([]))
@driver.homepath = @home_ri @driver.homepath = @home_ri
end end

View File

@ -245,7 +245,7 @@ class TestRDocRIFormatter < Test::Unit::TestCase
def test_raw_print_line def test_raw_print_line
@f.raw_print_line 'a b c' @f.raw_print_line 'a b c'
assert_equal "a b c\n", @output.string assert_equal "a b c", @output.string
end end
def test_strip_attributes_b def test_strip_attributes_b