diff --git a/ChangeLog b/ChangeLog index e3ec2b2294..b9f6fcdac8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -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 * io.c (rb_io_mode_enc): make it static. diff --git a/bin/ri b/bin/ri index 0d4e3304d8..243557403c 100755 --- a/bin/ri +++ b/bin/ri @@ -1,6 +1,5 @@ -#!/usr//bin/env ruby +#!/usr/bin/env ruby require 'rdoc/ri/driver' RDoc::RI::Driver.run ARGV - diff --git a/lib/rdoc.rb b/lib/rdoc.rb index 53b72241f8..797b119fbf 100644 --- a/lib/rdoc.rb +++ b/lib/rdoc.rb @@ -1,14 +1,14 @@ $DEBUG_RDOC = nil ## -# RDoc - Ruby Documentation System +# = \RDoc - Ruby Documentation System # # 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, -# 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 -# 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 # formats. The markup library is used to interpret the comment blocks that # 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, # read on. # * 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 use the library to format text blocks into HTML, have a look # at RDoc::Markup. @@ -28,21 +26,21 @@ $DEBUG_RDOC = nil # # == Summary # -# Once installed, you can create documentation using the 'rdoc' command -# (the command is 'rdoc.bat' under Windows) +# Once installed, you can create documentation using the +rdoc+ command # # % 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 -# source (such as rdoc itself). +# source (such as RDoc itself). # # % rdoc # # This command generates documentation for all the Ruby and C source # 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 # 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. # # RDoc uses file extensions to determine how to process each file. File names -# ending +.rb+ and .rbw 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 # contain just Markup-style markup (with or without leading '#' comment # markers). If directory names are passed to RDoc, they are scanned # 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 -# RDoc::Markup. +# % export RDOCOPT="-S" # -# 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 -# 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: # # =begin rdoc @@ -93,7 +117,7 @@ $DEBUG_RDOC = nil # # ... # 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 # from comment text to their description. # @@ -124,15 +148,109 @@ $DEBUG_RDOC = nil # +:yields:+ is an example of a documentation directive. These appear # 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 ".", 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. +# +# [\text...] displays word in a *bold* font +# [\text...] displays word in an _emphasized_ font +# [\\text...] displays word in an italicized font +# [\text...] 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 . 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 label[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: {multi word label}[url]. +# # == Directives # # [+:nodoc:+ / +:nodoc:+ all] -# Don't include this element in the documentation. For classes -# and modules, the methods, aliases, constants, and attributes -# directly within the affected class or module will also be -# omitted. By default, though, modules and classes within that -# class of module _will_ be documented. This is turned off by -# adding the +all+ modifier. +# This directive prevents documentation for the element from +# being generated. For classes and modules, the methods, aliases, +# constants, and attributes directly within the affected class or +# module also will be omitted. By default, though, modules and +# classes within that class of module _will_ be documented. This is +# turned off by adding the +all+ modifier. # # module MyModule # :nodoc: # class Input @@ -144,22 +262,22 @@ $DEBUG_RDOC = nil # end # end # -# In the above code, only class +MyModule::Input+ will be documented.The -# The :nodoc: directive is global across all files the class or module -# appears in, so use :stopdoc:/:startdoc: to only omit documentation for a -# particular set of methods, etc. +# In the above code, only class MyModule::Input will be documented. +# The +:nodoc:+ directive is global across all files for the class or module +# to which it applies, so use +:stopdoc:+/+:startdoc:+ to suppress +# documentation only for a particular set of methods, etc. # # [+:doc:+] -# Force a method or attribute to be documented even if it wouldn't otherwise -# be. Useful if, for example, you want to include documentation of a +# Forces a method or attribute to be documented even if it wouldn't be +# otherwise. Useful if, for example, you want to include documentation of a # particular private method. # # [+:notnew:+] # Only applicable to the +initialize+ instance method. Normally RDoc -# assumes that the documentation and parameters for #initialize are -# actually for the ::new method, and so fakes out a ::new for the class. -# The :notnew: modifier stops this. Remember that #initialize is protected, -# so you won't see the documentation unless you use the -a command line +# assumes that the documentation and parameters for +initialize+ are +# actually for the +new+ method, and so fakes out a +new+ for the class. +# The +:notnew:+ modifier stops this. Remember that +initialize+ is private, +# so you won't see the documentation unless you use the +-a+ command line # option. # # 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, # disables documentation for the entire class or module. # -# = Other stuff +# == Other stuff # # RDoc is currently being maintained by Eric Hodel # @@ -254,7 +372,7 @@ module RDoc ## # 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 diff --git a/lib/rdoc/code_objects.rb b/lib/rdoc/code_objects.rb index fbdb612b92..0916b03398 100644 --- a/lib/rdoc/code_objects.rb +++ b/lib/rdoc/code_objects.rb @@ -219,6 +219,22 @@ module RDoc @modules.values 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 @@ -272,7 +288,24 @@ module RDoc end 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 def add_module(class_type, name) @@ -282,25 +315,41 @@ module RDoc def add_method(a_method) a_method.visibility = @visibility 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 def add_attribute(an_attribute) add_to(@attributes, an_attribute) 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) meth = find_instance_method_named(an_alias.old_name) if meth then - 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) + add_alias_impl(an_alias, meth) else add_to(@aliases, an_alias) + unmatched_alias_list = @unmatched_alias_lists[an_alias.old_name] ||= [] + unmatched_alias_list.push(an_alias) end an_alias @@ -360,6 +409,10 @@ module RDoc @requires = [] @includes = [] @constants = [] + + # This Hash maps a method name to a list of unmatched + # aliases (aliases of a method not yet encountered). + @unmatched_alias_lists = {} end # and remove classes and modules when we see a :nodoc: all @@ -374,9 +427,12 @@ module RDoc # Find a named module 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] return res if res + return self if self.name == name find_enclosing_module_named(name) end @@ -435,6 +491,7 @@ module RDoc unless modules.empty? then module_name = modules.shift result = find_module_named(module_name) + if result then modules.each do |name| result = result.find_module_named(name) @@ -573,9 +630,18 @@ module RDoc cls = all[name] - unless cls then + if !cls then cls = class_type.new name, superclass 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 collection[name] = cls unless @done_documenting diff --git a/lib/rdoc/diagram.rb b/lib/rdoc/diagram.rb index e235e043dc..4aa2ec5656 100644 --- a/lib/rdoc/diagram.rb +++ b/lib/rdoc/diagram.rb @@ -311,28 +311,30 @@ module RDoc # inclusion on the page def wrap_in_image_map(src, dot) - res = %{\n} + res = "" dot_map = `dot -Tismap #{src}` - 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 + + if(!dot_map.empty?) + res << %{\n} + 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_name}\n} end - - xs, ys = [$1.to_i, $3.to_i], [$2.to_i, $4.to_i] - url, area_name = $5, $6 - - res << %{ #{area_name}\n} + res << "\n" end - res << "\n" -# map_file = src.sub(/.dot/, '.map') -# system("dot -Timap #{src} -o #{map_file}") - res << %{#{dot}} + + res << %{#{dot}} return res end end end - diff --git a/lib/rdoc/generator.rb b/lib/rdoc/generator.rb index fbc08c4e20..d695e661d1 100644 --- a/lib/rdoc/generator.rb +++ b/lib/rdoc/generator.rb @@ -127,7 +127,7 @@ module RDoc::Generator # * a complete list of all hyperlinkable terms (file, class, module, and # method names) - def self.build_indicies(toplevels, options) + def self.build_indices(toplevels, options) files = [] classes = [] @@ -215,7 +215,7 @@ module RDoc::Generator @methods.sort.map do |meth| { "name" => CGI.escapeHTML(meth.name), - "aref" => "#{path_prefix}\##{meth.aref}" + "aref" => "##{meth.aref}" } end end @@ -614,9 +614,9 @@ module RDoc::Generator def class_attribute_values h_name = CGI.escapeHTML(name) - @values["path"] = @path + @values["href"] = @path @values["classmod"] = @is_module ? "Module" : "Class" - @values["title"] = "#{@values['classmod']}: #{h_name}" + @values["title"] = "#{@values['classmod']}: #{h_name} [#{@options.title}]" c = @context c = c.parent while c and not c.diagram @@ -704,7 +704,7 @@ module RDoc::Generator def filename_to_label @context.file_relative_name.gsub(/%|\/|\?|\#/) do - '%%%x' % $&[0].unpack('C') + ('%%%x' % $&[0]).unpack('C') end end @@ -791,7 +791,7 @@ module RDoc::Generator full_path = @context.file_absolute_name 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 @values["diagram"] = diagram_reference(@context.diagram) @@ -821,18 +821,18 @@ module RDoc::Generator attr_reader :img_url attr_reader :source_code - @@seq = "M000000" - - @@all_methods = [] - def self.all_methods @@all_methods end def self.reset @@all_methods = [] + @@seq = "M000000" end + # Initialize the class variables. + self.reset + def initialize(context, html_class, options) # TODO: rethink the class hierarchy here... @context = context @@ -1043,12 +1043,18 @@ module RDoc::Generator first = $1.to_i - 1 last = first + src.count("\n") size = last.to_s.length - real_fmt = "%#{size}d: " - fmt = " " * (size+2) + fmt = "%#{size}d: " + is_first_line = true + line_num = first src.gsub!(/^/) do - res = sprintf(fmt, first) - first += 1 - fmt = real_fmt + if is_first_line then + is_first_line = false + res = " " * (size+2) + else + res = sprintf(fmt, line_num) + end + + line_num += 1 res end end diff --git a/lib/rdoc/generator/chm/chm.rb b/lib/rdoc/generator/chm/chm.rb index 0a17a9e1ea..cceeca5dfc 100644 --- a/lib/rdoc/generator/chm/chm.rb +++ b/lib/rdoc/generator/chm/chm.rb @@ -6,6 +6,8 @@ module RDoc::Generator::CHM::CHM HTML = RDoc::Generator::HTML::HTML INDEX = HTML::INDEX + + STYLE = HTML::STYLE CLASS_INDEX = HTML::CLASS_INDEX CLASS_PAGE = HTML::CLASS_PAGE diff --git a/lib/rdoc/generator/html.rb b/lib/rdoc/generator/html.rb index a9e030a896..d136de7b00 100644 --- a/lib/rdoc/generator/html.rb +++ b/lib/rdoc/generator/html.rb @@ -68,7 +68,6 @@ class RDoc::Generator::HTML def initialize(options) #:not-new: @options = options load_html_template - @main_page_path = nil end ## @@ -94,6 +93,15 @@ class RDoc::Generator::HTML # If the template name contains a slash, use it literally 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 unless template =~ %r{/|\\} then @@ -101,14 +109,25 @@ class RDoc::Generator::HTML template) end - require template + begin + require template - @template = self.class.const_get @options.template.upcase - @options.template_class = @template + @template = self.class.const_get @options.template.upcase + @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}'" - exit 99 + $stderr.puts "Could not find HTML template '#{template}': #{e.message}" + exit 99 + end end ## @@ -146,14 +165,16 @@ class RDoc::Generator::HTML end def build_indices - @files, @classes = RDoc::Generator::Context.build_indicies(@toplevels, - @options) + @files, @classes = RDoc::Generator::Context.build_indices(@toplevels, + @options) end ## # Generate all the HTML def generate_html + @main_url = main_url + # the individual descriptions for files and classes gen_into(@files) gen_into(@classes) @@ -165,23 +186,50 @@ class RDoc::Generator::HTML gen_main_index # 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 def gen_into(list) - @file_list ||= index_to_links @files - @class_list ||= index_to_links @classes - @method_list ||= index_to_links RDoc::Generator::Method.all_methods + # + # The file, class, and method lists technically should be regenerated + # 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| next unless item.document_self 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| - item.write_on io, @file_list, @class_list, @method_list + item.write_on io, file_list, class_list, method_list end end end @@ -211,8 +259,9 @@ class RDoc::Generator::HTML values = { "entries" => res, + 'title' => CGI.escapeHTML("#{title} [#{@options.title}]"), 'list_title' => CGI.escapeHTML(title), - 'index_url' => main_url, + 'index_url' => @main_url, 'charset' => @options.charset, 'style_url' => style_url('', @options.css), } @@ -230,47 +279,55 @@ class RDoc::Generator::HTML def gen_main_index if @template.const_defined? :FRAMELESS then - main = @files.find do |file| - @main_page == file.name - end - - if main.nil? then - main = @classes.find do |klass| - main_page == klass.context.full_name - end + # + # If we're using a template without frames, then just redirect + # to it from index.html. + # + # One alternative to this, expanding the main page's template into + # index.html, is tricky because the relative URLs will be different + # (since index.html is located in at the site's root, + # rather than within a files or a classes subdirectory). + # + open 'index.html', 'w' do |f| + f.puts(%{}) + f.puts(%{}) + f.puts(%{}) + f.puts(%{#{CGI.escapeHTML(@options.title)}}) + f.puts(%{}) + f.puts(%{}) + f.puts(%{}) + f.puts(%{}) end else main = RDoc::TemplatePage.new @template::INDEX - end - open 'index.html', 'w' do |f| - style_url = style_url '', @options.css + open 'index.html', 'w' do |f| + 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 end end end - def index_to_links(collection) + def index_to_links(output_path, collection) collection.sort.map do |f| 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 @@ -278,32 +335,48 @@ class RDoc::Generator::HTML # Returns the url of the main page def main_url - @main_page = @options.main_page - @main_page_ref = nil + main_page = @options.main_page - 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 - @main_page_path = @main_page_ref.path + if main_page_ref then + return main_page_ref.path else - $stderr.puts "Could not find main page #{@main_page}" + $stderr.puts "Could not find main page #{main_page}" end end - unless @main_page_path then - file = @files.find { |context| context.document_self } - @main_page_path = file.path if file + # + # No main page has been specified, so just use the README. + # + @files.each do |file| + if file.name =~ /^README/ then + return file.path + end end - unless @main_page_path then - $stderr.puts "Couldn't find anything to document" - $stderr.puts "Perhaps you've used :stopdoc: in all classes" - exit 1 + # + # There's no README (shame! shame!). Just use the first file + # that will be documented. + # + @files.each do |file| + if file.document_self then + return file.path + 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 + private :main_url end @@ -349,12 +422,9 @@ class RDoc::Generator::HTMLInOne < RDoc::Generator::HTML 'charset' => @options.charset, 'files' => gen_into(@files), '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 if @options.op_name @@ -372,26 +442,4 @@ class RDoc::Generator::HTMLInOne < RDoc::Generator::HTML end res 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 - diff --git a/lib/rdoc/generator/html/common.rb b/lib/rdoc/generator/html/common.rb new file mode 100644 index 0000000000..b25f009a72 --- /dev/null +++ b/lib/rdoc/generator/html/common.rb @@ -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 + +EOF + + XHTML_FRAME_PREAMBLE = <<-EOF + +EOF + + HTML_ELEMENT = <<-EOF + +EOF +end diff --git a/lib/rdoc/generator/html/frameless.rb b/lib/rdoc/generator/html/frameless.rb index 2af890ce04..0375fee313 100644 --- a/lib/rdoc/generator/html/frameless.rb +++ b/lib/rdoc/generator/html/frameless.rb @@ -1,15 +1,16 @@ -require 'rdoc/generator/html' -require 'rdoc/generator/html/one_page_html' +require 'rdoc/generator/html/html' ## # = 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 # 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]' # link. # +# Frameless basically is the html template without frames. +# # == Authors # # * Michael Granger @@ -25,692 +26,47 @@ module RDoc::Generator::HTML::FRAMELESS FRAMELESS = true - FONTS = "Verdana,Arial,Helvetica,sans-serif" + FONTS = RDoc::Generator::HTML::HTML::FONTS - STYLE = <<-EOF -body { - font-family: #{FONTS}; - font-size: 90%; - margin: 0; - margin-left: 40px; - padding: 0; - background: white; -} + STYLE = RDoc::Generator::HTML::HTML::STYLE -h1, h2, h3, h4 { - 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 -"?> - - EOF - - HEADER = XHTML_PREAMBLE + <<-EOF - - - <%= values["title"] %> - " /> - - " type="text/css" media="screen" /> - - - - -EOF - - ## - # Context content template - - CONTEXT_CONTENT = %{ -} - - ## - # Footer template + HEADER = RDoc::Generator::HTML::HTML::HEADER FOOTER = <<-EOF
- - +
+

Classes

+ + +
+

Methods

+ +
EOF - ## - # File page header template + FILE_PAGE = RDoc::Generator::HTML::HTML::FILE_PAGE - FILE_PAGE = <<-EOF -
-

<%= values["short_name"] %>

+ CLASS_PAGE = RDoc::Generator::HTML::HTML::CLASS_PAGE - - - - - - - - - - -
Path:<%= values["full_path"] %> -<% if values["cvsurl"] then %> -  (">CVS) -<% end %> -
Last Update:<%= values["dtm_modified"] %>
-
- EOF - - ## - # Class page header template - - CLASS_PAGE = <<-EOF -
- - - - - - - - - - - -<% if values["parent"] then %> - - - - -<% end %> -
<%= values["classmod"] %><%= values["full_name"] %>
In: -<% values["infiles"].each do |infiles| %> -<% if infiles["full_path_url"] then %> - "> -<% end %> - <%= infiles["full_path"] %> -<% if infiles["full_path_url"] then %> - -<% end %> -<% if infiles["cvsurl"] then %> -  (">CVS) -<% end %> -
-<% end %><%# values["infiles"] %> -
Parent: -<% if values["par_url"] then %> - "> -<% end %> - <%= values["parent"] %> -<% if values["par_url"] then %> - -<% end %> -
-
- EOF - - ## - # Method list template - - METHOD_LIST = <<-EOF - -
-<% if values["diagram"] then %> -
- <%= values["diagram"] %> -
-<% end %> - -<% if values["description"] then %> -
- <%= values["description"] %> -
-<% end %> - -<% if values["requires"] then %> -
-

Required files

- -
-<% values["requires"].each do |requires| %> - <%= href requires["aref"], requires["name"] %>   -<% end %><%# values["requires"] %> -
-
-<% end %> - -<% if values["toc"] then %> -
-

Contents

- -<% end %> -
- -<% if values["methods"] then %> -
-

Methods

- -
-<% values["methods"].each do |methods| %> - <%= href methods["aref"], methods["name"] %>   -<% end %><%# values["methods"] %> -
-
-<% end %> - -
- - - -<% if values["includes"] then %> -
-

Included Modules

- -
-<% values["includes"].each do |includes| %> - <%= href includes["aref"], includes["name"] %> -<% end %><%# values["includes"] %> -
-
-<% end %> - -<% values["sections"].each do |sections| %> -
-<% if sections["sectitle"] then %> -

"><%= sections["sectitle"] %>

-<% if sections["seccomment"] then %> -
- <%= sections["seccomment"] %> -
-<% end %> -<% end %> - -<% if values["classlist"] then %> -
-

Classes and Modules

- - <%= values["classlist"] %> -
-<% end %> - -<% if values["constants"] then %> -
-

Constants

- -
- -<% values["constants"].each do |constants| %> - - - - -<% if values["desc"] then %> - - -<% end %> - -<% end %><%# values["constants"] %> -
<%= constants["name"] %>=<%= constants["value"] %> <%= constants["desc"] %>
-
-
-<% end %> - -<% if values["aliases"] then %> -
-

External Aliases

- -
- -<% values["aliases"].each do |aliases| $stderr.puts({ :aliases => aliases }.inspect) %> - - - - - -<% if values["desc"] then %> - - - - -<% end %> -<% end %><%# values["aliases"] %> -
<%= values["old_name"] %>-><%= values["new_name"] %>
 <%= values["desc"] %>
-
-
-<% end %> - - -<% if values["attributes"] then %> -
-

Attributes

- -
- -<% values["attributes"].each do |attributes| $stderr.puts({ :attributes => attributes }.inspect) %> - - -<% if values["rw"] then %> - -<% end %> -<% unless values["rw"] then %> - -<% end %> - - -<% end %><%# values["attributes"] %> -
<%= values["name"] %> [<%= values["rw"] %>]   <%= values["a_desc"] %>
-
-
-<% end %> - - -<% if sections["method_list"] then %> -
-<% sections["method_list"].each do |method_list| %> -<% if method_list["methods"] then %> -

<%= method_list["type"] %> <%= method_list["category"] %> methods

- -<% method_list["methods"].each do |methods| %> -
" class="method-detail"> - "> - - - -
-<% if methods["m_desc"] then %> - <%= methods["m_desc"] %> -<% end %> -<% if methods["sourcecode"] then %> -

-source');return false;">[Source]

-
-source"> -
-<%= methods["sourcecode"] %>
-
-
-<% end %> -
-
- -<% end %><%# method_list["methods"] %> -<% end %> -<% end %><%# sections["method_list"] %> - -
-<% end %> -<% end %><%# values["sections"] %> - EOF - - ## - # Body template + METHOD_LIST = RDoc::Generator::HTML::HTML::METHOD_LIST BODY = HEADER + %{ @@ -724,72 +80,13 @@ EOF } + FOOTER - ## - # Source code template + SRC_PAGE = RDoc::Generator::HTML::HTML::SRC_PAGE - SRC_PAGE = XHTML_PREAMBLE + <<-EOF - - - <%= values["title"] %> - " /> - " type="text/css" media="screen" /> - - -
<%= values["code"] %>
- - - EOF + FR_INDEX_BODY = RDoc::Generator::HTML::HTML::FR_INDEX_BODY - ## - # Index file templates + FILE_INDEX = RDoc::Generator::HTML::HTML::FILE_INDEX - FR_INDEX_BODY = %{ -<%= template_include %> -} - - FILE_INDEX = XHTML_PREAMBLE + <<-EOF - - - <%= values["list_title"] %> - " /> - " type="text/css" /> - - - -
-

<%= values["list_title"] %>

-
-<% values["entries"].each do |entries| %> - "><%= entries["name"] %>
-<% end %><%# values["entries"] %> -
-
- - - EOF - - CLASS_INDEX = FILE_INDEX - METHOD_INDEX = FILE_INDEX - - INDEX = <<-EOF -"?> - - - - <%= values["title"] %> - " /> - - - - - - - " name="docwin" /> - - - EOF + CLASS_INDEX = RDoc::Generator::HTML::HTML::CLASS_INDEX + METHOD_INDEX = RDoc::Generator::HTML::HTML::METHOD_INDEX end - diff --git a/lib/rdoc/generator/html/hefss.rb b/lib/rdoc/generator/html/hefss.rb index e186a40384..540c23d869 100644 --- a/lib/rdoc/generator/html/hefss.rb +++ b/lib/rdoc/generator/html/hefss.rb @@ -1,16 +1,16 @@ require 'rdoc/generator/html' -require 'rdoc/generator/html/html' +require 'rdoc/generator/html/kilmerfactory' module RDoc::Generator::HTML::HEFSS FONTS = "Verdana, Arial, Helvetica, sans-serif" -STYLE = <<-EOF -body,p { font-family: Verdana, Arial, Helvetica, sans-serif; + CENTRAL_STYLE = <<-EOF +body,p { font-family: <%= values["fonts"] %>; color: #000040; background: #BBBBBB; } -td { font-family: Verdana, Arial, Helvetica, sans-serif; +td { font-family: <%= values["fonts"] %>; color: #000040; } @@ -21,16 +21,20 @@ td { font-family: Verdana, Arial, Helvetica, sans-serif; } .big-title-font { color: white; - font-family: Verdana, Arial, Helvetica, sans-serif; + font-family: <%= values["fonts"] %>; font-size: large; height: 50px} .small-title-font { color: purple; - font-family: Verdana, Arial, Helvetica, sans-serif; + font-family: <%= values["fonts"] %>; font-size: small; } .aqua { color: purple } +#diagram img { + border: 0; +} + .method-name, attr-name { font-family: monospace; font-weight: bold; } @@ -75,241 +79,6 @@ td { font-family: Verdana, Arial, Helvetica, sans-serif; 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 - - <%= values["title"] %> - "> - " type="text/css" media="screen" /> - - - - -<%= template_include %> - -<% if values["diagram"] then %> -
-<%= values["diagram"] %> -
-<% end %> - -<% if values["description"] then %> -
<%= values["description"] %>
-<% end %> - -<% if values["requires"] then %> - - -
Required files

-
-<% values["requires"].each do |requires| %> -<%= href requires["aref"], requires["name"] %> -<% end %><%# values["requires"] %> -<% 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 %> - - -
Subroutines and Functions

-
-<% method_list["methods"].each do |methods| %> -" target="source"><%= methods["name"] %> -<% end %><%# values["methods"] %> -
-<% end %> -<% end %><%# values["method_list"] %> -<% end %> - -<% if sections["attributes"] then %> - - -
Arguments

- -<% sections["attributes"].each do |attributes| %> - -<% if attributes["rw"] then %> - -<% end %> -<% unless attributes["rw"] then %> - -<% end %> - - - -<% end %><%# values["attributes"] %> -
 [<%= attributes["rw"] %>] <%= attributes["name"] %><%= attributes["a_desc"] %>
-<% end %> -<% end %><%# values["sections"] %> -<% end %> - -<% if values["classlist"] then %> - - -
Modules

-<%= values["classlist"] %>
-<% end %> - - <%= template_include %> - - - - EOF - - FILE_PAGE = <<-EOF - - - - -
- -
File
<%= values["short_name"] %>
- - - - - - - - -
Path:<%= values["full_path"] %> -<% if values["cvsurl"] then %> -  (">CVS) -<% end %> -
Modified:<%= values["dtm_modified"] %>
-

- EOF - - CLASS_PAGE = <<-EOF - - - - - -
- <%= values["classmod"] %>
<%= values["full_name"] %> -
- - - - - -<% if values["parent"] then %> - - - - -<% end %> -
In: -<% values["infiles"].each do |infiles| %> -<%= href infiles["full_path_url"], infiles["full_path"] %> -<% if infiles["cvsurl"] then %> - (">CVS) -<% end %> -<% end %><%# values["infiles"] %> -
Parent: -<% if values["par_url"] then %> - " class="cyan"> -<% end %> -<%= values["parent"] %> -<% if values["par_url"] then %> - -<% end %> -
-

- EOF - - METHOD_LIST = <<-EOF -<% if values["includes"] then %> -
Uses

-
-<% values["includes"].each do |includes| %> - <%= href includes["aref"], includes["name"] %> -<% end %><%# values["includes"] %> -
-<% 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 %> - - -
<%= method_list["type"] %> <%= method_list["category"] %> methods
-<% method_list["methods"].each do |methods| %> - - -
-"> -<%= methods["name"] %><%= methods["params"] %> -<% if methods["codeurl"] then %> -" target="source" class="srclink">src -<% end %> -
-<% if method_list["m_desc"] then %> -
-<%= method_list["m_desc"] %> -
-<% end %> -<% end %><%# method_list["methods"] %> -<% end %> -<% end %><%# sections["method_list"] %> -<% end %> -<% end %><%# values["sections"] %> -<% end %> - EOF - - SRC_PAGE = <<-EOF - -<%= values["title"] %> -"> - - - -
<%= values["code"] %>
- - + +.srcbut { float: right } EOF - FR_INDEX_BODY = %{ -<%= template_include %> + INDEX_STYLE = <<-EOF +body { + background-color: #bbbbbb; + font-family: #{FONTS}; + font-size: 11px; + font-style: normal; + line-height: 14px; + color: #000040; } - FILE_INDEX = <<-EOF - - -"> - - - - - -<% values["entries"].each do |entries| %> -"><%= entries["name"] %>
-<% end %><%# values["entries"] %> - - EOF + FACTORY = RDoc::Generator::HTML:: + KilmerFactory.new(:central_css => CENTRAL_STYLE, + :index_css => INDEX_STYLE, + :method_list_heading => "Subroutines and Functions", + :class_and_module_list_heading => "Classes and Modules", + :attribute_list_heading => "Arguments") - CLASS_INDEX = FILE_INDEX - METHOD_INDEX = FILE_INDEX + STYLE = FACTORY.get_STYLE() - INDEX = <<-EOF - - - <%= values["title"] %> - "> - + METHOD_LIST = FACTORY.get_METHOD_LIST() + + BODY = FACTORY.get_BODY() + + FILE_PAGE = FACTORY.get_FILE_PAGE() - - - - - - - - " name="docwin"> - - - - <body bgcolor="#BBBBBB"> - Click <a href="html/index.html">here</a> for a non-frames - version of this page. - </body> - - + CLASS_PAGE = FACTORY.get_CLASS_PAGE() - - EOF + SRC_PAGE = FACTORY.get_SRC_PAGE() - # Blank page to use as a target - BLANK = %{ - -} + FR_INDEX_BODY = FACTORY.get_FR_INDEX_BODY() - def write_extra_pages - template = TemplatePage.new(BLANK) - File.open("blank.html", "w") { |f| template.write_html_on(f, {}) } + FILE_INDEX = FACTORY.get_FILE_INDEX() + + 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 - diff --git a/lib/rdoc/generator/html/html.rb b/lib/rdoc/generator/html/html.rb index 1ab90c6264..823d8056e7 100644 --- a/lib/rdoc/generator/html/html.rb +++ b/lib/rdoc/generator/html/html.rb @@ -1,15 +1,17 @@ require 'rdoc/generator/html' -require 'rdoc/generator/html/one_page_html' +require 'rdoc/generator/html/common' ## # = 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 # 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 # '[source]' link. # +# This template *also* forms the basis of the frameless template. +# # == Authors # # * Michael Granger @@ -23,34 +25,54 @@ require 'rdoc/generator/html/one_page_html' module RDoc::Generator::HTML::HTML + include RDoc::Generator::HTML::Common + FONTS = "Verdana,Arial,Helvetica,sans-serif" STYLE = <<-EOF body { - font-family: Verdana,Arial,Helvetica,sans-serif; - font-size: 90%; - margin: 0; - margin-left: 40px; - padding: 0; - background: white; + font-family: #{FONTS}; + font-size: 90%; + margin: 0; + margin-left: 40px; + padding: 0; + background: white; + color: black; } -h1,h2,h3,h4 { margin: 0; color: #efefef; background: transparent; } -h1 { font-size: 150%; } -h2,h3,h4 { margin-top: 1em; } +h1, h2, h3, h4 { + margin: 0; + background: transparent; +} -a { background: #eef; color: #039; text-decoration: none; } -a:hover { background: #039; color: #eef; } +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 > a { +td > :link, td > :visited { background: transparent; color: #039; text-decoration: none; } /* and inside a section title */ -.section-title > a { +.section-title > :link, .section-title > :visited { background: transparent; color: #eee; text-decoration: none; @@ -58,181 +80,255 @@ td > a { /* === Structural elements =================================== */ -div#index { - margin: 0; - margin-left: -40px; - padding: 0; - font-size: 90%; +.index { + margin: 0; + margin-left: -40px; + padding: 0; + font-size: 90%; } - -div#index a { - margin-left: 0.7em; +.index :link, .index :visited { + margin-left: 0.7em; } -div#index .section-bar { - margin-left: 0px; - padding-left: 0.7em; - background: #ccc; - font-size: small; +.index .section-bar { + margin-left: 0px; + padding-left: 0.7em; + background: #ccc; + font-size: small; } - -div#classHeader, div#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, #fileHeader { + width: auto; + color: white; + padding: 0.5em 1.5em 0.5em 1.5em; + margin: 0; + margin-left: -40px; + border-bottom: 3px solid #006; } -div#classHeader a, div#fileHeader a { - background: inherit; - color: white; +#classHeader :link, #fileHeader :link, +#classHeader :visited, #fileHeader :visited { + background: inherit; + color: white; } -div#classHeader td, div#fileHeader td { - background: inherit; - color: white; +#classHeader td, #fileHeader td { + background: inherit; + color: white; } - -div#fileHeader { - background: #057; +#fileHeader { + background: #057; } -div#classHeader { - background: #048; +#classHeader { + background: #048; } - .class-name-in-header { font-size: 180%; font-weight: bold; } - -div#bodyContent { - padding: 0 1.5em 0 1.5em; +#bodyContent { + padding: 0 1.5em 0 1.5em; } -div#description { - padding: 0.5em 1.5em; - background: #efefef; - border: 1px dotted #999; +#description { + padding: 0.5em 1.5em; + background: #efefef; + border: 1px dotted #999; } -div#description h1,h2,h3,h4,h5,h6 { - color: #125;; - background: transparent; +#description h1, #description h2, #description h3, +#description h4, #description h5, #description h6 { + color: #125; + background: transparent; } -div#validator-badges { - 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 { + text-align: center; } +#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 =================================== */ table.header-table { - color: white; - font-size: small; + color: white; + font-size: small; } .type-note { - font-size: small; - color: #DEDEDE; -} - -.xxsection-bar { - background: #eee; - color: #333; - padding: 3px; + font-size: small; + color: #dedede; } .section-bar { - color: #333; - border-bottom: 1px solid #999; - margin-left: -20px; + 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; + 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 } +.top-aligned-row { + vertical-align: top +} + +.bottom-aligned-row { + vertical-align: bottom +} + +#diagram img { + border: 0; +} /* --- 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; } + +.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; + 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; } + +.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 -------------------- */ -a.source-toggle { font-size: 90%; } -div.method-source-code { - background: #262626; - color: #ffdead; - margin: 1em; - padding: 0.5em; - border: 1px dashed #999; - overflow: hidden; +:link.source-toggle, :visited.source-toggle { + font-size: 90%; } -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 --------------------- */ -.standalone-code { background: #221111; color: #ffdead; overflow: hidden; } +.standalone-code { + background: #221111; + color: #ffdead; + overflow: auto; +} -.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; } +.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 @@ -240,15 +336,7 @@ EOF ### H E A D E R T E M P L A T E ##################################################################### - XHTML_PREAMBLE = <<-EOF -"?> - - EOF - - HEADER = XHTML_PREAMBLE + <<-EOF - + HEADER = XHTML_STRICT_PREAMBLE + HTML_ELEMENT + <<-EOF <%= values["title"] %> " /> @@ -281,7 +369,7 @@ EOF } // Make codeblocks hidden by default - document.writeln( "" ) + document.writeln( " - - -
<%= values["code"] %>
- - EOF - FR_INDEX_BODY = %{ -<%= template_include %> -} - - FILE_INDEX = <<-EOF - - -"> - - - - - -<% values["entries"].each do |entries| %> -"><%= entries["name"] %>
-<% end %><%# values["entries"] %> - - EOF + FACTORY = RDoc::Generator::HTML:: + KilmerFactory.new(:central_css => CENTRAL_STYLE, + :index_css => INDEX_STYLE) - CLASS_INDEX = FILE_INDEX - METHOD_INDEX = FILE_INDEX + STYLE = FACTORY.get_STYLE() - INDEX = <<-EOF - - - <%= values["title"] %> - "> - + METHOD_LIST = FACTORY.get_METHOD_LIST() + + BODY = FACTORY.get_BODY() + + FILE_PAGE = FACTORY.get_FILE_PAGE() - - - - - - -<% if values["inline_source"] then %> - " name="docwin"> -<% end %> -<% unless values["inline_source"] then %> - - " name="docwin"> - - -<% end %> - - <body bgcolor="white"> - Click <a href="html/index.html">here</a> for a non-frames - version of this page. - </body> - - + CLASS_PAGE = FACTORY.get_CLASS_PAGE() - - EOF + SRC_PAGE = FACTORY.get_SRC_PAGE() - # A blank page to use as a target - BLANK = %{ - -} + FR_INDEX_BODY = FACTORY.get_FR_INDEX_BODY() - def write_extra_pages - template = TemplatePage.new(BLANK) - File.open("blank.html", "w") { |f| template.write_html_on(f, {}) } + FILE_INDEX = FACTORY.get_FILE_INDEX() + + 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 - diff --git a/lib/rdoc/generator/html/kilmerfactory.rb b/lib/rdoc/generator/html/kilmerfactory.rb new file mode 100644 index 0000000000..ef6f3f3b4d --- /dev/null +++ b/lib/rdoc/generator/html/kilmerfactory.rb @@ -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 %> +
+
+<%= values["diagram"] %> +
+
+<% end %> + +<% if values["description"] then %> +
<%= values["description"] %>
+<% end %> + +<% if values["requires"] then %> + + +
Required files

+
+<% values["requires"].each do |requires| %> +<%= href requires["aref"], requires["name"] %> +<% end %><%# values["requires"] %> +
+<% end %> + +<% if values["methods"] then %> + + +
#{@method_list_heading}

+
+<% values["methods"].each do |methods| %> +<%= href methods["aref"], methods["name"] %>, +<% end %><%# values["methods"] %> +
+<% end %> + +<% if values["includes"] then %> +
Included modules

+
+<% values["includes"].each do |includes| %> + <%= href includes["aref"], includes["name"] %> +<% end %><%# values["includes"] %> +
+<% end %> + +<% values["sections"].each do |sections| %> +
+<% if sections["sectitle"] then %> +

"><%= sections["sectitle"] %>

+<% if sections["seccomment"] then %> +
+ <%= sections["seccomment"] %> +
+<% end %> +<% end %> +<% if sections["attributes"] then %> + + +
#{@attribute_list_heading}

+ +<% sections["attributes"].each do |attributes| %> + +<% if attributes["rw"] then %> + +<% end %> +<% unless attributes["rw"] then %> + +<% end %> + + + +<% end %><%# sections["attributes"] %> +
 [<%= attributes["rw"] %>] <%= attributes["name"] %><%= attributes["a_desc"] %>
+<% end %> + +<% if sections["classlist"] then %> + + +
#{@class_and_module_list_heading}

+<%= sections["classlist"] %>
+<% end %> + +<% if sections["method_list"] then %> +<% sections["method_list"].each do |method_list| %> +<% if method_list["methods"] then %> + + +
<%= method_list["type"] %> <%= method_list["category"] %> methods
+<% method_list["methods"].each do |methods| %> + + +
+"> +<% if methods["callseq"] then %> +<%= methods["callseq"] %> +<% end %> +<% unless methods["callseq"] then %> + <%= methods["name"] %><%= methods["params"] %> +<% end %> + +<% if methods["codeurl"] then %> +" target="source" class="srclink">src +<% end %> +
+<% if methods["m_desc"] then %> +
+<%= methods["m_desc"] %> +
+<% end %> +<% if methods["aka"] then %> +
+This method is also aliased as +<% methods["aka"].each do |aka| %> +"><%= methods["name"] %> +<% end %><%# methods["aka"] %> +
+<% end %> +<% if methods["sourcecode"] then %> +
+<%= methods["sourcecode"] %>
+
+<% end %> +<% end %><%# method_list["methods"] %> +<% end %> +<% end %><%# sections["method_list"] %> +<% end %> + +<% end %><%# values["sections"] %> +
+} + end + + def get_BODY + return XHTML_STRICT_PREAMBLE + HTML_ELEMENT + %{ + + <%= values["title"] %> + " /> + " type="text/css" media="screen" /> + + + +
+<%= template_include %> + +#{get_METHOD_LIST()} +
+ + +} + end + +def get_FILE_PAGE + return %{ + + + + +
+ +
File
<%= values["short_name"] %>
+ + + + + + + + +
Path:<%= values["full_path"] %> +<% if values["cvsurl"] then %> +  (">CVS) +<% end %> +
Modified:<%= values["dtm_modified"] %>
+

+} +end + +def get_CLASS_PAGE + return %{ + + + + + +
+ <%= values["classmod"] %>
<%= values["full_name"] %> +
+ + + + + +<% if values["parent"] then %> + + + + +<% end %> +
In: +<% values["infiles"].each do |infiles| %> +<%= href infiles["full_path_url"], infiles["full_path"] %> +<% if infiles["cvsurl"] then %> + (">CVS) +<% end %> +<% end %><%# values["infiles"] %> +
Parent: +<% if values["par_url"] then %> + " class="cyan"> +<% end %> +<%= values["parent"] %> +<% if values["par_url"] then %> + +<% end %> +
+

+} +end + +def get_SRC_PAGE + return XHTML_STRICT_PREAMBLE + HTML_ELEMENT + %{ +<%= values["title"] %> +" /> +" type="text/css" media="screen" /> + + +
<%= values["code"] %>
+ + +} +end + +def get_FR_INDEX_BODY + return %{<%= template_include %>} +end + +def get_FILE_INDEX + return XHTML_STRICT_PREAMBLE + HTML_ELEMENT + %{ + +<%= values["title"] %> +" /> + + + + +
+ +<% values["entries"].each do |entries| %> +"><%= entries["name"] %>
+<% end %><%# values["entries"] %> +
+ +} +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 + %{ + + <%= values["title"] %> + " /> + + + + + + + + +<% if values["inline_source"] then %> + " name="docwin" /> +<% end %> +<% unless values["inline_source"] then %> + + " name="docwin" /> + + +<% end %> + + + +} +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 + %{ + + Source Code Frame <%= values["title_suffix"] %> + " /> + " type="text/css" media="screen" /> + + + + +} +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 diff --git a/lib/rdoc/generator/html/one_page_html.rb b/lib/rdoc/generator/html/one_page_html.rb index c4dd95529d..51ae32351a 100644 --- a/lib/rdoc/generator/html/one_page_html.rb +++ b/lib/rdoc/generator/html/one_page_html.rb @@ -1,7 +1,10 @@ require 'rdoc/generator/html' +require 'rdoc/generator/html/common' module RDoc::Generator::HTML::ONE_PAGE_HTML + include RDoc::Generator::HTML::Common + CONTENTS_XML = <<-EOF <% if defined? classes and classes["description"] then %> <%= classes["description"] %> @@ -76,16 +79,14 @@ module RDoc::Generator::HTML::ONE_PAGE_HTML <% end %> EOF - ONE_PAGE = %{ - - + ONE_PAGE = XHTML_STRICT_PREAMBLE + HTML_ELEMENT + %{ <%= values["title"] %> " /> <% values["files"].each do |files| %> -

File: <%= files["short_name"] %>

+

File: "><%= files["short_name"] %>

@@ -97,7 +98,7 @@ module RDoc::Generator::HTML::ONE_PAGE_HTML

Classes

<% values["classes"].each do |classes| %> <% if classes["parent"] then %> -

<%= classes["classmod"] %> <%= classes["full_name"] %> < <%= href classes["par_url"], classes["parent"] %>

+

<%= classes["classmod"] %> "><%= classes["full_name"] %> < <%= href classes["par_url"], classes["parent"] %>

<% end %> <% unless classes["parent"] then %>

<%= classes["classmod"] %> <%= classes["full_name"] %>

diff --git a/lib/rdoc/generator/texinfo.rb b/lib/rdoc/generator/texinfo.rb index 0b79820228..70db875af9 100644 --- a/lib/rdoc/generator/texinfo.rb +++ b/lib/rdoc/generator/texinfo.rb @@ -3,13 +3,10 @@ require 'rdoc/generator' require 'rdoc/markup/to_texinfo' module RDoc - RDoc::GENERATORS['texinfo'] = RDoc::Generator.new("rdoc/generator/texinfo", - :Texinfo, - 'texinfo') module Generator # This generates Texinfo files for viewing with GNU Info or Emacs # from RDoc extracted from Ruby source files. - class Texinfo + class TEXINFO # What should the .info file be named by default? DEFAULT_INFO_FILENAME = 'rdoc.info' @@ -26,8 +23,8 @@ module RDoc # Generate the +texinfo+ files def generate(toplevels) @toplevels = toplevels - @files, @classes = ::RDoc::Generator::Context.build_indicies(@toplevels, - @options) + @files, @classes = ::RDoc::Generator::Context.build_indices(@toplevels, + @options) (@files + @classes).each { |x| x.value_hash } diff --git a/lib/rdoc/generator/texinfo/class.texinfo.erb b/lib/rdoc/generator/texinfo/class.texinfo.erb index 07f17eaef2..74ecc59f7d 100644 --- a/lib/rdoc/generator/texinfo/class.texinfo.erb +++ b/lib/rdoc/generator/texinfo/class.texinfo.erb @@ -38,7 +38,7 @@ Methods <% (method_list["methods"] || []).uniq.each do |method| %> <%= TexinfoTemplate.new(@v.merge({'method' => method, 'list' => method_list}), 'method.texinfo.erb').render %><% end %> -<% end # section["method_list"] %> -<% end %> +<% end %> +<% end # if section["method_list"] %> <% end # @v['class']["sections"] %> <% end %> diff --git a/lib/rdoc/generator/xml.rb b/lib/rdoc/generator/xml.rb index 3335f2ce7c..0d4c5a7ea1 100644 --- a/lib/rdoc/generator/xml.rb +++ b/lib/rdoc/generator/xml.rb @@ -34,15 +34,15 @@ class RDoc::Generator::XML < RDoc::Generator::HTML ## # Generate: # - # * a list of HtmlFile objects for each TopLevel object. - # * a list of HtmlClass objects for each first level + # * a list of File objects for each TopLevel object. + # * a list of Class objects for each first level # class or module in the TopLevel objects # * a complete list of all hyperlinkable terms (file, # class, module, and method names) def build_indices @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 RDoc::TopLevel.all_classes_and_modules.each do |cls| @@ -51,7 +51,7 @@ class RDoc::Generator::XML < RDoc::Generator::HTML end 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| build_class_list(mod, html_file, class_dir) end @@ -68,9 +68,6 @@ class RDoc::Generator::XML < RDoc::Generator::HTML '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 if @options.op_name diff --git a/lib/rdoc/generator/xml/xml.rb b/lib/rdoc/generator/xml/xml.rb index ffb1329c4e..4b54e7350f 100644 --- a/lib/rdoc/generator/xml/xml.rb +++ b/lib/rdoc/generator/xml/xml.rb @@ -17,11 +17,23 @@ module RDoc::Generator::XML::XML href="<%= requires["aref"] %>" <% end %> /> -<% end # files["requires"] %> +<% end %><%# files["requires"] %> <% end %> <% if defined? classes and classes["sections"] then %> <% classes["sections"].each do |sections| %> +<% if sections["constants"] then %> + +<% sections["constants"].each do |constant| %> + "> +<% if constant["value"] then %> + <%= constant["value"] %> +<% end %> + <%= constant["a_desc"] %> + +<% end %><%# sections["constants"] %> + +<% end %> <% if sections["attributes"] then %> <% sections["attributes"].each do |attributes| %> @@ -31,7 +43,7 @@ module RDoc::Generator::XML::XML <% end %> <%= attributes["a_desc"] %> -<% end # sections["attributes"] %> +<% end %><%# sections["attributes"] %> <% end %> <% if sections["method_list"] then %> @@ -52,12 +64,12 @@ module RDoc::Generator::XML::XML <% end %> -<% end # method_list["methods"] %> +<% end %><%# method_list["methods"] %> <% end %> -<% end # sections["method_list"] %> +<% end %><%# sections["method_list"] %> <% end %> -<% end # classes["sections"] %> +<% end %><%# classes["sections"] %> <% end %> <% if defined? classes and classes["includes"] then %> @@ -67,7 +79,7 @@ module RDoc::Generator::XML::XML href="<%= includes["aref"] %>" <% end %> /> -<% end # classes["includes"] %> +<% end %><%# classes["includes"] %> <% end %> @@ -84,7 +96,7 @@ module RDoc::Generator::XML::XML } + CONTENTS_XML + %{ -<% end # values["files"] %> +<% end %><%# values["files"] %> <% values["classes"].each do |classes| %> @@ -94,7 +106,7 @@ module RDoc::Generator::XML::XML <% classes["infiles"].each do |infiles| %> <%= href infiles["full_path_url"], infiles["full_path"] %> -<% end # classes["infiles"] %> +<% end %><%# classes["infiles"] %> <% end %> <% if classes["parent"] then %> @@ -103,7 +115,7 @@ module RDoc::Generator::XML::XML } + CONTENTS_XML + %{ > -<% end # values["classes"] %> +<% end %><%# values["classes"] %> } diff --git a/lib/rdoc/markup.rb b/lib/rdoc/markup.rb index 0e1b596255..9d22b38946 100644 --- a/lib/rdoc/markup.rb +++ b/lib/rdoc/markup.rb @@ -20,101 +20,6 @@ require 'rdoc' # RDoc::Markup could be the basis for formatting RDoc style comment blocks, # 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 ".", 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. -# -# [\text...] displays word in a *bold* font -# [\text...] displays word in an _emphasized_ font -# [\text...] displays word in an _emphasized_ font -# [\text...] 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 \\\bold -# text and \\\*bold* produce \bold text 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 . 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 label[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: {multi word label}[url]. -# # == Synopsis # # This code converts +input_string+ to HTML. The conversion takes place in diff --git a/lib/rdoc/markup/inline.rb b/lib/rdoc/markup/inline.rb index ee77679a11..46c9b5822c 100644 --- a/lib/rdoc/markup/inline.rb +++ b/lib/rdoc/markup/inline.rb @@ -47,7 +47,7 @@ class RDoc::Markup class AttrChanger 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 diff --git a/lib/rdoc/markup/to_html.rb b/lib/rdoc/markup/to_html.rb index ca29373db1..dce7a69b12 100644 --- a/lib/rdoc/markup/to_html.rb +++ b/lib/rdoc/markup/to_html.rb @@ -57,7 +57,7 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter ## # 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) 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... def convert_string_fancy(item) - # convert -- to em-dash, (-- to en-dash) - item.gsub(/---?/, '—'). #gsub(/--/, '–'). + # convert ampersand before doing anything else + item.gsub(/&/, '&'). + # convert -- to em-dash, (-- to en-dash) + gsub(/---?/, '—'). #gsub(/--/, '–'). + # convert ... to elipsis (and make sure .... becomes .) gsub(/\.\.\.\./, '.…').gsub(/\.\.\./, '…'). @@ -318,15 +321,15 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter gsub(/'/, '‘'). # convert double closing quote - gsub(%r{([^ \t\r\n\[\{\(])\'(?=\W)}, '\1”'). # } + gsub(%r{([^ \t\r\n\[\{\(])\"(?=\W)}, '\1”'). # } # convert double opening quote - gsub(/'/, '“'). + gsub(/"/, '“'). # convert copyright gsub(/\(c\)/, '©'). - # convert and registered trademark + # convert registered trademark gsub(/\(r\)/, '®') end diff --git a/lib/rdoc/markup/to_html_crossref.rb b/lib/rdoc/markup/to_html_crossref.rb index a6f29c5c2c..dc64b30da1 100644 --- a/lib/rdoc/markup/to_html_crossref.rb +++ b/lib/rdoc/markup/to_html_crossref.rb @@ -9,6 +9,68 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml 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 # 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? super() - # class names, variable names, or instance variables - @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) + @markup.add_special(CROSSREF_REGEXP, :CROSSREF) @from_path = from_path @context = context @@ -48,6 +99,9 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml def handle_special_CROSSREF(special) 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 @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 # whether the string as a whole is a known symbol). - if /([A-Z][\w:]*)[.\#](\w+[!?=]?)/ =~ lookup then - container = $1 - method = $2 - ref = @context.find_symbol container, method - end - - if !ref and - /([A-Za-z][\w:]*)[.\#](\w+(\([\.\w+\*\/\+\-\=\<\>]+\))?)/ =~ lookup then + if /#{CLASS_REGEXP_STR}[\.\#]#{METHOD_REGEXP_STR}/ =~ lookup then container = $1 method = $2 ref = @context.find_symbol container, method @@ -99,4 +146,3 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml end end - diff --git a/lib/rdoc/markup/to_texinfo.rb b/lib/rdoc/markup/to_texinfo.rb index 533d3e34a0..65a1608c4d 100644 --- a/lib/rdoc/markup/to_texinfo.rb +++ b/lib/rdoc/markup/to_texinfo.rb @@ -30,7 +30,7 @@ class RDoc::Markup::ToTexInfo < RDoc::Markup::Formatter def accept_heading(attributes, text) heading = ['@majorheading', '@chapheading'][text.head_level - 1] || '@heading' - @text << "#{heading}{#{format(text)}}" + @text << "#{heading} #{format(text)}" end def accept_list_start(attributes, text) diff --git a/lib/rdoc/options.rb b/lib/rdoc/options.rb index d683a14022..1d92bd4748 100644 --- a/lib/rdoc/options.rb +++ b/lib/rdoc/options.rb @@ -184,7 +184,7 @@ class RDoc::Options @css = nil @webcvs = nil - @charset = 'iso-8859-1' + @charset = 'utf-8' end ## @@ -196,6 +196,7 @@ class RDoc::Options opts = OptionParser.new do |opt| opt.program_name = File.basename $0 opt.version = RDoc::VERSION + opt.release = nil opt.summary_indent = ' ' * 4 opt.banner = <<-EOF Usage: #{opt.program_name} [options] [names...] @@ -257,7 +258,7 @@ Usage: #{opt.program_name} [options] [names...] opt.separator nil opt.on("--charset=CHARSET", "-c", - "Specifies the HTML character-set.") do |value| + "Specifies the output HTML character-set.") do |value| @charset = value end @@ -283,9 +284,7 @@ Usage: #{opt.program_name} [options] [names...] opt.on("--exclude=PATTERN", "-x", Regexp, "Do not process files or directories", - "matching PATTERN. Files given explicitly", - "on the command line will never be", - "excluded.") do |value| + "matching PATTERN.") do |value| @exclude << value end diff --git a/lib/rdoc/parser.rb b/lib/rdoc/parser.rb index 794fad00e9..6855cfcfc5 100644 --- a/lib/rdoc/parser.rb +++ b/lib/rdoc/parser.rb @@ -62,11 +62,32 @@ class RDoc::Parser true 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 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 ## diff --git a/lib/rdoc/parser/c.rb b/lib/rdoc/parser/c.rb index 43bb767da9..a5b06c2b04 100644 --- a/lib/rdoc/parser/c.rb +++ b/lib/rdoc/parser/c.rb @@ -120,7 +120,7 @@ class RDoc::Parser::C < RDoc::Parser @stats.add_alias as end - end + end def do_classes @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* + \s*([\w\*\s\(\)\.\->]+)\s* # for SWIG \s*\)/mx) do |var_name, in_module, class_name, parent| handle_class_module(var_name, "class", class_name, parent, in_module) end @@ -251,7 +251,7 @@ class RDoc::Parser::C < RDoc::Parser handle_method("method", "rb_mFileTest", meth_name, meth_body, param_count) handle_method("singleton_method", "rb_cFile", meth_name, meth_body, param_count) end - end + end def find_attr_comment(attr_name) if @content =~ %r{((?>/\*.*?\*/\s+)) @@ -267,10 +267,10 @@ class RDoc::Parser::C < RDoc::Parser ## # 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 - when %r"((?>/\*.*?\*/\s*))(?:static\s+)?VALUE\s+#{meth_name} - \s*(\([^)]*\))\s*\{.*?^\}"xm + when %r"((?>/\*.*?\*/\s*)*)(?:(?:static|SWIGINTERN)\s+)?(?:intern\s+)?VALUE\s+#{meth_name} + \s*(\([^)]*\))([^;]|$)"xm comment, params = $1, $2 body_text = $& @@ -279,9 +279,7 @@ class RDoc::Parser::C < RDoc::Parser # see if we can find the whole body re = Regexp.escape(body_text) + '[^(]*^\{.*?^\}' - if Regexp.new(re, Regexp::MULTILINE).match(body) - body_text = $& - end + body_text = $& if /#{re}/m =~ body # The comment block may have been overridden with a 'Document-method' # 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 # 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 find_modifiers(comment, meth_obj) if comment @@ -300,18 +298,18 @@ class RDoc::Parser::C < RDoc::Parser meth_obj.comment = mangle_comment(comment) when %r{((?>/\*.*?\*/\s*))^\s*\#\s*define\s+#{meth_name}\s+(\w+)}m comment = $1 - find_body($2, meth_obj, body, true) + find_body(class_name, $2, meth_obj, body, true) find_modifiers(comment, meth_obj) meth_obj.comment = mangle_comment(comment) + meth_obj.comment 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 return false end else # 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 find_modifiers(comment, meth_obj) @@ -367,10 +365,10 @@ class RDoc::Parser::C < RDoc::Parser def find_class_comment(class_name, class_meth) comment = nil 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 - elsif @content =~ %r{Document-(class|module):\s#{class_name}\s*?\n((?>.*?\*/))}m - comment = $2 else if @content =~ /rb_define_(class|module)/m then class_name = class_name.split("::").last @@ -424,9 +422,11 @@ class RDoc::Parser::C < RDoc::Parser end end - def find_override_comment(meth_name) + def find_override_comment(class_name, 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 end end @@ -480,6 +480,10 @@ class RDoc::Parser::C < RDoc::Parser end 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 @stats.add_class cm else @@ -562,18 +566,16 @@ class RDoc::Parser::C < RDoc::Parser return unless class_name - class_obj = find_class(var_name, class_name) + class_obj = find_class var_name, class_name - if class_obj - if meth_name == "initialize" + if class_obj then + if meth_name == "initialize" then meth_name = "new" type = "singleton_method" 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) @@ -585,14 +587,16 @@ class RDoc::Parser::C < RDoc::Parser meth_obj.params = "(" + (1..p_count).map{|i| "p#{i}"}.join(", ") + ")" end - if source_file + if source_file then file_name = File.join(@file_dir, source_file) body = (@@known_bodies[source_file] ||= File.read(file_name)) else body = @content 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 @@ -628,8 +632,8 @@ class RDoc::Parser::C < RDoc::Parser end def remove_private_comments(comment) - comment.gsub!(/\/?\*--(.*?)\/?\*\+\+/m, '') - comment.sub!(/\/?\*--.*/m, '') + comment.gsub!(/\/?\*--\n(.*?)\/?\*\+\+/m, '') + comment.sub!(/\/?\*--\n.*/m, '') end ## diff --git a/lib/rdoc/parser/perl.rb b/lib/rdoc/parser/perl.rb new file mode 100644 index 0000000000..43d1e9ff69 --- /dev/null +++ b/lib/rdoc/parser/perl.rb @@ -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<(.*?)>/, '\1'); + comment.gsub!(/I<(.*?)>/, '\1'); + comment.gsub!(/B<(.*?)>/, '\1'); + comment + end + +end + diff --git a/lib/rdoc/parser/ruby.rb b/lib/rdoc/parser/ruby.rb index abbc85bde7..865cb79d39 100644 --- a/lib/rdoc/parser/ruby.rb +++ b/lib/rdoc/parser/ruby.rb @@ -134,7 +134,7 @@ module RDoc::RubyToken TokenDefinitions = [ [:TkCLASS, TkKW, "class", EXPR_CLASS], - [:TkMODULE, TkKW, "module", EXPR_BEG], + [:TkMODULE, TkKW, "module", EXPR_CLASS], [:TkDEF, TkKW, "def", EXPR_FNAME], [:TkUNDEF, TkKW, "undef", EXPR_FNAME], [:TkBEGIN, TkKW, "begin", EXPR_BEG], @@ -1945,9 +1945,9 @@ class RDoc::Parser::Ruby < RDoc::Parser case tk when TkSEMICOLON break - when TkLPAREN, TkfLPAREN + when TkLPAREN, TkfLPAREN, TkLBRACE, TkLBRACK, TkDO nest += 1 - when TkRPAREN + when TkRPAREN, TkRBRACE, TkRBRACK, TkEND nest -= 1 when TkCOMMENT if nest <= 0 && @scanner.lex_state == EXPR_END @@ -1955,7 +1955,7 @@ class RDoc::Parser::Ruby < RDoc::Parser break end 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) break end @@ -2683,8 +2683,8 @@ class RDoc::Parser::Ruby < RDoc::Parser end def remove_private_comments(comment) - comment.gsub!(/^#--.*?^#\+\+/m, '') - comment.sub!(/^#--.*/m, '') + comment.gsub!(/^#--\n.*?^#\+\+/m, '') + comment.sub!(/^#--\n.*/m, '') end def remove_token_listener(obj) diff --git a/lib/rdoc/parser/simple.rb b/lib/rdoc/parser/simple.rb index 6e123a4655..cdfe686718 100644 --- a/lib/rdoc/parser/simple.rb +++ b/lib/rdoc/parser/simple.rb @@ -31,7 +31,7 @@ class RDoc::Parser::Simple < RDoc::Parser end def remove_private_comments(comment) - comment.gsub(/^--[^-].*?^\+\+/m, '').sub(/^--.*/m, '') + comment.gsub(/^--\n.*?^\+\+/m, '').sub(/^--\n.*/m, '') end end diff --git a/lib/rdoc/rdoc.rb b/lib/rdoc/rdoc.rb index bc0a32f407..ce1cb1a93f 100644 --- a/lib/rdoc/rdoc.rb +++ b/lib/rdoc/rdoc.rb @@ -7,6 +7,7 @@ require 'rdoc/parser/simple' require 'rdoc/parser/ruby' require 'rdoc/parser/c' require 'rdoc/parser/f95' +require 'rdoc/parser/perl' require 'rdoc/stats' require 'rdoc/options' @@ -189,11 +190,11 @@ module RDoc def parse_files(options) @stats = Stats.new options.verbosity - + files = options.files 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? @@ -288,6 +289,5 @@ module RDoc end end end - end diff --git a/lib/rdoc/ri/cache.rb b/lib/rdoc/ri/cache.rb index 2e267d95fb..06177a00de 100644 --- a/lib/rdoc/ri/cache.rb +++ b/lib/rdoc/ri/cache.rb @@ -14,7 +14,7 @@ class RDoc::RI::ClassEntry @inferior_classes = [] 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. def add_path(path) @path_names << path @@ -37,10 +37,10 @@ class RDoc::RI::ClassEntry if name =~ /^(.*?)-(c|i).yaml$/ external_name = $1 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 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 full_name = File.join(dir, name) if File.directory?(full_name) @@ -48,7 +48,7 @@ class RDoc::RI::ClassEntry if inf_class inf_class.add_path(full_name) else - inf_class = ClassEntry.new(full_name, name, self) + inf_class = RDoc::RI::ClassEntry.new(full_name, name, self) @inferior_classes << inf_class end inf_class.load_from(full_name) @@ -168,7 +168,7 @@ class RDoc::RI::MethodEntry 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 @@ -185,4 +185,3 @@ class RDoc::RI::Cache end end - diff --git a/lib/rdoc/ri/descriptions.rb b/lib/rdoc/ri/descriptions.rb index 0d8560323a..467b7de2a9 100644 --- a/lib/rdoc/ri/descriptions.rb +++ b/lib/rdoc/ri/descriptions.rb @@ -77,7 +77,9 @@ end class RDoc::RI::ModuleDescription < RDoc::RI::Description attr_accessor :class_methods + attr_accessor :class_method_extensions attr_accessor :instance_methods + attr_accessor :instance_method_extensions attr_accessor :attributes attr_accessor :constants attr_accessor :includes @@ -148,6 +150,7 @@ class RDoc::RI::MethodDescription < RDoc::RI::Description attr_accessor :aliases attr_accessor :is_alias_for attr_accessor :params + attr_accessor :source_path end diff --git a/lib/rdoc/ri/display.rb b/lib/rdoc/ri/display.rb index 379cef11b3..05a7cf253d 100644 --- a/lib/rdoc/ri/display.rb +++ b/lib/rdoc/ri/display.rb @@ -1,5 +1,15 @@ 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 # 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 # +ri_reader+ as necessary. - def display_class_info(klass, ri_reader) + def display_class_info(klass) page do superclass = klass.superclass_string @@ -61,17 +71,11 @@ class RDoc::RI::DefaultDisplay @formatter.blankline @formatter.display_heading("Includes:", 2, "") incs = [] + klass.includes.each do |inc| - inc_desc = ri_reader.find_class_by_name(inc.name) - if inc_desc - str = inc.name + "(" - str << inc_desc.instance_methods.map{|m| m.name}.join(", ") - str << ")" - incs << str - else - incs << inc.name - end - end + incs << inc.name + end + @formatter.wrap(incs.sort.join(', ')) end @@ -82,42 +86,19 @@ class RDoc::RI::DefaultDisplay constants = klass.constants.sort_by { |constant| constant.name } constants.each do |constant| + @formatter.wrap "#{constant.name} = #{constant.value}" if constant.comment then - @formatter.wrap "#{constant.name}:" - @formatter.indent do @formatter.display_flow constant.comment end else - @formatter.wrap constant.name + @formatter.break_to_newline 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 @formatter.blankline - @formatter.display_heading 'Attributes:', 2, '' attributes = klass.attributes.sort_by { |attribute| attribute.name } @@ -130,11 +111,119 @@ class RDoc::RI::DefaultDisplay end else @formatter.wrap "#{attribute.name} (#{attribute.rw})" + @formatter.break_to_newline end end end + + return display_class_method_list(klass) 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+. @@ -172,10 +261,42 @@ class RDoc::RI::DefaultDisplay def display_method_list(methods) page do @formatter.wrap "More than one method matched your request. You can refine your search by asking for information on one of:" - @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 @@ -198,10 +319,8 @@ class RDoc::RI::DefaultDisplay @formatter.break_to_newline end - if method.source_path then - @formatter.blankline - @formatter.wrap("Extension from #{method.source_path}") - end + @formatter.blankline + @formatter.wrap("From #{method.source_path}") end ## diff --git a/lib/rdoc/ri/driver.rb b/lib/rdoc/ri/driver.rb index dfc5f2f98a..0c91232b70 100644 --- a/lib/rdoc/ri/driver.rb +++ b/lib/rdoc/ri/driver.rb @@ -11,29 +11,33 @@ require 'rdoc/markup/to_flow' class RDoc::RI::Driver - class Hash < ::Hash - def self.convert(hash) - hash = new.update hash + # + # This class offers both Hash and OpenStruct functionality. + # 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| - hash[key] = case value - when ::Hash then - convert value - when Array then - value = value.map do |v| - ::Hash === v ? convert(v) : v - end - value - else - value - end + object.each do |key, value| + new_hash[key] = convert(value) + end + + new_hash + when Array then + object.map do |element| + convert(element) + end + else + object end - - hash - end - - def method_missing method, *args - self[method.to_s] end def merge_enums(other) @@ -57,6 +61,10 @@ class RDoc::RI::Driver end end end + + def method_missing method, *args + self[method.to_s] + end end class Error < RDoc::RI::Error; end @@ -69,25 +77,31 @@ class RDoc::RI::Driver attr_accessor :homepath # :nodoc: - def self.process_args(argv) + def self.default_options options = {} options[:use_stdout] = !$stdout.tty? options[:width] = 72 options[:formatter] = RDoc::RI::Formatter.for 'plain' - options[:list_classes] = false - options[:list_names] = false + options[:interactive] = false + options[:use_cache] = true - # By default all paths are used. If any of these are true, only those - # directories are used. - use_system = false - use_site = false - use_home = false - use_gems = false - doc_dirs = [] + # By default all standard paths are used. + options[:use_system] = true + options[:use_site] = true + options[:use_home] = true + options[:use_gems] = true + options[:extra_doc_dirs] = [] + + return options + end + + def self.process_args(argv) + options = default_options opts = OptionParser.new do |opt| opt.program_name = File.basename $0 opt.version = RDoc::VERSION + opt.release = nil opt.summary_indent = ' ' * 4 directories = [ @@ -142,30 +156,6 @@ Options may also be set in the 'RI' environment variable. opt.separator "Options:" 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", RDoc::RI::Formatter::FORMATTERS.keys, "Format to use when displaying output:", @@ -179,49 +169,101 @@ Options may also be set in the 'RI' environment variable. opt.separator nil - unless RDoc::RI::Paths::GEMDIRS.empty? then - opt.on("--[no-]gems", - "Include documentation from RubyGems.") do |value| - use_gems = value + opt.on("--doc-dir=DIRNAME", "-d", Array, + "List of directories from which to source", + "documentation in addition to the standard", + "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 opt.separator nil - opt.on("--[no-]home", - "Include documentation stored in ~/.rdoc.") do |value| - use_home = value + opt.on("--[no-]use-cache", + "Whether or not to use ri's cache.", + "True by default.") do |value| + options[:use_cache] = value end opt.separator nil - opt.on("--[no-]list-names", "-l", - "List all the names known to RDoc, one per", - "line.") do |value| - options[:list_names] = value - end - - opt.separator nil - - opt.on("--no-pager", "-T", - "Send output directly to stdout.") do |value| - options[:use_stdout] = !value - end - - opt.separator nil - - opt.on("--[no-]site", - "Include documentation from libraries", - "installed in site_lib.") do |value| - use_site = value + opt.on("--no-standard-docs", + "Do not include documentation from", + "the Ruby standard library, site_lib,", + "installed gems, or ~/.rdoc.", + "Equivalent to specifying", + "the options --no-system, --no-site, --no-gems,", + "and --no-home") do + options[:use_system] = false + options[:use_site] = false + options[:use_gems] = false + options[:use_home] = false end opt.separator nil opt.on("--[no-]system", "Include documentation from Ruby's standard", - "library.") do |value| - use_system = value + "library. Defaults to true.") do |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 opt.separator nil @@ -238,10 +280,10 @@ Options may also be set in the 'RI' environment variable. options[:names] = argv - options[:path] = RDoc::RI::Paths.path(use_system, use_site, use_home, - use_gems, *doc_dirs) - options[:raw_path] = RDoc::RI::Paths.raw_path(use_system, use_site, - use_home, use_gems, *doc_dirs) + options[:formatter] ||= RDoc::RI::Formatter.for('plain') + options[:use_stdout] ||= !$stdout.tty? + options[:use_stdout] ||= options[:interactive] + options[:width] ||= 72 options @@ -258,22 +300,30 @@ Options may also be set in the 'RI' environment variable. ri.run end - def initialize(options={}) - options[:formatter] ||= RDoc::RI::Formatter.for('plain') - options[:use_stdout] ||= !$stdout.tty? - options[:width] ||= 72 - @names = options[:names] + def initialize(initial_options={}) + options = self.class.default_options.update(initial_options) + @names = options[:names] @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 = @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 + @cache_doc_dirs_path = File.join cache_file_path, ".doc_dirs" + @use_cache = options[:use_cache] @class_cache = nil + @interactive = options[:interactive] @display = RDoc::RI::DefaultDisplay.new(options[:formatter], options[:width], options[:use_stdout]) @@ -282,158 +332,67 @@ Options may also be set in the 'RI' environment variable. def 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 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 - 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 - load_cache_for @class_cache_name - else - class_cache = RDoc::RI::Driver::Hash.new + if up_to_date and @use_cache then + open class_cache_file_path, 'rb' do |fp| + begin + @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 end - def class_cache_file_path - File.join cache_file_path, @class_cache_name - end + def create_class_cache + class_cache = OpenStructHash.new - 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] - 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 + if(@use_cache) + # Dump the documentation directories to a file in the cache, so that + # we only will use the cache for future instantiations with identical + # documentation directories. + File.open @cache_doc_dirs_path, "wb" do |fp| + fp << @doc_dirs.join("\n") 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 - RDoc::RI::Driver::Hash.convert cache - end + classes = map_dirs('**/cdesc*.yaml') { |f| Dir[f] } + warn "Updating class cache with #{classes.size} classes..." + populate_class_cache class_cache, classes - ## - # Finds the next ancestor of +orig_klass+ after +klass+. + write_cache class_cache, class_cache_file_path - def lookup_ancestor(klass, orig_klass) - 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] + class_cache end 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" end - klass = RDoc::RI::Driver::Hash.convert klass - klass.merge_enums desc klass["sources"] << cdesc 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) 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):SM::(\S+)/, ' !ruby/\1:RDoc::Markup::\2') - YAML.load data + OpenStructHash.convert(YAML.load(data)) end 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 else @names.each do |name| - case name - when /::|\#|\./ then - if class_cache.key? name then - display_class name - else - klass, = parse_name name + if class_cache.key? name then + method_map = display_class name + if(@interactive) + method_name = @display.get_class_method_choice(method_map) - orig_klass = klass - orig_name = name - - 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 + if(method_name != nil) + method = lookup_method "#{name}#{method_name}", name + display_method method end - - raise NotFoundError, orig_name unless method - - @display.display_method_info method end - else - if class_cache.key? name then - display_class name - else - methods = select_methods(/^#{name}/) + elsif name =~ /::|\#|\./ then + klass, = parse_name name - if methods.size == 0 - raise NotFoundError, name - elsif methods.size == 1 - @display.display_method_info methods.first + orig_klass = klass + orig_name = name + + 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 @display.display_method_list methods end @@ -540,12 +657,13 @@ Options may also be set in the 'RI' environment variable. end def write_cache(cache, path) - File.open path, "wb" do |cache_file| - Marshal.dump cache, cache_file + if(@use_cache) + File.open path, "wb" do |cache_file| + Marshal.dump cache, cache_file + end end cache end end - diff --git a/lib/rdoc/ri/formatter.rb b/lib/rdoc/ri/formatter.rb index 0a0c3f7380..933882abc4 100644 --- a/lib/rdoc/ri/formatter.rb +++ b/lib/rdoc/ri/formatter.rb @@ -93,7 +93,7 @@ class RDoc::RI::Formatter end def raw_print_line(txt) - @output.puts txt + @output.print txt end ## diff --git a/lib/rdoc/ri/paths.rb b/lib/rdoc/ri/paths.rb index b4b6c64925..2f72b9dfd5 100644 --- a/lib/rdoc/ri/paths.rb +++ b/lib/rdoc/ri/paths.rb @@ -26,9 +26,9 @@ module RDoc::RI::Paths 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") SITEDIR = File.join(base, "site") homedir = ENV['HOME'] || ENV['USERPROFILE'] || ENV['HOMEPATH'] @@ -39,9 +39,6 @@ module RDoc::RI::Paths HOMEDIR = nil end - # This is the search path for 'ri' - PATH = [ SYSDIR, SITEDIR, HOMEDIR ].find_all {|p| p && File.directory?(p)} - begin require 'rubygems' unless defined?(Gem) and defined?(Gem::Enable) and Gem::Enable @@ -67,7 +64,6 @@ module RDoc::RI::Paths end GEMDIRS = ri_paths.map { |k,v| v.last }.sort - GEMDIRS.each { |dir| PATH << dir } rescue LoadError GEMDIRS = [] end @@ -85,9 +81,6 @@ module RDoc::RI::Paths # found. 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 << extra_dirs unless extra_dirs.empty? path << SYSDIR if use_system @@ -97,6 +90,4 @@ module RDoc::RI::Paths return path.flatten.compact end - end - diff --git a/lib/rdoc/ri/reader.rb b/lib/rdoc/ri/reader.rb index 986bb75954..de3c8d9afa 100644 --- a/lib/rdoc/ri/reader.rb +++ b/lib/rdoc/ri/reader.rb @@ -45,7 +45,7 @@ class RDoc::RI::Reader def get_method(method_entry) path = method_entry.path_name - File.open(path) { |f| RI::Description.deserialize(f) } + File.open(path) { |f| RDoc::RI::Description.deserialize(f) } end ## @@ -54,8 +54,8 @@ class RDoc::RI::Reader def get_class(class_entry) result = nil for path in class_entry.path_names - path = RiWriter.class_desc_path(path, class_entry) - desc = File.open(path) {|f| RI::Description.deserialize(f) } + path = RDoc::RI::Writer.class_desc_path(path, class_entry) + desc = File.open(path) {|f| RDoc::RI::Description.deserialize(f) } if result result.merge_in(desc) else diff --git a/lib/rdoc/ri/util.rb b/lib/rdoc/ri/util.rb index 34277f2594..4e91eb978d 100644 --- a/lib/rdoc/ri/util.rb +++ b/lib/rdoc/ri/util.rb @@ -1,7 +1,5 @@ require 'rdoc/ri' -class RDoc::RI::Error < RuntimeError; end - ## # Break argument into its constituent class or module names, an # optional method type, and a method name diff --git a/test/rdoc/binary.dat b/test/rdoc/binary.dat new file mode 100644 index 0000000000..371950efe6 Binary files /dev/null and b/test/rdoc/binary.dat differ diff --git a/test/rdoc/rdoc_markup_to_html_crossref_reference.rb b/test/rdoc/rdoc_markup_to_html_crossref_reference.rb new file mode 100644 index 0000000000..cbf6734b28 --- /dev/null +++ b/test/rdoc/rdoc_markup_to_html_crossref_reference.rb @@ -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 diff --git a/test/rdoc/test_attribute_manager.rb b/test/rdoc/test_attribute_manager.rb new file mode 100644 index 0000000000..298eca0141 --- /dev/null +++ b/test/rdoc/test_attribute_manager.rb @@ -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 diff --git a/test/rdoc/test_rdoc_info_formatting.rb b/test/rdoc/test_rdoc_info_formatting.rb index 6c024f7454..b18cdf2931 100644 --- a/test/rdoc/test_rdoc_info_formatting.rb +++ b/test/rdoc/test_rdoc_info_formatting.rb @@ -5,7 +5,7 @@ require 'test/unit' require 'rdoc/generator/texinfo' # From chapter 18 of the Pickaxe 3rd ed. and the TexInfo manual. -class TestRdocInfoFormatting < Test::Unit::TestCase +class TestRDocInfoFormatting < Test::Unit::TestCase def setup @output_dir = File.join Dir.tmpdir, "test_rdoc_info_formatting_#{$$}" @output_file = File.join @output_dir, 'rdoc.texinfo' @@ -19,7 +19,7 @@ class TestRdocInfoFormatting < Test::Unit::TestCase end def teardown - FileUtils.rm_rf @output_dir + # FileUtils.rm_rf @output_dir end # Make sure tags like *this* do not make HTML @@ -73,10 +73,10 @@ class TestRdocInfoFormatting < Test::Unit::TestCase # === Everything deeper becomes a regular @heading # ====== Regardless of its nesting level def test_headings - assert_match(/@majorheading\{Huge heading should be a @@majorheading\}/) - assert_match(/@chapheading\{There is also @@chapheading\}/) - assert_match(/@heading\{Everything deeper becomes a regular @@heading\}/) - assert_match(/@heading\{Regardless of its nesting level\}/) + assert_match(/@majorheading Huge heading should be a @@majorheading/) + assert_match(/@chapheading There is also @@chapheading/) + assert_match(/@heading Everything deeper becomes a regular @@heading/) + assert_match(/@heading Regardless of its nesting level/) end # * list item diff --git a/test/rdoc/test_rdoc_info_sections.rb b/test/rdoc/test_rdoc_info_sections.rb index cceba186c1..6825d37a7d 100644 --- a/test/rdoc/test_rdoc_info_sections.rb +++ b/test/rdoc/test_rdoc_info_sections.rb @@ -6,10 +6,10 @@ require 'tmpdir' require 'rdoc/generator/texinfo' # 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 TestRdocInfoSections < Test::Unit::TestCase +class TestRDocInfoSections < Test::Unit::TestCase def setup @output_dir = File.join Dir.tmpdir, "test_rdoc_info_sections_#{$$}" diff --git a/test/rdoc/test_rdoc_markup_attribute_manager.rb b/test/rdoc/test_rdoc_markup_attribute_manager.rb index 8ba9d7440a..eea87822a8 100644 --- a/test/rdoc/test_rdoc_markup_attribute_manager.rb +++ b/test/rdoc/test_rdoc_markup_attribute_manager.rb @@ -1,5 +1,6 @@ require "test/unit" require "rdoc/markup/inline" +require "rdoc/markup/to_html_crossref" class TestRDocMarkupAttributeManager < Test::Unit::TestCase @@ -201,24 +202,23 @@ class TestRDocMarkupAttributeManager < Test::Unit::TestCase end def test_special - # class names, variable names, file names, or instance variables - @am.add_special(/( - \b([A-Z]\w+(::\w+)*) - | \#\w+[!?=]? - | \b\w+([_\/\.]+\w+)+[!?=]? - )/x, - :CROSSREF) + @am.add_special(RDoc::Markup::ToHtmlCrossref::CROSSREF_REGEXP, :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, - @am.flow("cat #fred dog")) + assert_equal(["cats' ", crossref("#fred"), " dogs'"].flatten, + @am.flow("cats' #fred dogs'")) - assert_equal([crossref("#fred"), " dog"].flatten, - @am.flow("#fred dog")) + assert_equal([crossref("#fred"), " dogs'"].flatten, + @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 - diff --git a/test/rdoc/test_rdoc_markup_to_html.rb b/test/rdoc/test_rdoc_markup_to_html.rb index 463228cc4a..05fb9f2375 100644 --- a/test/rdoc/test_rdoc_markup_to_html.rb +++ b/test/rdoc/test_rdoc_markup_to_html.rb @@ -2,7 +2,7 @@ require 'test/unit' require 'rdoc/markup' require 'rdoc/markup/to_html' -class TestRdocMarkupToHtml < Test::Unit::TestCase +class TestRDocMarkupToHtml < Test::Unit::TestCase def setup @am = RDoc::Markup::AttributeManager.new @@ -10,11 +10,23 @@ class TestRdocMarkupToHtml < Test::Unit::TestCase end def test_tt_formatting - assert_equal "

\n--(c) ©\n

\n", - util_format("-- -- (c) (c)") + assert_equal "

\n--cats' cats’\n

\n", + util_format("-- -- cats' cats'") assert_equal "

\n\n

\n", util_format("--") 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 "

\n“cats”.\n

\n", util_format("\"cats\".") + assert_equal "

\n‘cats’.\n

\n", util_format("\'cats\'.") + assert_equal "

\ncat’s-\n

\n", util_format("cat\'s-") + end + def util_fragment(text) RDoc::Markup::Fragment.new 0, nil, nil, text end diff --git a/test/rdoc/test_rdoc_markup_to_html_crossref.rb b/test/rdoc/test_rdoc_markup_to_html_crossref.rb index ab4c3e7e9c..de99a58b75 100644 --- a/test/rdoc/test_rdoc_markup_to_html_crossref.rb +++ b/test/rdoc/test_rdoc_markup_to_html_crossref.rb @@ -2,17 +2,287 @@ require 'test/unit' require 'rdoc/generator' require 'rdoc/markup/to_html_crossref' -class TestRdocMarkupToHtmlCrossref < Test::Unit::TestCase +require 'pathname' - def setup - @xref = RDoc::Markup::ToHtmlCrossref.new 'from_path', nil, nil +class TestRDocMarkupToHtmlCrossref < Test::Unit::TestCase + + # + # 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 = "

\n#{expected_result}\n

\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 = "#{reference}" + + 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 = "#{reference}" + + 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 = "#{reference}" + + 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 = "Ref_Class2::Ref_Class3;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 def test_handle_special_CROSSREF_no_underscore - out = @xref.convert 'foo' + class_hash = create_class_hash - assert_equal "

\nfoo\n

\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 - diff --git a/test/rdoc/test_rdoc_parser.rb b/test/rdoc/test_rdoc_parser.rb new file mode 100644 index 0000000000..4f71974a5f --- /dev/null +++ b/test/rdoc/test_rdoc_parser.rb @@ -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 diff --git a/test/rdoc/test_rdoc_parser_c.rb b/test/rdoc/test_rdoc_parser_c.rb index fd750070d8..9387a030c0 100644 --- a/test/rdoc/test_rdoc_parser_c.rb +++ b/test/rdoc/test_rdoc_parser_c.rb @@ -10,7 +10,7 @@ class RDoc::Parser::C public :do_classes, :do_constants end -class TestRdocParserC < Test::Unit::TestCase +class TestRDocParserC < Test::Unit::TestCase def setup @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 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) parser = util_parser content - parser.do_classes + parser.scan parser.classes[name] end diff --git a/test/rdoc/test_rdoc_parser_perl.rb b/test/rdoc/test_rdoc_parser_perl.rb new file mode 100644 index 0000000000..458e32745e --- /dev/null +++ b/test/rdoc/test_rdoc_parser_perl.rb @@ -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 + diff --git a/test/rdoc/test_rdoc_parser_ruby.rb b/test/rdoc/test_rdoc_parser_ruby.rb index 77d8bd24fc..30ab3f84f5 100644 --- a/test/rdoc/test_rdoc_parser_ruby.rb +++ b/test/rdoc/test_rdoc_parser_ruby.rb @@ -6,12 +6,16 @@ require 'rdoc/options' require 'rdoc/parser/ruby' require 'rdoc/stats' -class TestRdocParserRuby < Test::Unit::TestCase +class TestRDocParserRuby < Test::Unit::TestCase def setup @tempfile = Tempfile.new self.class.name @filename = @tempfile.path + # Some tests need two paths. + @tempfile2 = Tempfile.new self.class.name + @filename2 = @tempfile2.path + util_toplevel @options = RDoc::Options.new @options.quiet = true @@ -20,6 +24,7 @@ class TestRdocParserRuby < Test::Unit::TestCase def teardown @tempfile.close + @tempfile2.close end def test_look_for_directives_in_commented @@ -158,6 +163,105 @@ class TestRdocParserRuby < Test::Unit::TestCase assert_equal 'Super', bar.superclass 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 content = <<-EOF class Foo @@ -416,9 +520,107 @@ end @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 assert_equal 'foo2', foo2.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 def test_parse_statements_identifier_attr @@ -530,9 +732,17 @@ end @stats 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 RDoc::TopLevel.reset @top_level = RDoc::TopLevel.new @filename + @top_level2 = RDoc::TopLevel.new @filename2 end end diff --git a/test/rdoc/test_rdoc_ri_default_display.rb b/test/rdoc/test_rdoc_ri_default_display.rb index 97fa6c94ae..266b501ebb 100644 --- a/test/rdoc/test_rdoc_ri_default_display.rb +++ b/test/rdoc/test_rdoc_ri_default_display.rb @@ -4,7 +4,7 @@ require 'rdoc/ri/formatter' require 'rdoc/ri/display' require 'rdoc/ri/driver' -class TestRdocRiDefaultDisplay < Test::Unit::TestCase +class TestRDocRiDefaultDisplay < Test::Unit::TestCase def setup @output = StringIO.new @@ -27,7 +27,6 @@ class TestRdocRiDefaultDisplay < Test::Unit::TestCase end def test_display_class_info - ri_reader = nil klass = h \ 'attributes' => [ { 'name' => 'attribute', 'rw' => 'RW', @@ -43,9 +42,9 @@ class TestRdocRiDefaultDisplay < Test::Unit::TestCase ], 'comment' => [RDoc::Markup::Flow::P.new('SomeClass comment')], 'constants' => [ - { 'name' => 'CONSTANT', 'value' => '"value"', + { 'name' => 'CONSTANT', 'value' => '"value1"', 'comment' => [RDoc::Markup::Flow::P.new('CONSTANT value')] }, - { 'name' => 'CONSTANT_NOCOMMENT', 'value' => '"value"', + { 'name' => 'CONSTANT_NOCOMMENT', 'value' => '"value2"', 'comment' => nil }, ], 'display_name' => 'Class', @@ -59,7 +58,7 @@ class TestRdocRiDefaultDisplay < Test::Unit::TestCase ], 'superclass_string' => 'Object' - @dd.display_class_info klass, ri_reader + @dd.display_class_info klass expected = <<-EOF ---------------------------------------------------- Class: SomeClass < Object @@ -71,10 +70,19 @@ class TestRdocRiDefaultDisplay < Test::Unit::TestCase Constants: ---------- - CONSTANT: + CONSTANT = "value1" CONSTANT value - CONSTANT_NOCOMMENT + CONSTANT_NOCOMMENT = "value2" + + +Attributes: +----------- + + attribute (RW): + attribute comment + + attribute_no_comment (RW) Class methods: @@ -99,15 +107,6 @@ Instance method extensions: --------------------------- instance_method_extension - - -Attributes: ------------ - - attribute (RW): - attribute comment - - attribute_no_comment (RW) EOF assert_equal expected, @output.string @@ -140,7 +139,7 @@ Attributes: -------------------------------------------------------- SomeClass#some_method some_method(arg1, arg2) {|block_param| ...} - Extension from /nonexistent + From /nonexistent ------------------------------------------------------------------------------ some comment @@ -152,7 +151,7 @@ Attributes: end def test_display_method_info_singleton - method = RDoc::RI::Driver::Hash.new.update \ + method = RDoc::RI::Driver::OpenStructHash.new.update \ 'aliases' => [], 'block_params' => nil, 'comment' => nil, @@ -167,6 +166,8 @@ Attributes: expected = <<-EOF ------------------------------------------------------- SomeClass::some_method SomeClass::some_method(arg1, arg2) + + From ------------------------------------------------------------------------------ [no description] EOF @@ -176,7 +177,7 @@ Attributes: def test_display_method_list methods = [ - RDoc::RI::Driver::Hash.new.update( + RDoc::RI::Driver::OpenStructHash.new.update( "aliases" => [], "block_params" => nil, "comment" => nil, @@ -186,7 +187,7 @@ Attributes: "params" => "()", "visibility" => "public" ), - RDoc::RI::Driver::Hash.new.update( + RDoc::RI::Driver::OpenStructHash.new.update( "aliases" => [], "block_params" => nil, "comment" => nil, @@ -204,7 +205,8 @@ Attributes: More than one method matched your request. You can refine your search by asking for information on one of: - SomeClass#some_method, SomeClass#some_other_method +SomeClass#some_method [] +SomeClass#some_other_method [] EOF assert_equal expected, @output.string @@ -216,7 +218,7 @@ Attributes: expected = <<-EOF some_method(arg1, arg2) {|block_param| ...} - Extension from /nonexistent + From /nonexistent EOF assert_equal expected, @output.string @@ -234,7 +236,7 @@ some_method(start, length) some_method(index) some_method(start, length) - Extension from /nonexistent + From /nonexistent EOF assert_equal expected, @output.string @@ -249,7 +251,7 @@ some_method(start, length) expected = <<-EOF SomeClass::some_method(arg1, arg2) {|block_param| ...} - Extension from /nonexistent + From /nonexistent EOF assert_equal expected, @output.string @@ -289,8 +291,7 @@ install an additional package, or ask the packager to enable ri generation. end def h(hash) - RDoc::RI::Driver::Hash.convert hash + RDoc::RI::Driver::OpenStructHash.convert hash end end - diff --git a/test/rdoc/test_rdoc_ri_driver.rb b/test/rdoc/test_rdoc_ri_driver.rb index cddd4e60d1..22643d61d3 100644 --- a/test/rdoc/test_rdoc_ri_driver.rb +++ b/test/rdoc/test_rdoc_ri_driver.rb @@ -14,7 +14,7 @@ class TestRDocRIDriver < Test::Unit::TestCase FileUtils.mkdir_p @home_ri FileUtils.mkdir_p @cache_dir - @driver = RDoc::RI::Driver.new + @driver = RDoc::RI::Driver.new(RDoc::RI::Driver.process_args([])) @driver.homepath = @home_ri end diff --git a/test/rdoc/test_rdoc_ri_formatter.rb b/test/rdoc/test_rdoc_ri_formatter.rb index ed2ccba22d..539359033e 100644 --- a/test/rdoc/test_rdoc_ri_formatter.rb +++ b/test/rdoc/test_rdoc_ri_formatter.rb @@ -245,7 +245,7 @@ class TestRDocRIFormatter < Test::Unit::TestCase def test_raw_print_line @f.raw_print_line 'a b c' - assert_equal "a b c\n", @output.string + assert_equal "a b c", @output.string end def test_strip_attributes_b
Path:<%= files["full_path"] %>
Modified:<%= files["dtm_modified"] %>