[前][次][番号順一覧][スレッド一覧]

ruby-changes:3931

From: ko1@a...
Date: Sun, 10 Feb 2008 12:59:38 +0900 (JST)
Subject: [ruby-changes:3931] drbrain - Ruby:r15421 (trunk): * lib/rdoc/code_objects.rb: Make some attributes accessible for reuse.

drbrain	2008-02-10 12:59:08 +0900 (Sun, 10 Feb 2008)

  New Revision: 15421

  Added files:
    trunk/lib/rdoc/markup/formatter.rb
    trunk/lib/rdoc/markup/to_html_hyperlink.rb
  Modified files:
    trunk/ChangeLog
    trunk/lib/rdoc/code_objects.rb
    trunk/lib/rdoc/generator/html/html.rb
    trunk/lib/rdoc/generator/html.rb
    trunk/lib/rdoc/generator.rb
    trunk/lib/rdoc/markup/fragments.rb
    trunk/lib/rdoc/markup/inline.rb
    trunk/lib/rdoc/markup/lines.rb
    trunk/lib/rdoc/markup/to_flow.rb
    trunk/lib/rdoc/markup/to_html.rb
    trunk/lib/rdoc/markup/to_latex.rb
    trunk/lib/rdoc/markup/to_test.rb
    trunk/lib/rdoc/markup.rb
    trunk/lib/rdoc/options.rb
    trunk/lib/rdoc/rdoc.rb
    trunk/test/rdoc/test_rdoc_markup.rb
    trunk/test/rdoc/test_rdoc_markup_attribute_manager.rb
    trunk/test/rdoc/test_rdoc_ri_formatter.rb
    trunk/test/rdoc/test_rdoc_ri_overstrike_formatter.rb

  Log:
    * lib/rdoc/code_objects.rb: Make some attributes accessible for reuse.
    * lib/rdoc/generator/html.rb: Pull out ContextUser classes and related
      methods for reuse.
    * lib/rdoc/generator.rb: Move ContextUser classes to
      RDoc::Generator::Context for reuse.
    * lib/rdoc/rdoc.rb: Make RDoc::RDoc initialization a little easier.
    * lib/rdoc/options.rb: Make RDoc::Options easier to use without
      parsing an ARGV.
    * lib/rdoc/markup/to_*.rb: Subclass RDoc::Markup::Formatter.
    * lib/rdoc/markup/formatter.rb: Add RDoc::Markup::Formatter to make
      RDoc markup conversion easier.
    * lib/rdoc/markup/fragments.rb: Make RDoc::Markup::ListItem easier to
      test.
    * lib/rdoc/markup/to_html_hyperlink.rb: Pulled out of the HTML
      generator for easier reusability.
    * lib/rdoc/markup.rb: Fix bug with labeled lists containing bullet
      lists.
    * lib/rdoc/generators/html/html.rb: Fix Constant display.


  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rdoc/markup/fragments.rb?r1=15421&r2=15420&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rdoc/generator/html.rb?r1=15421&r2=15420&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/test/rdoc/test_rdoc_ri_overstrike_formatter.rb?r1=15421&r2=15420&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/test/rdoc/test_rdoc_ri_formatter.rb?r1=15421&r2=15420&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/test/rdoc/test_rdoc_markup.rb?r1=15421&r2=15420&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/ChangeLog?r1=15421&r2=15420&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rdoc/markup/to_test.rb?r1=15421&r2=15420&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rdoc/markup/to_html_hyperlink.rb?revision=15421&view=markup
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rdoc/markup/to_html_hyperlink.rb?r1=15421&r2=15420&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rdoc/markup/to_flow.rb?r1=15421&r2=15420&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rdoc/generator.rb?r1=15421&r2=15420&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rdoc/markup/formatter.rb?revision=15421&view=markup
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rdoc/markup/formatter.rb?r1=15421&r2=15420&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/test/rdoc/test_rdoc_markup_attribute_manager.rb?r1=15421&r2=15420&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rdoc/markup/to_latex.rb?r1=15421&r2=15420&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rdoc/markup/lines.rb?r1=15421&r2=15420&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rdoc/markup/inline.rb?r1=15421&r2=15420&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rdoc/code_objects.rb?r1=15421&r2=15420&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rdoc/markup.rb?r1=15421&r2=15420&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rdoc/markup/to_html.rb?r1=15421&r2=15420&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rdoc/options.rb?r1=15421&r2=15420&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rdoc/generator/html/html.rb?r1=15421&r2=15420&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rdoc/rdoc.rb?r1=15421&r2=15420&diff_format=u

Index: ChangeLog
===================================================================
--- ChangeLog	(revision 15420)
+++ ChangeLog	(revision 15421)
@@ -1,3 +1,24 @@
+Sun Feb 10 12:58:33 2008  Eric Hodel  <drbrain@s...>
+
+	* lib/rdoc/code_objects.rb: Make some attributes accessible for reuse.
+	* lib/rdoc/generator/html.rb: Pull out ContextUser classes and related
+	  methods for reuse.
+	* lib/rdoc/generator.rb: Move ContextUser classes to
+	  RDoc::Generator::Context for reuse.
+	* lib/rdoc/rdoc.rb: Make RDoc::RDoc initialization a little easier.
+	* lib/rdoc/options.rb: Make RDoc::Options easier to use without
+	  parsing an ARGV.
+	* lib/rdoc/markup/to_*.rb: Subclass RDoc::Markup::Formatter.
+	* lib/rdoc/markup/formatter.rb: Add RDoc::Markup::Formatter to make
+	  RDoc markup conversion easier.
+	* lib/rdoc/markup/fragments.rb: Make RDoc::Markup::ListItem easier to
+	  test.
+	* lib/rdoc/markup/to_html_hyperlink.rb: Pulled out of the HTML
+	  generator for easier reusability.
+	* lib/rdoc/markup.rb: Fix bug with labeled lists containing bullet
+	  lists.
+	* lib/rdoc/generators/html/html.rb: Fix Constant display.
+
 Sat Feb  9 23:44:29 2008  Tanaka Akira  <akr@f...>
 
 	* missing/tgamma.c (tgamma): use lgamma_r if available.
Index: lib/rdoc/code_objects.rb
===================================================================
--- lib/rdoc/code_objects.rb	(revision 15420)
+++ lib/rdoc/code_objects.rb	(revision 15421)
@@ -5,10 +5,10 @@
 
 module RDoc
 
-
+  ##
   # We contain the common stuff for contexts (which are containers)
   # and other elements (methods, attributes and so on)
-  #
+
   class CodeObject
 
     attr_accessor :parent
@@ -82,8 +82,7 @@
     # Access the code object's comment
     attr_reader :comment
 
-    # Update the comment, but don't overwrite a real comment
-    # with an empty one
+    # Update the comment, but don't overwrite a real comment with an empty one
     def comment=(comment)
       @comment = comment unless comment.empty?
     end
@@ -94,7 +93,7 @@
     # those directives. Wehn a comment is assigned, we then extract
     # out any matching directives and update our object
 
-    def CodeObject.attr_overridable(name, *aliases)
+    def self.attr_overridable(name, *aliases)
       @overridables ||= {}
 
       attr_accessor name
@@ -623,7 +622,7 @@
     end
   end
 
-
+  ##
   # AnyMethod is the base class for objects representing methods
 
   class AnyMethod < CodeObject
@@ -632,14 +631,18 @@
     attr_accessor :block_params
     attr_accessor :dont_rename_initialize
     attr_accessor :singleton
-    attr_reader   :aliases           # list of other names for this method
-    attr_accessor :is_alias_for      # or a method we're aliasing
+    attr_reader :text
 
+    # list of other names for this method
+    attr_reader   :aliases
+
+    # method we're aliasing
+    attr_accessor :is_alias_for
+
     attr_overridable :params, :param, :parameters, :parameter
 
     attr_accessor :call_seq
 
-
     include TokenStream
 
     def initialize(text, name)
@@ -693,7 +696,6 @@
     end
   end
 
-
   # Represent an alias, which is an old_name/ new_name pair associated
   # with a particular context
   class Alias < CodeObject
Index: lib/rdoc/generator/html/html.rb
===================================================================
--- lib/rdoc/generator/html/html.rb	(revision 15420)
+++ lib/rdoc/generator/html/html.rb	(revision 15421)
@@ -478,14 +478,14 @@
 
       <div class="name-list">
         <table summary="Constants">
-<% values["constants"].each do |constants| $stderr.puts({ :constants => constants }.inspect) %>
+<% values["constants"].each do |constants| %>
         <tr class="top-aligned-row context-row">
-          <td class="context-item-name"><%= values["name"] %></td>
+          <td class="context-item-name"><%= constants["name"] %></td>
           <td>=</td>
-          <td class="context-item-value"><%= values["value"] %></td>
+          <td class="context-item-value"><%= constants["value"] %></td>
 <% if values["desc"] then %>
           <td width="3em">&nbsp;</td>
-          <td class="context-item-desc"><%= values["desc"] %></td>
+          <td class="context-item-desc"><%= constants["desc"] %></td>
 <% end %>
         </tr>
 <% end # values["constants"] %>
Index: lib/rdoc/generator/html.rb
===================================================================
--- lib/rdoc/generator/html.rb	(revision 15420)
+++ lib/rdoc/generator/html.rb	(revision 15421)
@@ -3,1528 +3,367 @@
 require 'rdoc/generator'
 require 'rdoc/markup/to_html'
 
-module RDoc::Generator
+##
+# We're responsible for generating all the HTML files from the object tree
+# defined in code_objects.rb. We generate:
+#
+# [files]   an html file for each input file given. These
+#           input files appear as objects of class
+#           TopLevel
+#
+# [classes] an html file for each class or module encountered.
+#           These classes are not grouped by file: if a file
+#           contains four classes, we'll generate an html
+#           file for the file itself, and four html files
+#           for the individual classes.
+#
+# [indices] we generate three indices for files, classes,
+#           and methods. These are displayed in a browser
+#           like window with three index panes across the
+#           top and the selected description below
+#
+# Method descriptions appear in whatever entity (file, class, or module) that
+# contains them.
+#
+# We generate files in a structure below a specified subdirectory, normally
+# +doc+.
+#
+#  opdir
+#     |
+#     |___ files
+#     |       |__  per file summaries
+#     |
+#     |___ classes
+#             |__ per class/module descriptions
+#
+# HTML is generated using the Template class.
 
-  ##
-  # Build a hash of all items that can be cross-referenced.
-  # This is used when we output required and included names:
-  # if the names appear in this hash, we can generate
-  # an html cross reference to the appropriate description.
-  # We also use this when parsing comment blocks: any decorated
-  # words matching an entry in this list are hyperlinked.
+class RDoc::Generator::HTML
 
-  class AllReferences
-    @@refs = {}
+  include RDoc::Generator::MarkUp
 
-    def AllReferences::reset
-      @@refs = {}
-    end
+  ##
+  # Generator may need to return specific subclasses depending on the
+  # options they are passed. Because of this we create them using a factory
 
-    def AllReferences.add(name, html_class)
-      @@refs[name] = html_class
-    end
+  def self.for(options)
+    RDoc::Generator::AllReferences.reset
+    RDoc::Generator::Method.reset
 
-    def AllReferences.[](name)
-      @@refs[name]
+    if options.all_one_file
+      RDoc::Generator::HTMLInOne.new options
+    else
+      new options
     end
+  end
 
-    def AllReferences.keys
-      @@refs.keys
-    end
+  class << self
+    protected :new
   end
 
   ##
-  # Subclass of the RDoc::Markup::ToHtml class that supports looking up words
-  # in the AllReferences list. Those that are found (like AllReferences in
-  # this comment) will be hyperlinked
+  # Set up a new HTML generator. Basically all we do here is load up the
+  # correct output temlate
 
-  class HyperlinkHtml < RDoc::Markup::ToHtml
-
-    ##
-    # We need to record the html path of our caller so we can generate
-    # correct relative paths for any hyperlinks that we find
-    def initialize(from_path, context, options)
-      super()
-
-      @from_path = from_path
-
-      @parent_name = context.parent_name
-      @parent_name += "::" if @parent_name
-      @context = context
-
-      @options = options
-    end
-
-    ##
-    # We're invoked when any text matches the CROSSREF pattern
-    # (defined in MarkUp). If we fine the corresponding reference,
-    # generate a hyperlink. If the name we're looking for contains
-    # no punctuation, we look for it up the module/class chain. For
-    # example, HyperlinkHtml is found, even without the Generator::
-    # prefix, because we look for it in module Generator first.
-
-    def handle_special_CROSSREF(special)
-      name = special.text
-      if name[0,1] == '#'
-        lookup = name[1..-1]
-        name = lookup unless @options.show_hash
-      else
-        lookup = name
-      end
-
-      # Find class, module, or method in class or module.
-      if /([A-Z]\w*)[.\#](\w+[!?=]?)/ =~ lookup
-        container = $1
-        method = $2
-        ref = @context.find_symbol(container, method)
-      elsif /([A-Za-z]\w*)[.\#](\w+(\([\.\w+\*\/\+\-\=\<\>]+\))?)/ =~ lookup
-        container = $1
-        method = $2
-        ref = @context.find_symbol(container, method)
-      else
-        ref = @context.find_symbol(lookup)
-      end
-
-      if ref and ref.document_self
-        "<a href=\"#{ref.as_href(@from_path)}\">#{name}</a>"
-      else
-        name
-      end
-    end
-
-    ##
-    # Generate a hyperlink for url, labeled with text. Handle the
-    # special cases for img: and link: described under handle_special_HYPEDLINK
-
-    def gen_url(url, text)
-      if url =~ /([A-Za-z]+):(.*)/
-        type = $1
-        path = $2
-      else
-        type = "http"
-        path = url
-        url  = "http://#{url}"
-      end
-
-      if type == "link"
-        if path[0,1] == '#'     # is this meaningful?
-          url = path
-        else
-          url = HTML.gen_url(@from_path, path)
-        end
-      end
-
-      if (type == "http" || type == "link") &&
-          url =~ /\.(gif|png|jpg|jpeg|bmp)$/
-
-        "<img src=\"#{url}\" />"
-      else
-        "<a href=\"#{url}\">#{text.sub(%r{^#{type}:/*}, '')}</a>"
-      end
-    end
-
-    ##
-    # And we're invoked with a potential external hyperlink mailto:
-    # just gets inserted. http: links are checked to see if they
-    # reference an image. If so, that image gets inserted using an
-    # <img> tag. Otherwise a conventional <a href> is used.  We also
-    # support a special type of hyperlink, link:, which is a reference
-    # to a local file whose path is relative to the --op directory.
-
-    def handle_special_HYPERLINK(special)
-      url = special.text
-      gen_url(url, url)
-    end
-
-    ##
-    # Here's a hypedlink where the label is different to the URL
-    #  <label>[url]
-
-    def handle_special_TIDYLINK(special)
-      text = special.text
-#      unless text =~ /(\S+)\[(.*?)\]/
-      unless text =~ /\{(.*?)\}\[(.*?)\]/ or text =~ /(\S+)\[(.*?)\]/
-        return text
-      end
-      label = $1
-      url   = $2
-      gen_url(url, label)
-    end
-
+  def initialize(options) #:not-new:
+    @options = options
+    load_html_template
   end
 
   ##
-  # Handle common markup tasks for the various Html classes
+  # Build the initial indices and output objects
+  # based on an array of TopLevel objects containing
+  # the extracted information.
 
-  module MarkUp
+  def generate(toplevels)
+    @toplevels  = toplevels
+    @files      = []
+    @classes    = []
 
-    ##
-    # Convert a string in markup format into HTML. We keep a cached
-    # RDoc::Markup object lying around after the first time we're
-    # called per object.
+    write_style_sheet
+    gen_sub_directories()
+    build_indices
+    generate_html
+  end
 
-    def markup(str, remove_para=false)
-      return '' unless str
-      unless defined? @markup
-        @markup = RDoc::Markup.new
+  private
 
-        # class names, variable names, or instance variables
-        @markup.add_special(/(
-                               \w+(::\w+)*[.\#]\w+(\([\.\w+\*\/\+\-\=\<\>]+\))?  # A::B.meth(**) (for operator in Fortran95)
-                             | \#\w+(\([.\w\*\/\+\-\=\<\>]+\))?  #  meth(**) (for operator in Fortran95)
-                             | \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)
+  ##
+  # Load up the HTML template specified in the options.
+  # If the template name contains a slash, use it literally
 
-        # external hyperlinks
-        @markup.add_special(/((link:|https?:|mailto:|ftp:|www\.)\S+\w)/, :HYPERLINK)
+  def load_html_template
+    template = @options.template
 
-        # and links of the form  <text>[<url>]
-        @markup.add_special(/(((\{.*?\})|\b\S+?)\[\S+?\.\S+?\])/, :TIDYLINK)
-#        @markup.add_special(/\b(\S+?\[\S+?\.\S+?\])/, :TIDYLINK)
-
-      end
-      unless defined? @html_formatter
-        @html_formatter = HyperlinkHtml.new(self.path, self, @options)
-      end
-
-      # Convert leading comment markers to spaces, but only
-      # if all non-blank lines have them
-
-      if str =~ /^(?>\s*)[^\#]/
-        content = str
-      else
-        content = str.gsub(/^\s*(#+)/)  { $1.tr('#',' ') }
-      end
-
-      res = @markup.convert(content, @html_formatter)
-      if remove_para
-        res.sub!(/^<p>/, '')
-        res.sub!(/<\/p>$/, '')
-      end
-      res
+    unless template =~ %r{/|\\} then
+      template = File.join('rdoc', 'generator', @options.generator.key,
+                           template)
     end
 
-    ##
-    # Qualify a stylesheet URL; if if +css_name+ does not begin with '/' or
-    # 'http[s]://', prepend a prefix relative to +path+. Otherwise, return it
-    # unmodified.
+    require template
 
-    def style_url(path, css_name=nil)
-#      $stderr.puts "style_url( #{path.inspect}, #{css_name.inspect} )"
-      css_name ||= CSS_NAME
-      if %r{^(https?:/)?/} =~ css_name
-        return css_name
-      else
-        return HTML.gen_url(path, css_name)
-      end
-    end
+    @template = self.class.const_get @options.template.upcase
+    @options.template_class = @template
 
-    ##
-    # Build a webcvs URL with the given 'url' argument. URLs with a '%s' in them
-    # get the file's path sprintfed into them; otherwise they're just catenated
-    # together.
-
-    def cvs_url(url, full_path)
-      if /%s/ =~ url
-        return sprintf( url, full_path )
-      else
-        return url + full_path
-      end
-    end
-
+  rescue LoadError
+    $stderr.puts "Could not find HTML template '#{template}'"
+    exit 99
   end
 
   ##
-  # A Context is built by the parser to represent a container: contexts
-  # hold classes, modules, methods, require lists and include lists.
-  # ClassModule and TopLevel are the context objects we process here
+  # Write out the style sheet used by the main frames
 
-  class ContextUser
+  def write_style_sheet
+    return unless @template.constants.include? :STYLE or
+                  @template.constants.include? 'STYLE'
 
-    include MarkUp
+    template = RDoc::TemplatePage.new @template::STYLE
 
-    attr_reader :context
+    unless @options.css then
+      open RDoc::Generator::CSS_NAME, 'w' do |f|
+        values = {}
 
-    def initialize(context, options)
-      @context = context
-      @options = options
-
-      # HACK ugly
-      @template = options.template_class
-    end
-
-    ##
-    # convenience method to build a hyperlink
-
-    def href(link, cls, name)
-      %{<a href="#{link}" class="#{cls}">#{name}</a>} #"
-    end
-
-    ##
-    # Returns a reference to outselves to be used as an href= the form depends
-    # on whether we're all in one file or in multiple files
-
-    def as_href(from_path)
-      if @options.all_one_file
-        "#" + path
-      else
-        HTML.gen_url(from_path, path)
-      end
-    end
-
-    ##
-    # Create a list of HtmlMethod objects for each method in the corresponding
-    # context object. If the @options.show_all variable is set (corresponding
-    # to the <tt>--all</tt> option, we include all methods, otherwise just the
-    # public ones.
-
-    def collect_methods
-      list = @context.method_list
-      unless @options.show_all
-        list = list.find_all {|m| m.visibility == :public || m.visibility == :protected || m.force_documentation }
-      end
-      @methods = list.collect {|m| HtmlMethod.new(m, self, @options) }
-    end
-
-    ##
-    # Build a summary list of all the methods in this context
-
-    def build_method_summary_list(path_prefix="")
-      collect_methods unless @methods
-      meths = @methods.sort
-      res = []
-      meths.each do |meth|
-        res << {
-          "name" => CGI.escapeHTML(meth.name),
-          "aref" => "#{path_prefix}\##{meth.aref}"
-        }
-      end
-      res
-    end
-
-    ##
-    # Build a list of aliases for which we couldn't find a
-    # corresponding method
-
-    def build_alias_summary_list(section)
-      values = []
-      @context.aliases.each do |al|
-        next unless al.section == section
-        res = {
-          'old_name' => al.old_name,
-          'new_name' => al.new_name,
-        }
-        if al.comment && !al.comment.empty?
-          res['desc'] = markup(al.comment, true)
+        if @template.constants.include? :FONTS or
+           @template.constants.include? 'FONTS' then
+          values["fonts"] = @template::FONTS
         end
-        values << res
-      end
-      values
-    end
 
-    ##
-    # Build a list of constants
-
-    def build_constants_summary_list(section)
-      values = []
-      @context.constants.each do |co|
-        next unless co.section == section
-        res = {
-          'name'  => co.name,
-          'value' => CGI.escapeHTML(co.value)
-        }
-        res['desc'] = markup(co.comment, true) if co.comment && !co.comment.empty?
-        values << res
+        template.write_html_on(f, values)
       end
-      values
     end
+  end
 
-    def build_requires_list(context)
-      potentially_referenced_list(context.requires) {|fn| [fn + ".rb"] }
-    end
+  ##
+  # See the comments at the top for a description of the directory structure
 
-    def build_include_list(context)
-      potentially_referenced_list(context.includes)
-    end
+  def gen_sub_directories
+    FileUtils.mkdir_p RDoc::Generator::FILE_DIR
+    FileUtils.mkdir_p RDoc::Generator::CLASS_DIR
+  rescue
+    $stderr.puts $!.message
+    exit 1
+  end
 
-    ##
-    # Build a list from an array of <i>Htmlxxx</i> items. Look up each
-    # in the AllReferences hash: if we find a corresponding entry,
-    # we generate a hyperlink to it, otherwise just output the name.
-    # However, some names potentially need massaging. For example,
-    # you may require a Ruby file without the .rb extension,
-    # but the file names we know about may have it. To deal with
-    # this, we pass in a block which performs the massaging,
-    # returning an array of alternative names to match
+  def build_indices
+    @files, @classes = RDoc::Generator::Context.build_indicies(@toplevels,
+                                                               @options)
+  end
 
-    def potentially_referenced_list(array)
-      res = []
-      array.each do |i|
-        ref = AllReferences[i.name]
-#         if !ref
-#           container = @context.parent
-#           while !ref && container
-#             name = container.name + "::" + i.name
-#             ref = AllReferences[name]
-#             container = container.parent
-#           end
-#         end
+  ##
+  # Generate all the HTML
 
-        ref = @context.find_symbol(i.name)
-        ref = ref.viewer if ref
+  def generate_html
+    # the individual descriptions for files and classes
+    gen_into(@files)
+    gen_into(@classes)
+    # and the index files
+    gen_file_index
+    gen_class_index
+    gen_method_index
+    gen_main_index
 
-        if !ref && block_given?
-          possibles = yield(i.name)
-          while !ref and !possibles.empty?
-            ref = AllReferences[possibles.shift]
-          end
-        end
-        h_name = CGI.escapeHTML(i.name)
-        if ref and ref.document_self
-          path = url(ref.path)
-          res << { "name" => h_name, "aref" => path }
-        else
-          res << { "name" => h_name }
-        end
-      end
-      res
-    end
+    # this method is defined in the template file
+    write_extra_pages if defined? write_extra_pages
+  end
 
-    ##
-    # Build an array of arrays of method details. The outer array has up
-    # to six entries, public, private, and protected for both class
-    # methods, the other for instance methods. The inner arrays contain
-    # a hash for each method
-
-    def build_method_detail_list(section)
-      outer = []
-
-      methods = @methods.sort
-      for singleton in [true, false]
-        for vis in [ :public, :protected, :private ]
-          res = []
-          methods.each do |m|
-            if m.section == section and
-                m.document_self and
-                m.visibility == vis and
-                m.singleton == singleton
-              row = {}
-              if m.call_seq
-                row["callseq"] = m.call_seq.gsub(/->/, '&rarr;')
-              else
-                row["name"]        = CGI.escapeHTML(m.name)
-                row["params"]      = m.params
-              end
-              desc = m.description.strip
-              row["m_desc"]      = desc unless desc.empty?
-              row["aref"]        = m.aref
-              row["visibility"]  = m.visibility.to_s
-
-              alias_names = []
-              m.aliases.each do |other|
-                if other.viewer   # won't be if the alias is private
-                  alias_names << {
-                    'name' => other.name,
-                    'aref'  => other.viewer.as_href(path)
-                  }
-                end
-              end
-              unless alias_names.empty?
-                row["aka"] = alias_names
-              end
-
-              if @options.inline_source
-                code = m.source_code
-                row["sourcecode"] = code if code
-              else
-                code = m.src_url
-                if code
-                  row["codeurl"] = code
-                  row["imgurl"]  = m.img_url
-                end
-              end
-              res << row
-            end
-          end
-          if res.size > 0
-            outer << {
-              "type"     => vis.to_s.capitalize,
-              "category" => singleton ? "Class" : "Instance",
-              "methods"  => res
-            }
-          end
-        end
+  def gen_into(list)
+    list.each do |item|
+      if item.document_self
+        op_file = item.path
+        FileUtils.mkdir_p(File.dirname(op_file))
+        open(op_file, "w") { |file| item.write_on(file) }
       end
-      outer
     end
 
-    ##
-    # Build the structured list of classes and modules contained
-    # in this context.
+  end
 
-    def build_class_list(level, from, section, infile=nil)
-      res = ""
-      prefix = "&nbsp;&nbsp;::" * level;
+  def gen_file_index
+    gen_an_index @files, 'Files', @template::FILE_INDEX, "fr_file_index.html"
+  end
 
-      from.modules.sort.each do |mod|
-        next unless mod.section == section
-        next if infile && !mod.defined_in?(infile)
-        if mod.document_self
-          res <<
-            prefix <<
-            "Module " <<
-            href(url(mod.viewer.path), "link", mod.full_name) <<
-            "<br />\n" <<
-            build_class_list(level + 1, mod, section, infile)
-        end
-      end
+  def gen_class_index
+    gen_an_index(@classes, 'Classes', @template::CLASS_INDEX,
+                 "fr_class_index.html")
+  end
 
-      from.classes.sort.each do |cls|
-        next unless cls.section == section
-        next if infile && !cls.defined_in?(infile)
-        if cls.document_self
-          res      <<
-            prefix <<
-            "Class " <<
-            href(url(cls.viewer.path), "link", cls.full_name) <<
-            "<br />\n" <<
-            build_class_list(level + 1, cls, section, infile)
-        end
-      end
+  def gen_method_index
+    gen_an_index(RDoc::Generator::Method.all_methods, 'Methods',
+                 @template::METHOD_INDEX, "fr_method_index.html")
+  end
 
-      res
-    end
-
-    def url(target)
-      HTML.gen_url(path, target)
-    end
-
-    def aref_to(target)
-      if @options.all_one_file
-        "#" + target
-      else
-        url(target)
+  def gen_an_index(collection, title, template, filename)
+    template = RDoc::TemplatePage.new @template::FR_INDEX_BODY, template
+    res = []
+    collection.sort.each do |f|
+      if f.document_self
+        res << { "href" => f.path, "name" => f.index_name }
       end
     end
 
-    def document_self
-      @context.document_self
-    end
+    values = {
+      "entries"    => res,
+      'list_title' => CGI.escapeHTML(title),
+      'index_url'  => main_url,
+      'charset'    => @options.charset,
+      'style_url'  => style_url('', @options.css),
+    }
 
-    def diagram_reference(diagram)
-      res = diagram.gsub(/((?:src|href)=")(.*?)"/) {
-        $1 + url($2) + '"'
-      }
-      res
+    open filename, 'w' do |f|
+      template.write_html_on(f, values)
     end
-
-    ##
-    # Find a symbol in ourselves or our parent
-
-    def find_symbol(symbol, method=nil)
-      res = @context.find_symbol(symbol, method)
-      if res
-        res = res.viewer
-      end
-      res
-    end
-
-    ##
-    # create table of contents if we contain sections
-
-    def add_table_of_sections
-      toc = []
-      @context.sections.each do |section|
-        if section.title
-          toc << {
-            'secname' => section.title,
-            'href'    => section.sequence
-          }
-        end
-      end
-
-      @values['toc'] = toc unless toc.empty?
-    end
-
   end
 
   ##
-  # Wrap a ClassModule context
+  # The main index page is mostly a template frameset, but includes the
+  # initial page. If the <tt>--main</tt> option was given, we use this as
+  # our main page, otherwise we use the first file specified on the command
+  # line.
 
-  class HtmlClass < ContextUser
+  def gen_main_index
+    template = RDoc::TemplatePage.new @template::INDEX
 
-    attr_reader :path
+    open 'index.html', 'w'  do |f|
+      classes = @classes.sort.map { |klass| klass.value_hash }
 
-    def initialize(context, html_file, prefix, options)
-      super(context, options)
+      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,
+      }
 
-      @html_file = html_file
-      @is_module = context.is_module?
-      @values    = {}
+      values['inline_source'] = @options.inline_source
 
-      context.viewer = self
-
-      if options.all_one_file
-        @path = context.full_name
-      else
-        @path = http_url(context.full_name, prefix)
-      end
-
-      collect_methods
-
-      AllReferences.add(name, self)
+      template.write_html_on f, values
     end
-
-    ##
-    # Returns the relative file name to store this class in, which is also its
-    # url
-
-    def http_url(full_name, prefix)
-      path = full_name.dup
-      if path['<<']
-        path.gsub!(/<<\s*(\w*)/) { "from-#$1" }
-      end
-      File.join(prefix, path.split("::")) + ".html"
-    end
-
-    def name
-      @context.full_name
-    end
-
-    def parent_name
-      @context.parent.full_name
-    end
-
-    def index_name
-      name
-    end
-
-    def write_on(f)
-      value_hash
-      template = RDoc::TemplatePage.new(@template::BODY,
-                                        @template::CLASS_PAGE,
-                                        @template::METHOD_LIST)
-      template.write_html_on(f, @values)
-    end
-
-    def value_hash
-      class_attribute_values
-      add_table_of_sections
-
-      @values["charset"] = @options.charset
-      @values["style_url"] = style_url(path, @options.css)
-
-      d = markup(@context.comment)
-      @values["description"] = d unless d.empty?
-
-      ml = build_method_summary_list @path
-      @values["methods"] = ml unless ml.empty?
-
-      il = build_include_list(@context)
-      @values["includes"] = il unless il.empty?
-
-      @values["sections"] = @context.sections.map do |section|
-
-        secdata = {
-          "sectitle" => section.title,
-          "secsequence" => section.sequence,
-          "seccomment" => markup(section.comment)
-        }
-
-        al = build_alias_summary_list(section)
-        secdata["aliases"] = al unless al.empty?
-
-        co = build_constants_summary_list(section)
-        secdata["constants"] = co unless co.empty?
-
-        al = build_attribute_list(section)
-        secdata["attributes"] = al unless al.empty?
-
-        cl = build_class_list(0, @context, section)
-        secdata["classlist"] = cl unless cl.empty?
-
-        mdl = build_method_detail_list(section)
-        secdata["method_list"] = mdl unless mdl.empty?
-
-        secdata
-      end
-
-      @values
-    end
-
-    def build_attribute_list(section)
-      atts = @context.attributes.sort
-      res = []
-      atts.each do |att|
-        next unless att.section == section
-        if att.visibility == :public || att.visibility == :protected || @options.show_all
-          entry = {
-            "name"   => CGI.escapeHTML(att.name),
-            "rw"     => att.rw,
-            "a_desc" => markup(att.comment, true)
-          }
-          unless att.visibility == :public || att.visibility == :protected
-            entry["rw"] << "-"
-          end
-          res << entry
-        end
-      end
-      res
-    end
-
-    def class_attribute_values
-      h_name = CGI.escapeHTML(name)
-
-      @values["path"]      = @path
-      @values["classmod"]  = @is_module ? "Module" : "Class"
-      @values["title"]     = "#{@values['classmod']}: #{h_name}"
-
-      c = @context
-      c = c.parent while c and !c.diagram
-      if c && c.diagram
-        @values["diagram"] = diagram_reference(c.diagram)
-      end
-
-      @values["full_name"] = h_name
-
-      parent_class = @context.superclass
-
-      if parent_class
-        @values["parent"] = CGI.escapeHTML(parent_class)
-
-        if parent_name
-          lookup = parent_name + "::" + parent_class
-        else
-          lookup = parent_class
-        end
-
-        parent_url = AllReferences[lookup] || AllReferences[parent_class]
-
-        if parent_url and parent_url.document_self
-          @values["par_url"] = aref_to(parent_url.path)
-        end
-      end
-
-      files = []
-      @context.in_files.each do |f|
-        res = {}
-        full_path = CGI.escapeHTML(f.file_absolute_name)
-
-        res["full_path"]     = full_path
-        res["full_path_url"] = aref_to(f.viewer.path) if f.document_self
-
-        if @options.webcvs
-          res["cvsurl"] = cvs_url( @options.webcvs, full_path )
-        end
-
-        files << res
-      end
-
-      @values['infiles'] = files
-    end
-
-    def <=>(other)
-      self.name <=> other.name
-    end
-
   end
 
   ##
-  # Handles the mapping of a file's information to HTML. In reality,
-  # a file corresponds to a +TopLevel+ object, containing modules,
-  # classes, and top-level methods. In theory it _could_ contain
-  # attributes and aliases, but we ignore these for now.
+  # Returns the url of the main page
 
-  class HtmlFile < ContextUser
-
-    attr_reader :path
-    attr_reader :name
-
-    def initialize(context, options, file_dir)
-      super(context, options)
-
-      @values = {}
-
-      if options.all_one_file
-        @path = filename_to_label
+  def main_url
+    @main_page = @options.main_page
+    @main_page_ref = nil
+    if @main_page
+      @main_page_ref = AllReferences[@main_page]
+      if @main_page_ref then
+        @main_page_path = @main_page_ref.path
       else
-        @path = http_url(file_dir)
+        $stderr.puts "Could not find main page #{@main_page}"
       end
-
-      @name = @context.file_relative_name
-
-      collect_methods
-      AllReferences.add(name, self)
-      context.viewer = self
     end
 
-    def http_url(file_dir)
-      File.join(file_dir, @context.file_relative_name.tr('.', '_')) +
-        ".html"
+    unless @main_page_path then
+      file = @files.find { |file| file.document_self }
+      @main_page_path = file.path if file
     end
 
-    def filename_to_label
-      @context.file_relative_name.gsub(/%|\/|\?|\#/) do |s|
-        '%%%x' % s[0].unpack('C')
-      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
     end
 
-    def index_name
-      name
-    end
+    @main_page_path
+  end
 
-    def parent_name
-      nil
-    end
+end
 
-    def value_hash
-      file_attribute_values
-      add_table_of_sections
+class RDoc::Generator::HTMLInOne < RDoc::Generator::HTML
 
-      @values["charset"]   = @options.charset
-      @values["href"]      = path
-      @values["style_url"] = style_url(path, @options.css)
-
-      if @context.comment
-        d = markup(@context.comment)
-        @values["description"] = d if d.size > 0
-      end
-
-      ml = build_method_summary_list
-      @values["methods"] = ml unless ml.empty?
-
-      il = build_include_list(@context)
-      @values["includes"] = il unless il.empty?
-
-      rl = build_requires_list(@context)
-      @values["requires"] = rl unless rl.empty?
-
-      if @options.promiscuous
-        file_context = nil
-      else
-        file_context = @context
-      end
-
-
-      @values["sections"] = @context.sections.map do |section|
-
-        secdata = {
-          "sectitle" => section.title,
-          "secsequence" => section.sequence,
-          "seccomment" => markup(section.comment)
-        }
-
-        cl = build_class_list(0, @context, section, file_context)
-        @values["classlist"] = cl unless cl.empty?
-
-        mdl = build_method_detail_list(section)
-        secdata["method_list"] = mdl unless mdl.empty?
-
-        al = build_alias_summary_list(section)
-        secdata["aliases"] = al unless al.empty?
-
-        co = build_constants_summary_list(section)
-        @values["constants"] = co unless co.empty?
-
-        secdata
-      end
-
-      @values
-    end
-
-    def write_on(f)
-      value_hash
-
-      template = RDoc::TemplatePage.new(@template::BODY,
-                                        @template::FILE_PAGE,
-                                        @template::METHOD_LIST)
-
-      template.write_html_on(f, @values)
-    end
-
-    def file_attribute_values
-      full_path = @context.file_absolute_name
-      short_name = File.basename(full_path)
-
-      @values["title"] = CGI.escapeHTML("File: #{short_name}")
-
-      if @context.diagram
-        @values["diagram"] = diagram_reference(@context.diagram)
-      end
-
-      @values["short_name"]   = CGI.escapeHTML(short_name)
-      @values["full_path"]    = CGI.escapeHTML(full_path)
-      @values["dtm_modified"] = @context.file_stat.mtime.to_s
-
-      if @options.webcvs
-        @values["cvsurl"] = cvs_url( @options.webcvs, @values["full_path"] )
-      end
-    end
-
-    def <=>(other)
-      self.name <=> other.name
-    end
-
+  def initialize(*args)
+    super
   end
 
-  class HtmlMethod
+  ##
+  # Build the initial indices and output objects
+  # based on an array of TopLevel objects containing
+  # the extracted information.
 
-    include MarkUp
+  def generate(info)
+    @toplevels  = info
+    @hyperlinks = {}
 
-    attr_reader :context
-    attr_reader :src_url
-    attr_reader :img_url
-    attr_reader :source_code
-
-    @@seq = "M000000"
-
-    @@all_methods = []
-
-    def self.reset
-      @@all_methods = []
-    end
-
-    def initialize(context, html_class, options)
-      @context    = context
-      @html_class = html_class
-      @options    = options
-
-      # HACK ugly
-      @template = options.template_class
-
-      @@seq       = @@seq.succ
-      @seq        = @@seq
-      @@all_methods << self
-
-      context.viewer = self
-
-      if (ts = @context.token_stream)
-        @source_code = markup_code(ts)
-        unless @options.inline_source
-          @src_url = create_source_code_file(@source_code)
-          @img_url = HTML.gen_url(path, 'source.png')
-        end
-      end
-
-      AllReferences.add(name, self)
-    end
-
-    ##
-    # Returns a reference to outselves to be used as an href= the form depends
-    # on whether we're all in one file or in multiple files
-
-    def as_href(from_path)
-      if @options.all_one_file
-        "#" + path
-      else
-        HTML.gen_url(from_path, path)
-      end
-    end
-
-    def name
-      @context.name
-    end
-
-    def section
-      @context.section
-    end
-
-    def index_name
-      "#{@context.name} (#{@html_class.name})"
-    end
-
-    def parent_name
-      if @context.parent.parent
-        @context.parent.parent.full_name
-      else
-        nil
-      end
-    end
-
-    def aref
-      @seq
-    end
-
-    def path
-      if @options.all_one_file
-        aref
-      else
-        @html_class.path + "#" + aref
-      end
-    end
-
-    def description
-      markup(@context.comment)
-    end
-
-    def visibility
-      @context.visibility
-    end
-
-    def singleton
-      @context.singleton
-    end
-
-    def call_seq
-      cs = @context.call_seq
-      if cs
-        cs.gsub(/\n/, "<br />\n")
-      else
-        nil
-      end
-    end
-
-    def params
-      # params coming from a call-seq in 'C' will start with the
-      # method name
-      if p !~ /^\w/
-        p = @context.params.gsub(/\s*\#.*/, '')
-        p = p.tr("\n", " ").squeeze(" ")
-        p = "(" + p + ")" unless p[0] == ?(
-
-        if (block = @context.block_params)
-         # If this method has explicit block parameters, remove any
-         # explicit &block
-
-         p.sub!(/,?\s*&\w+/, '')
-
-          block.gsub!(/\s*\#.*/, '')
-          block = block.tr("\n", " ").squeeze(" ")
-          if block[0] == ?(
-            block.sub!(/^\(/, '').sub!(/\)/, '')
-          end
-          p << " {|#{block.strip}| ...}"
-        end
-      end
-      CGI.escapeHTML(p)
-    end
-
-    def create_source_code_file(code_body)
-      meth_path = @html_class.path.sub(/\.html$/, '.src')
-      FileUtils.mkdir_p(meth_path)
-      file_path = File.join(meth_path, @seq) + ".html"
-
-      template = RDoc::TemplatePage.new(@template::SRC_PAGE)
-      File.open(file_path, "w") do |f|
-        values = {
-          'title'     => CGI.escapeHTML(index_name),
-          'code'      => code_body,
-          'style_url' => style_url(file_path, @options.css),
-          'charset'   => @options.charset
-        }
-        template.write_html_on(f, values)
-      end
-      HTML.gen_url(path, file_path)
-    end
-
-    def self.all_methods
-      @@all_methods
-    end
-
-    def <=>(other)
-      @context <=> other.context
-    end
-
-    ##
-    # Given a sequence of source tokens, mark up the source code
-    # to make it look purty.
-
-    def markup_code(tokens)
-      src = ""
-      tokens.each do |t|
-        next unless t
-        #    p t.class
-#        style = STYLE_MAP[t.class]
-        style = case t
-                when RubyToken::TkCONSTANT then "ruby-constant"
-                when RubyToken::TkKW       then "ruby-keyword kw"
-                when RubyToken::TkIVAR     then "ruby-ivar"
-                when RubyToken::TkOp       then "ruby-operator"
-                when RubyToken::TkId       then "ruby-identifier"
-                when RubyToken::TkNode     then "ruby-node"
-                when RubyToken::TkCOMMENT  then "ruby-comment cmt"
-                when RubyToken::TkREGEXP   then "ruby-regexp re"
-                when RubyToken::TkSTRING   then "ruby-value str"
-                when RubyToken::TkVal      then "ruby-value"
-                else
-                    nil
-                end
-
-        text = CGI.escapeHTML(t.text)
-
-        if style
-          src << "<span class=\"#{style}\">#{text}</span>"
-        else
-          src << text
-        end
-      end
-
-      add_line_numbers(src) if @options.include_line_numbers
-      src
-    end
-
-    ##
-    # We rely on the fact that the first line of a source code listing has
-    #    # File xxxxx, line dddd
-
-    def add_line_numbers(src)
-      if src =~ /\A.*, line (\d+)/
-        first = $1.to_i - 1
-        last  = first + src.count("\n")
-        size = last.to_s.length
-        real_fmt = "%#{size}d: "
-        fmt = " " * (size+2)
-        src.gsub!(/^/) do
-          res = sprintf(fmt, first)
-          first += 1
-          fmt = real_fmt
-          res
-        end
-      end
-    end
-
-    def document_self
-      @context.document_self
-    end
-
-    def aliases
-      @context.aliases
-    end
-
-    def find_symbol(symbol, method=nil)
-      res = @context.parent.find_symbol(symbol, method)
-      if res
-        res = res.viewer
-      end
-      res
-    end
-
+    build_indices
+    generate_xml
   end
 
   ##
-  # We're responsible for generating all the HTML files
-  # from the object tree defined in code_objects.rb. We
-  # generate:
+  # Generate:
   #
-  # [files]   an html file for each input file given. These
-  #           input files appear as objects of class
-  #           TopLevel
-  #
-  # [classes] an html file for each class or module encountered.
-  #           These classes are not grouped by file: if a file
-  #           contains four classes, we'll generate an html
-  #           file for the file itself, and four html files
-  #           for the individual classes.
-  #
-  # [indices] we generate three indices for files, classes,
-  #           and methods. These are displayed in a browser
-  #           like window with three index panes across the
-  #           top and the selected description below
-  #
-  # Method descriptions appear in whatever entity (file, class,
-  # or module) that contains them.
-  #
-  # We generate files in a structure below a specified subdirectory,
-  # normally +doc+.
-  #
-  #  opdir
-  #     |
-  #     |___ files
-  #     |       |__  per file summaries
-  #     |
-  #     |___ classes
-  #             |__ per class/module descriptions
-  #
-  # HTML is generated using the Template class.
+  # * a list of RDoc::Generator::File objects for each TopLevel object.
+  # * a list of RDoc::Generator::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)
 
-  class HTML
+  def build_indices
+    @files, @classes = RDoc::Generator::Context.build_indices(@toplevels,
+                                                              @options)
+  end
 
-    include MarkUp
+  ##
+  # Generate all the HTML. For the one-file case, we generate
+  # all the information in to one big hash
 
-    ##
-    # Converts a target url to one that is relative to a given path
+  def generate_xml
+    values = {
+      'charset' => @options.charset,
+      'files'   => gen_into(@files),
+      'classes' => gen_into(@classes),
+      'title'        => CGI.escapeHTML(@options.title),
+    }
 
-    def self.gen_url(path, target)
-      from          = File.dirname(path)
-      to, to_file   = File.split(target)
+    # this method is defined in the template file
+    write_extra_pages if defined? write_extra_pages
 
-      from = from.split("/")
-      to   = to.split("/")
+    template = RDoc::TemplatePage.new @template::ONE_PAGE
 
-      while from.size > 0 and to.size > 0 and from[0] == to[0]
-        from.shift
-        to.shift
-      end
-
-      from.fill("..")
-      from.concat(to)
-      from << to_file
-      File.join(*from)
+    if @options.op_name
+      opfile = open @options.op_name, 'w'
+    else
+      opfile = $stdout
     end
+    template.write_html_on(opfile, values)
+  end
 
-    ##
-    # Generator may need to return specific subclasses depending on the
-    # options they are passed. Because of this we create them using a factory
-
-    def self.for(options)
-      AllReferences.reset
-      HtmlMethod.reset
-
-      if options.all_one_file
-        HTMLInOne.new(options)
-      else
-        HTML.new(options)
-      end
+  def gen_into(list)
+    res = []
+    list.each do |item|
+      res << item.value_hash
     end
+    res
+  end
 
-    class <<self
-      protected :new
-    end
+  def gen_file_index
+    gen_an_index(@files, 'Files')
+  end
 
-    ##
-    # Set up a new HTML generator. Basically all we do here is load up the
-    # correct output temlate
+  def gen_class_index
+    gen_an_index(@classes, 'Classes')
+  end
 
-    def initialize(options) #:not-new:
-      @options    = options
-      load_html_template
-    end
-
-    ##
-    # Build the initial indices and output objects
-    # based on an array of TopLevel objects containing
-    # the extracted information.
-
-    def generate(toplevels)
-      @toplevels  = toplevels
-      @files      = []
-      @classes    = []
-
-      write_style_sheet
-      gen_sub_directories()
-      build_indices
-      generate_html
-    end
-
-    private
-
-    ##
-    # Load up the HTML template specified in the options.
-    # If the template name contains a slash, use it literally
-
-    def load_html_template
-      template = @options.template
-
-      unless template =~ %r{/|\\} then
-        template = File.join('rdoc', 'generator', @options.generator.key,
-                             template)
-      end
-
-      require template
-
-      @template = self.class.const_get @options.template.upcase
-      @options.template_class = @template
-
-    rescue LoadError
-      $stderr.puts "Could not find HTML template '#{template}'"
-      exit 99
-    end
-
-    ##
-    # Write out the style sheet used by the main frames
-
-    def write_style_sheet
-      return unless @template.constants.include? :STYLE or
-                    @template.constants.include? 'STYLE'
-
-      template = RDoc::TemplatePage.new @template::STYLE
-
-      unless @options.css then
-        File.open(CSS_NAME, "w") do |f|
-          values = {}
-
-          if @template.constants.include? :FONTS or
-             @template.constants.include? 'FONTS' then
-            values["fonts"] = @template::FONTS
-          end
-
-          template.write_html_on(f, values)
-        end
-      end
-    end
-
-    ##
-    # See the comments at the top for a description of the directory structure
-
-    def gen_sub_directories
-      FileUtils.mkdir_p(FILE_DIR)
-      FileUtils.mkdir_p(CLASS_DIR)
-    rescue
-      $stderr.puts $!.message
-      exit 1
-    end
-
-    ##
-    # Generate:
-    #
-    # * a list of HtmlFile objects for each TopLevel object.
-    # * a list of HtmlClass 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
-      @toplevels.each do |toplevel|
-        @files << HtmlFile.new(toplevel, @options, FILE_DIR)
-      end
-
-      RDoc::TopLevel.all_classes_and_modules.each do |cls|
-        build_class_list(cls, @files[0], CLASS_DIR)
-      end
-    end
-
-    def build_class_list(from, html_file, class_dir)
-      @classes << HtmlClass.new(from, html_file, class_dir, @options)
-      from.each_classmodule do |mod|
-        build_class_list(mod, html_file, class_dir)
-      end
-    end
-
-    ##
-    # Generate all the HTML
-
-    def generate_html
-      # the individual descriptions for files and classes
-      gen_into(@files)
-      gen_into(@classes)
-      # and the index files
-      gen_file_index
-      gen_class_index
-      gen_method_index
-      gen_main_index
-
-      # this method is defined in the template file
-      write_extra_pages if defined? write_extra_pages
-    end
-
-    def gen_into(list)
-      list.each do |item|
-        if item.document_self
-          op_file = item.path
-          FileUtils.mkdir_p(File.dirname(op_file))
-          File.open(op_file, "w") { |file| item.write_on(file) }
-        end
-      end
-
-    end
-
-    def gen_file_index
-      gen_an_index @files, 'Files', @template::FILE_INDEX, "fr_file_index.html"
-    end
-
-    def gen_class_index
-      gen_an_index(@classes, 'Classes', @template::CLASS_INDEX,
-                   "fr_class_index.html")
-    end
-
-    def gen_method_index
-      gen_an_index(HtmlMethod.all_methods, 'Methods', @template::METHOD_INDEX,
-                   "fr_method_index.html")
-    end
-
-    def gen_an_index(collection, title, template, filename)
-      template = RDoc::TemplatePage.new @template::FR_INDEX_BODY, template
-      res = []
-      collection.sort.each do |f|
-        if f.document_self
-          res << { "href" => f.path, "name" => f.index_name }
-        end
-      end
-
-      values = {
-        "entries"    => res,
-        'list_title' => CGI.escapeHTML(title),
-        'index_url'  => main_url,
-        'charset'    => @options.charset,
-        'style_url'  => style_url('', @options.css),
-      }
-
-      File.open(filename, "w") do |f|
-        template.write_html_on(f, values)
-      end
-    end
-
-    ##
-    # The main index page is mostly a template frameset, but includes the
-    # initial page. If the <tt>--main</tt> option was given, we use this as
-    # our main page, otherwise we use the first file specified on the command
-    # line.
-
-    def gen_main_index
-      template = RDoc::TemplatePage.new @template::INDEX
-
-      open 'index.html', 'w'  do |f|
-        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
-
-        template.write_html_on f, values
-      end
-    end
-
-    ##
-    # Returns the url of the main page
-
-    def main_url
-      @main_page = @options.main_page
-      @main_page_ref = nil
-      if @main_page
-        @main_page_ref = AllReferences[@main_page]
-        if @main_page_ref then
-          @main_page_path = @main_page_ref.path
-        else
-          $stderr.puts "Could not find main page #{@main_page}"
-        end
-      end
-
-      unless @main_page_path then
-        file = @files.find { |file| file.document_self }
-        @main_page_path = file.path if file
-      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
-      end
-
-      @main_page_path
-    end
-
+  def gen_method_index
+    gen_an_index(RDoc::Generator::Method.all_methods, 'Methods')
   end
 
-  class HTMLInOne < HTML
-
-    def initialize(*args)
-      super
-    end
-
-    ##
-    # Build the initial indices and output objects
-    # based on an array of TopLevel objects containing
-    # the extracted information.
-
-    def generate(info)
-      @toplevels  = info
-      @files      = []
-      @classes    = []
-      @hyperlinks = {}
-
-      build_indices
-      generate_xml
-    end
-
-    ##
-    # Generate:
-    #
-    # * a list of HtmlFile objects for each TopLevel object.
-    # * a list of HtmlClass 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
-
-      @toplevels.each do |toplevel|
-        @files << HtmlFile.new(toplevel, @options, FILE_DIR)
+  def gen_an_index(collection, title)
+    res = []
+    collection.sort.each do |f|
+      if f.document_self
+        res << { "href" => f.path, "name" => f.index_name }
       end
-
-      RDoc::TopLevel.all_classes_and_modules.each do |cls|
-        build_class_list(cls, @files[0], CLASS_DIR)
-      end
     end
 
-    def build_class_list(from, html_file, class_dir)
-      @classes << HtmlClass.new(from, html_file, class_dir, @options)
-      from.each_classmodule do |mod|
-        build_class_list(mod, html_file, class_dir)
-      end
-    end
-
-    ##
-    # Generate all the HTML. For the one-file case, we generate
-    # all the information in to one big hash
-
-    def generate_xml
-      values = {
-        'charset' => @options.charset,
-        'files'   => gen_into(@files),
-        'classes' => gen_into(@classes),
-        '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
-        opfile = File.open(@options.op_name, "w")
-      else
-        opfile = $stdout
-      end
-      template.write_html_on(opfile, values)
-    end
-
-    def gen_into(list)
-      res = []
-      list.each do |item|
-        res << item.value_hash
-      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(HtmlMethod.all_methods, 'Methods')
-    end
-
-    def gen_an_index(collection, title)
-      res = []
-      collection.sort.each do |f|
-        if f.document_self
-          res << { "href" => f.path, "name" => f.index_name }
-        end
-      end
-
-      return {
-        "entries" => res,
-        'list_title' => title,
-        'index_url'  => main_url,
-      }
-    end
-
+    return {
+      "entries" => res,
+      'list_title' => title,
+      'index_url'  => main_url,
+    }
   end
 
 end
 
+
Index: lib/rdoc/generator.rb
===================================================================
--- lib/rdoc/generator.rb	(revision 15420)
+++ lib/rdoc/generator.rb	(revision 15421)
@@ -1,7 +1,7 @@
 require 'cgi'
 require 'rdoc'
 require 'rdoc/options'
-require 'rdoc/markup'
+require 'rdoc/markup/to_html_hyperlink'
 require 'rdoc/template'
 
 module RDoc::Generator
@@ -21,5 +21,1027 @@
 
   CSS_NAME  = "rdoc-style.css"
 
+  ##
+  # Converts a target url to one that is relative to a given path
+
+  def self.gen_url(path, target)
+    from          = ::File.dirname path
+    to, to_file   = ::File.split target
+
+    from = from.split "/"
+    to   = to.split "/"
+
+    while from.size > 0 and to.size > 0 and from[0] == to[0] do
+      from.shift
+      to.shift
+    end
+
+    from.fill ".."
+    from.concat to
+    from << to_file
+    ::File.join(*from)
+  end
+
+  ##
+  # Build a hash of all items that can be cross-referenced.  This is used when
+  # we output required and included names: if the names appear in this hash,
+  # we can generate an html cross reference to the appropriate description.
+  # We also use this when parsing comment blocks: any decorated words matching
+  # an entry in this list are hyperlinked.
+
+  class AllReferences
+    @@refs = {}
+
+    def AllReferences::reset
+      @@refs = {}
+    end
+
+    def AllReferences.add(name, html_class)
+      @@refs[name] = html_class
+    end
+
+    def AllReferences.[](name)
+      @@refs[name]
+    end
+
+    def AllReferences.keys
+      @@refs.keys
+    end
+  end
+
+  ##
+  # Handle common markup tasks for the various Context subclasses
+
+  module MarkUp
+
+    ##
+    # Convert a string in markup format into HTML.
+
+    def markup(str, remove_para = false)
+      return '' unless str
+
+      unless defined? @formatter then
+        @formatter = RDoc::Markup::ToHtmlHyperlink.new(path, self,
+                                                       @options.show_hash)
+      end
+
+      # Convert leading comment markers to spaces, but only if all non-blank
+      # lines have them
+      if str =~ /^(?>\s*)[^\#]/ then
+        content = str
+      else
+        content = str.gsub(/^\s*(#+)/) { $1.tr '#', ' ' }
+      end
+
+      res = @formatter.convert content
+
+      if remove_para then
+        res.sub!(/^<p>/, '')
+        res.sub!(/<\/p>$/, '')
+      end
+
+      res
+    end
+
+    ##
+    # Qualify a stylesheet URL; if if +css_name+ does not begin with '/' or
+    # 'http[s]://', prepend a prefix relative to +path+. Otherwise, return it
+    # unmodified.
+
+    def style_url(path, css_name=nil)
+#      $stderr.puts "style_url( #{path.inspect}, #{css_name.inspect} )"
+      css_name ||= CSS_NAME
+      if %r{^(https?:/)?/} =~ css_name
+        css_name
+      else
+        RDoc::Generator.gen_url path, css_name
+      end
+    end
+
+    ##
+    # Build a webcvs URL with the given 'url' argument. URLs with a '%s' in them
+    # get the file's path sprintfed into them; otherwise they're just catenated
+    # together.
+
+    def cvs_url(url, full_path)
+      if /%s/ =~ url
+        return sprintf( url, full_path )
+      else
+        return url + full_path
+      end
+    end
+
+  end
+
+  ##
+  # A Context is built by the parser to represent a container: contexts hold
+  # classes, modules, methods, require lists and include lists.  ClassModule
+  # and TopLevel are the context objects we process here
+
+  class Context
+
+    include MarkUp
+
+    attr_reader :context
+
+    ##
+    # Generate:
+    #
+    # * a list of RDoc::Generator::File objects for each TopLevel object
+    # * a list of RDoc::Generator::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 self.build_indicies(toplevels, options)
+      files = []
+      classes = []
+
+      toplevels.each do |toplevel|
+        files << RDoc::Generator::File.new(toplevel, options,
+                                           RDoc::Generator::FILE_DIR)
+      end
+
+      RDoc::TopLevel.all_classes_and_modules.each do |cls|
+        build_class_list(classes, options, cls, files[0], 
+                         RDoc::Generator::CLASS_DIR)
+      end
+
+      return files, classes
+    end
+
+    def self.build_class_list(classes, options, from, html_file, class_dir)
+      classes << RDoc::Generator::Class.new(from, html_file, class_dir, options)
+
+      from.each_classmodule do |mod|
+        build_class_list(classes, options, mod, html_file, class_dir)
+      end
+    end
+
+    def initialize(context, options)
+      @context = context
+      @options = options
+
+      # HACK ugly
+      @template = options.template_class
+    end
+
+    ##
+    # convenience method to build a hyperlink
+
+    def href(link, cls, name)
+      %{<a href="#{link}" class="#{cls}">#{name}</a>} #"
+    end
+
+    ##
+    # Returns a reference to outselves to be used as an href= the form depends
+    # on whether we're all in one file or in multiple files
+
+    def as_href(from_path)
+      if @options.all_one_file
+        "#" + path
+      else
+        RDoc::Generator.gen_url from_path, path
+      end
+    end
+
+    ##
+    # Create a list of Method objects for each method in the corresponding
+    # context object. If the @options.show_all variable is set (corresponding
+    # to the <tt>--all</tt> option, we include all methods, otherwise just the
+    # public ones.
+
+    def collect_methods
+      list = @context.method_list
+
+      unless @options.show_all then
+        list = list.find_all do |m|
+          m.visibility == :public or
+            m.visibility == :protected or
+            m.force_documentation
+        end
+      end
+
+      @methods = list.collect do |m|
+        RDoc::Generator::Method.new m, self, @options
+      end
+    end
+
+    ##
+    # Build a summary list of all the methods in this context
+
+    def build_method_summary_list(path_prefix="")
+      collect_methods unless @methods
+      meths = @methods.sort
+      res = []
+      meths.each do |meth|
+        res << {
+          "name" => CGI.escapeHTML(meth.name),
+          "aref" => "#{path_prefix}\##{meth.aref}"
+        }
+      end
+      res
+    end
+
+    ##
+    # Build a list of aliases for which we couldn't find a
+    # corresponding method
+
+    def build_alias_summary_list(section)
+      values = []
+      @context.aliases.each do |al|
+        next unless al.section == section
+        res = {
+          'old_name' => al.old_name,
+          'new_name' => al.new_name,
+        }
+        if al.comment && !al.comment.empty?
+          res['desc'] = markup(al.comment, true)
+        end
+        values << res
+      end
+      values
+    end
+
+    ##
+    # Build a list of constants
+
+    def build_constants_summary_list(section)
+      values = []
+      @context.constants.each do |co|
+        next unless co.section == section
+        res = {
+          'name'  => co.name,
+          'value' => CGI.escapeHTML(co.value)
+        }
+        res['desc'] = markup(co.comment, true) if co.comment && !co.comment.empty?
+        values << res
+      end
+      values
+    end
+
+    def build_requires_list(context)
+      potentially_referenced_list(context.requires) {|fn| [fn + ".rb"] }
+    end
+
+    def build_include_list(context)
+      potentially_referenced_list(context.includes)
+    end
+
+    ##
+    # Build a list from an array of Context items. Look up each in the
+    # AllReferences hash: if we find a corresponding entry, we generate a
+    # hyperlink to it, otherwise just output the name.  However, some names
+    # potentially need massaging. For example, you may require a Ruby file
+    # without the .rb extension, but the file names we know about may have it.
+    # To deal with this, we pass in a block which performs the massaging,
+    # returning an array of alternative names to match
+
+    def potentially_referenced_list(array)
+      res = []
+      array.each do |i|
+        ref = AllReferences[i.name]
+#         if !ref
+#           container = @context.parent
+#           while !ref && container
+#             name = container.name + "::" + i.name
+#             ref = AllReferences[name]
+#             container = container.parent
+#           end
+#         end
+
+        ref = @context.find_symbol(i.name)
+        ref = ref.viewer if ref
+
+        if !ref && block_given?
+          possibles = yield(i.name)
+          while !ref and !possibles.empty?
+            ref = AllReferences[possibles.shift]
+          end
+        end
+        h_name = CGI.escapeHTML(i.name)
+        if ref and ref.document_self
+          path = url(ref.path)
+          res << { "name" => h_name, "aref" => path }
+        else
+          res << { "name" => h_name }
+        end
+      end
+      res
+    end
+
+    ##
+    # Build an array of arrays of method details. The outer array has up
+    # to six entries, public, private, and protected for both class
+    # methods, the other for instance methods. The inner arrays contain
+    # a hash for each method
+
+    def build_method_detail_list(section)
+      outer = []
+
+      methods = @methods.sort
+      for singleton in [true, false]
+        for vis in [ :public, :protected, :private ]
+          res = []
+          methods.each do |m|
+            if m.section == section and
+                m.document_self and
+                m.visibility == vis and
+                m.singleton == singleton
+              row = {}
+              if m.call_seq
+                row["callseq"] = m.call_seq.gsub(/->/, '&rarr;')
+              else
+                row["name"]        = CGI.escapeHTML(m.name)
+                row["params"]      = m.params
+              end
+              desc = m.description.strip
+              row["m_desc"]      = desc unless desc.empty?
+              row["aref"]        = m.aref
+              row["visibility"]  = m.visibility.to_s
+
+              alias_names = []
+              m.aliases.each do |other|
+                if other.viewer   # won't be if the alias is private
+                  alias_names << {
+                    'name' => other.name,
+                    'aref'  => other.viewer.as_href(path)
+                  }
+                end
+              end
+              unless alias_names.empty?
+                row["aka"] = alias_names
+              end
+
+              if @options.inline_source
+                code = m.source_code
+                row["sourcecode"] = code if code
+              else
+                code = m.src_url
+                if code
+                  row["codeurl"] = code
+                  row["imgurl"]  = m.img_url
+                end
+              end
+              res << row
+            end
+          end
+          if res.size > 0
+            outer << {
+              "type"     => vis.to_s.capitalize,
+              "category" => singleton ? "Class" : "Instance",
+              "methods"  => res
+            }
+          end
+        end
+      end
+      outer
+    end
+
+    ##
+    # Build the structured list of classes and modules contained
+    # in this context.
+
+    def build_class_list(level, from, section, infile=nil)
+      res = ""
+      prefix = "&nbsp;&nbsp;::" * level;
+
+      from.modules.sort.each do |mod|
+        next unless mod.section == section
+        next if infile && !mod.defined_in?(infile)
+        if mod.document_self
+          res <<
+            prefix <<
+            "Module " <<
+            href(url(mod.viewer.path), "link", mod.full_name) <<
+            "<br />\n" <<
+            build_class_list(level + 1, mod, section, infile)
+        end
+      end
+
+      from.classes.sort.each do |cls|
+        next unless cls.section == section
+        next if infile && !cls.defined_in?(infile)
+        if cls.document_self
+          res      <<
+            prefix <<
+            "Class " <<
+            href(url(cls.viewer.path), "link", cls.full_name) <<
+            "<br />\n" <<
+            build_class_list(level + 1, cls, section, infile)
+        end
+      end
+
+      res
+    end
+
+    def url(target)
+      RDoc::Generator.gen_url path, target
+    end
+
+    def aref_to(target)
+      if @options.all_one_file
+        "#" + target
+      else
+        url(target)
+      end
+    end
+
+    def document_self
+      @context.document_self
+    end
+
+    def diagram_reference(diagram)
+      res = diagram.gsub(/((?:src|href)=")(.*?)"/) {
+        $1 + url($2) + '"'
+      }
+      res
+    end
+
+    ##
+    # Find a symbol in ourselves or our parent
+
+    def find_symbol(symbol, method=nil)
+      res = @context.find_symbol(symbol, method)
+      if res
+        res = res.viewer
+      end
+      res
+    end
+
+    ##
+    # create table of contents if we contain sections
+
+    def add_table_of_sections
+      toc = []
+      @context.sections.each do |section|
+        if section.title
+          toc << {
+            'secname' => section.title,
+            'href'    => section.sequence
+          }
+        end
+      end
+
+      @values['toc'] = toc unless toc.empty?
+    end
+
+  end
+
+  ##
+  # Wrap a ClassModule context
+
+  class Class < Context
+
+    attr_reader :methods
+    attr_reader :path
+
+    def initialize(context, html_file, prefix, options)
+      super(context, options)
+
+      @html_file = html_file
+      @is_module = context.is_module?
+      @values    = {}
+
+      context.viewer = self
+
+      if options.all_one_file
+        @path = context.full_name
+      else
+        @path = http_url(context.full_name, prefix)
+      end
+
+      collect_methods
+
+      AllReferences.add(name, self)
+    end
+
+    ##
+    # Returns the relative file name to store this class in, which is also its
+    # url
+
+    def http_url(full_name, prefix)
+      path = full_name.dup
+
+      path.gsub!(/<<\s*(\w*)/) { "from-#$1" } if path['<<']
+
+      ::File.join(prefix, path.split("::")) + ".html"
+    end
+
+    def name
+      @context.full_name
+    end
+
+    def parent_name
+      @context.parent.full_name
+    end
+
+    def index_name
+      name
+    end
+
+    def write_on(f)
+      value_hash
+      template = RDoc::TemplatePage.new(@template::BODY,
+                                        @template::CLASS_PAGE,
+                                        @template::METHOD_LIST)
+      template.write_html_on(f, @values)
+    end
+
+    def value_hash
+      class_attribute_values
+      add_table_of_sections
+
+      @values["charset"] = @options.charset
+      @values["style_url"] = style_url(path, @options.css)
+
+      d = markup(@context.comment)
+      @values["description"] = d unless d.empty?
+
+      ml = build_method_summary_list @path
+      @values["methods"] = ml unless ml.empty?
+
+      il = build_include_list(@context)
+      @values["includes"] = il unless il.empty?
+
+      @values["sections"] = @context.sections.map do |section|
+
+        secdata = {
+          "sectitle" => section.title,
+          "secsequence" => section.sequence,
+          "seccomment" => markup(section.comment)
+        }
+
+        al = build_alias_summary_list(section)
+        secdata["aliases"] = al unless al.empty?
+
+        co = build_constants_summary_list(section)
+        secdata["constants"] = co unless co.empty?
+
+        al = build_attribute_list(section)
+        secdata["attributes"] = al unless al.empty?
+
+        cl = build_class_list(0, @context, section)
+        secdata["classlist"] = cl unless cl.empty?
+
+        mdl = build_method_detail_list(section)
+        secdata["method_list"] = mdl unless mdl.empty?
+
+        secdata
+      end
+
+      @values
+    end
+
+    def build_attribute_list(section)
+      atts = @context.attributes.sort
+      res = []
+      atts.each do |att|
+        next unless att.section == section
+        if att.visibility == :public || att.visibility == :protected || @options.show_all
+          entry = {
+            "name"   => CGI.escapeHTML(att.name),
+            "rw"     => att.rw,
+            "a_desc" => markup(att.comment, true)
+          }
+          unless att.visibility == :public || att.visibility == :protected
+            entry["rw"] << "-"
+          end
+          res << entry
+        end
+      end
+      res
+    end
+
+    def class_attribute_values
+      h_name = CGI.escapeHTML(name)
+
+      @values["path"]      = @path
+      @values["classmod"]  = @is_module ? "Module" : "Class"
+      @values["title"]     = "#{@values['classmod']}: #{h_name}"
+
+      c = @context
+      c = c.parent while c and !c.diagram
+      if c && c.diagram
+        @values["diagram"] = diagram_reference(c.diagram)
+      end
+
+      @values["full_name"] = h_name
+
+      parent_class = @context.superclass
+
+      if parent_class
+        @values["parent"] = CGI.escapeHTML(parent_class)
+
+        if parent_name
+          lookup = parent_name + "::" + parent_class
+        else
+          lookup = parent_class
+        end
+
+        parent_url = AllReferences[lookup] || AllReferences[parent_class]
+
+        if parent_url and parent_url.document_self
+          @values["par_url"] = aref_to(parent_url.path)
+        end
+      end
+
+      files = []
+      @context.in_files.each do |f|
+        res = {}
+        full_path = CGI.escapeHTML(f.file_absolute_name)
+
+        res["full_path"]     = full_path
+        res["full_path_url"] = aref_to(f.viewer.path) if f.document_self
+
+        if @options.webcvs
+          res["cvsurl"] = cvs_url( @options.webcvs, full_path )
+        end
+
+        files << res
+      end
+
+      @values['infiles'] = files
+    end
+
+    def <=>(other)
+      self.name <=> other.name
+    end
+
+  end
+
+  ##
+  # Handles the mapping of a file's information to HTML. In reality, a file
+  # corresponds to a +TopLevel+ object, containing modules, classes, and
+  # top-level methods. In theory it _could_ contain attributes and aliases,
+  # but we ignore these for now.
+
+  class File < Context
+
+    attr_reader :path
+    attr_reader :name
+
+    def initialize(context, options, file_dir)
+      super(context, options)
+
+      @values = {}
+
+      if options.all_one_file
+        @path = filename_to_label
+      else
+        @path = http_url(file_dir)
+      end
+
+      @name = @context.file_relative_name
+
+      collect_methods
+      AllReferences.add(name, self)
+      context.viewer = self
+    end
+
+    def http_url(file_dir)
+      ::File.join file_dir, "#{@context.file_relative_name.tr '.', '_'}.html"
+    end
+
+    def filename_to_label
+      @context.file_relative_name.gsub(/%|\/|\?|\#/) do |s|
+        '%%%x' % s[0].unpack('C')
+      end
+    end
+
+    def index_name
+      name
+    end
+
+    def parent_name
+      nil
+    end
+
+    def value_hash
+      file_attribute_values
+      add_table_of_sections
+
+      @values["charset"]   = @options.charset
+      @values["href"]      = path
+      @values["style_url"] = style_url(path, @options.css)
+
+      if @context.comment
+        d = markup(@context.comment)
+        @values["description"] = d if d.size > 0
+      end
+
+      ml = build_method_summary_list
+      @values["methods"] = ml unless ml.empty?
+
+      il = build_include_list(@context)
+      @values["includes"] = il unless il.empty?
+
+      rl = build_requires_list(@context)
+      @values["requires"] = rl unless rl.empty?
+
+      if @options.promiscuous
+        file_context = nil
+      else
+        file_context = @context
+      end
+
+
+      @values["sections"] = @context.sections.map do |section|
+
+        secdata = {
+          "sectitle" => section.title,
+          "secsequence" => section.sequence,
+          "seccomment" => markup(section.comment)
+        }
+
+        cl = build_class_list(0, @context, section, file_context)
+        @values["classlist"] = cl unless cl.empty?
+
+        mdl = build_method_detail_list(section)
+        secdata["method_list"] = mdl unless mdl.empty?
+
+        al = build_alias_summary_list(section)
+        secdata["aliases"] = al unless al.empty?
+
+        co = build_constants_summary_list(section)
+        @values["constants"] = co unless co.empty?
+
+        secdata
+      end
+
+      @values
+    end
+
+    def write_on(f)
+      value_hash
+
+      template = RDoc::TemplatePage.new(@template::BODY,
+                                        @template::FILE_PAGE,
+                                        @template::METHOD_LIST)
+
+      template.write_html_on(f, @values)
+    end
+
+    def file_attribute_values
+      full_path = @context.file_absolute_name
+      short_name = ::File.basename full_path
+
+      @values["title"] = CGI.escapeHTML("File: #{short_name}")
+
+      if @context.diagram then
+        @values["diagram"] = diagram_reference(@context.diagram)
+      end
+
+      @values["short_name"]   = CGI.escapeHTML(short_name)
+      @values["full_path"]    = CGI.escapeHTML(full_path)
+      @values["dtm_modified"] = @context.file_stat.mtime.to_s
+
+      if @options.webcvs then
+        @values["cvsurl"] = cvs_url @options.webcvs, @values["full_path"]
+      end
+    end
+
+    def <=>(other)
+      self.name <=> other.name
+    end
+
+  end
+
+  class Method
+
+    include MarkUp
+
+    attr_reader :context
+    attr_reader :src_url
+    attr_reader :img_url
+    attr_reader :source_code
+
+    @@seq = "M000000"
+
+    @@all_methods = []
+
+    def self.all_methods
+      @@all_methods
+    end
+
+    def self.reset
+      @@all_methods = []
+    end
+
+    def initialize(context, html_class, options)
+      @context    = context
+      @html_class = html_class
+      @options    = options
+
+      # HACK ugly
+      @template = options.template_class
+
+      @@seq       = @@seq.succ
+      @seq        = @@seq
+      @@all_methods << self
+
+      context.viewer = self
+
+      if (ts = @context.token_stream)
+        @source_code = markup_code(ts)
+        unless @options.inline_source
+          @src_url = create_source_code_file(@source_code)
+          @img_url = RDoc::Generator.gen_url path, 'source.png'
+        end
+      end
+
+      AllReferences.add(name, self)
+    end
+
+    ##
+    # Returns a reference to outselves to be used as an href= the form depends
+    # on whether we're all in one file or in multiple files
+
+    def as_href(from_path)
+      if @options.all_one_file
+        "#" + path
+      else
+        RDoc::Generator.gen_url from_path, path
+      end
+    end
+
+    def name
+      @context.name
+    end
+
+    def section
+      @context.section
+    end
+
+    def index_name
+      "#{@context.name} (#{@html_class.name})"
+    end
+
+    def parent_name
+      if @context.parent.parent
+        @context.parent.parent.full_name
+      else
+        nil
+      end
+    end
+
+    def aref
+      @seq
+    end
+
+    def path
+      if @options.all_one_file
+        aref
+      else
+        @html_class.path + "#" + aref
+      end
+    end
+
+    def description
+      markup(@context.comment)
+    end
+
+    def visibility
+      @context.visibility
+    end
+
+    def singleton
+      @context.singleton
+    end
+
+    def call_seq
+      cs = @context.call_seq
+      if cs
+        cs.gsub(/\n/, "<br />\n")
+      else
+        nil
+      end
+    end
+
+    def params
+      # params coming from a call-seq in 'C' will start with the
+      # method name
+      if p !~ /^\w/
+        p = @context.params.gsub(/\s*\#.*/, '')
+        p = p.tr("\n", " ").squeeze(" ")
+        p = "(" + p + ")" unless p[0] == ?(
+
+        if (block = @context.block_params)
+         # If this method has explicit block parameters, remove any
+         # explicit &block
+
+         p.sub!(/,?\s*&\w+/, '')
+
+          block.gsub!(/\s*\#.*/, '')
+          block = block.tr("\n", " ").squeeze(" ")
+          if block[0] == ?(
+            block.sub!(/^\(/, '').sub!(/\)/, '')
+          end
+          p << " {|#{block.strip}| ...}"
+        end
+      end
+      CGI.escapeHTML(p)
+    end
+
+    def create_source_code_file(code_body)
+      meth_path = @html_class.path.sub(/\.html$/, '.src')
+      FileUtils.mkdir_p(meth_path)
+      file_path = ::File.join meth_path, "#{@seq}.html"
+
+      template = RDoc::TemplatePage.new(@template::SRC_PAGE)
+
+      open file_path, 'w' do |f|
+        values = {
+          'title'     => CGI.escapeHTML(index_name),
+          'code'      => code_body,
+          'style_url' => style_url(file_path, @options.css),
+          'charset'   => @options.charset
+        }
+        template.write_html_on(f, values)
+      end
+
+      RDoc::Generator.gen_url path, file_path
+    end
+
+    def <=>(other)
+      @context <=> other.context
+    end
+
+    ##
+    # Given a sequence of source tokens, mark up the source code
+    # to make it look purty.
+
+    def markup_code(tokens)
+      src = ""
+      tokens.each do |t|
+        next unless t
+        #    p t.class
+#        style = STYLE_MAP[t.class]
+        style = case t
+                when RubyToken::TkCONSTANT then "ruby-constant"
+                when RubyToken::TkKW       then "ruby-keyword kw"
+                when RubyToken::TkIVAR     then "ruby-ivar"
+                when RubyToken::TkOp       then "ruby-operator"
+                when RubyToken::TkId       then "ruby-identifier"
+                when RubyToken::TkNode     then "ruby-node"
+                when RubyToken::TkCOMMENT  then "ruby-comment cmt"
+                when RubyToken::TkREGEXP   then "ruby-regexp re"
+                when RubyToken::TkSTRING   then "ruby-value str"
+                when RubyToken::TkVal      then "ruby-value"
+                else
+                    nil
+                end
+
+        text = CGI.escapeHTML(t.text)
+
+        if style
+          src << "<span class=\"#{style}\">#{text}</span>"
+        else
+          src << text
+        end
+      end
+
+      add_line_numbers(src) if @options.include_line_numbers
+      src
+    end
+
+    ##
+    # We rely on the fact that the first line of a source code listing has
+    #    # File xxxxx, line dddd
+
+    def add_line_numbers(src)
+      if src =~ /\A.*, line (\d+)/
+        first = $1.to_i - 1
+        last  = first + src.count("\n")
+        size = last.to_s.length
+        real_fmt = "%#{size}d: "
+        fmt = " " * (size+2)
+        src.gsub!(/^/) do
+          res = sprintf(fmt, first)
+          first += 1
+          fmt = real_fmt
+          res
+        end
+      end
+    end
+
+    def document_self
+      @context.document_self
+    end
+
+    def aliases
+      @context.aliases
+    end
+
+    def find_symbol(symbol, method=nil)
+      res = @context.parent.find_symbol(symbol, method)
+      if res
+        res = res.viewer
+      end
+      res
+    end
+
+  end
+
 end
 
Index: lib/rdoc/rdoc.rb
===================================================================
--- lib/rdoc/rdoc.rb	(revision 15420)
+++ lib/rdoc/rdoc.rb	(revision 15421)
@@ -53,13 +53,15 @@
       end
     end
 
-    private
+    def initialize
+      @stats = Stats.new
+    end
 
     ##
     # Report an error message and exit
 
     def error(msg)
-      raise RDoc::Error, msg
+      raise ::RDoc::Error, msg
     end
 
     ##
@@ -206,8 +208,6 @@
       file_info
     end
 
-    public
-
     ##
     # Format up one or more files according to the given arguments.
     #
@@ -223,8 +223,6 @@
     def document(argv)
       TopLevel::reset
 
-      @stats = Stats.new
-
       options = Options.new GENERATORS
       options.parse argv
 
Index: lib/rdoc/options.rb
===================================================================
--- lib/rdoc/options.rb	(revision 15420)
+++ lib/rdoc/options.rb	(revision 15421)
@@ -49,7 +49,7 @@
   ##
   # The list of files to be processed
 
-  attr_reader :files
+  attr_accessor :files
 
   ##
   # Scan newer sources than the flag file if true.
@@ -74,7 +74,7 @@
   ##
   # Should source code be included inline, or displayed in a popup
 
-  attr_reader :inline_source
+  attr_accessor :inline_source
 
   ##
   # Name of the file, class or module to display in the initial index page (if
Index: lib/rdoc/markup/inline.rb
===================================================================
--- lib/rdoc/markup/inline.rb	(revision 15420)
+++ lib/rdoc/markup/inline.rb	(revision 15421)
@@ -12,9 +12,9 @@
     @@name_to_bitmap = { :_SPECIAL_ => SPECIAL }
     @@next_bitmap = 2
 
-    def Attribute.bitmap_for(name)
+    def self.bitmap_for(name)
       bitmap = @@name_to_bitmap[name]
-      if !bitmap
+      unless bitmap then
         bitmap = @@next_bitmap
         @@next_bitmap <<= 1
         @@name_to_bitmap[name] = bitmap
@@ -22,7 +22,7 @@
       bitmap
     end
 
-    def Attribute.as_string(bitmap)
+    def self.as_string(bitmap)
       return "none" if bitmap.zero?
       res = []
       @@name_to_bitmap.each do |name, bit|
@@ -31,7 +31,7 @@
       res.join(",")
     end
 
-    def Attribute.each_name_of(bitmap)
+    def self.each_name_of(bitmap)
       @@name_to_bitmap.each do |name, bit|
         next if bit == SPECIAL
         yield name.to_s if (bitmap & bit) != 0
@@ -85,14 +85,15 @@
       self.text == o.text && self.type == o.type
     end
 
+    def inspect
+      "#<RDoc::Markup::Special:0x%x @type=%p, name=%p @text=%p>" % [
+        object_id, @type, RDoc::Markup::Attribute.as_string(type), text.dump]
+    end
+
     def to_s
       "Special: type=#{type}, name=#{RDoc::Markup::Attribute.as_string type}, text=#{text.dump}"
     end
 
-    def inspect
-      "#<RDoc::Markup::Special:0x%x @type=%p, name=%p @text=%p>" % [
-        object_id, @type, RDoc::Markup::Attribute.as_string(type), text.dump]
-    end
   end
 
   class AttributeManager
@@ -165,8 +166,10 @@
     def convert_attrs(str, attrs)
       # first do matching ones
       tags = MATCHING_WORD_PAIRS.keys.join("")
+
       re = "(^|\\W)([#{tags}])([A-Za-z_]+?)\\2(\\W|\$)"
 #      re = "(^|\\W)([#{tags}])(\\S+?)\\2(\\W|\$)"
+
       1 while str.gsub!(Regexp.new(re)) {
         attr = MATCHING_WORD_PAIRS[$2];
         attrs.set_attrs($`.length + $1.length + $2.length, $3.length, attr)
@@ -185,9 +188,9 @@
     end
 
     def convert_html(str, attrs)
-      tags = HTML_TAGS.keys.join("|")
-      re = "<(#{tags})>(.*?)</\\1>"
-      1 while str.gsub!(Regexp.new(re, Regexp::IGNORECASE)) {
+      tags = HTML_TAGS.keys.join '|'
+
+      1 while str.gsub!(/<(#{tags})>(.*?)<\/\1>/i) {
         attr = HTML_TAGS[$1.downcase]
         html_length = $1.length + 2
         seq = NULL * html_length
@@ -210,7 +213,7 @@
     # A \ in front of a character that would normally be processed turns off
     # processing. We do this by turning \< into <#{PROTECT}
 
-    PROTECTABLE = [ "<" << "\\" ]  #"
+    PROTECTABLE = [ "<" << "\\" ]
 
 
     def mask_protected_sequences
@@ -300,7 +303,6 @@
     end
 
     def split_into_flow
-
       display_attributes if $DEBUG_RDOC
 
       res = []
Index: lib/rdoc/markup/to_latex.rb
===================================================================
--- lib/rdoc/markup/to_latex.rb	(revision 15420)
+++ lib/rdoc/markup/to_latex.rb	(revision 15421)
@@ -1,3 +1,4 @@
+require 'rdoc/markup/formatter'
 require 'rdoc/markup/fragments'
 require 'rdoc/markup/inline'
 
@@ -6,7 +7,7 @@
 ##
 # Convert SimpleMarkup to basic LaTeX report format.
 
-class RDoc::Markup::ToLaTeX
+class RDoc::Markup::ToLaTeX < RDoc::Markup::Formatter
 
   BS = "\020"   # \
   OB = "\021"   # {
Index: lib/rdoc/markup/to_test.rb
===================================================================
--- lib/rdoc/markup/to_test.rb	(revision 15420)
+++ lib/rdoc/markup/to_test.rb	(revision 15421)
@@ -1,9 +1,10 @@
 require 'rdoc/markup'
+require 'rdoc/markup/formatter'
 
 ##
 # This Markup outputter is used for testing purposes.
 
-class RDoc::Markup::ToTest
+class RDoc::Markup::ToTest < RDoc::Markup::Formatter
 
   def start_accepting
     @res = []
Index: lib/rdoc/markup/formatter.rb
===================================================================
--- lib/rdoc/markup/formatter.rb	(revision 0)
+++ lib/rdoc/markup/formatter.rb	(revision 15421)
@@ -0,0 +1,14 @@
+require 'rdoc/markup'
+
+class RDoc::Markup::Formatter
+
+  def initialize
+    @markup = RDoc::Markup.new
+  end
+
+  def convert(content)
+    @markup.convert content, self
+  end
+
+end
+

Property changes on: lib/rdoc/markup/formatter.rb
___________________________________________________________________
Name: svn:eol-style
   + LF
Name: svn:keywords
   + Author Date Id Revision

Index: lib/rdoc/markup/fragments.rb
===================================================================
--- lib/rdoc/markup/fragments.rb	(revision 15420)
+++ lib/rdoc/markup/fragments.rb	(revision 15421)
@@ -83,10 +83,16 @@
   class ListItem < ListBase
     type_name :LIST
 
-    #  def label
-    #    am = AttributeManager.new(@param)
-    #    am.flow
-    #  end
+    def to_s
+      text = if [:NOTE, :LABELED].include? type then
+               "#{@param}: #{@txt}"
+             else
+               @txt
+             end
+
+      "L#@level: #{type} #{self.class.name.split('::')[-1]}\n#{text}"
+    end
+
   end
 
   class ListStart < ListBase
@@ -311,9 +317,8 @@
 
     def tidy_blank_lines
       (@fragments.size - 1).times do |i|
-        if @fragments[i].kind_of?(BlankLine) and 
-            @fragments[i+1].kind_of?(ListEnd)
-          @fragments[i], @fragments[i+1] = @fragments[i+1], @fragments[i] 
+        if BlankLine === @fragments[i] and ListEnd === @fragments[i+1] then
+          @fragments[i], @fragments[i+1] = @fragments[i+1], @fragments[i]
         end
       end
 
Index: lib/rdoc/markup/to_html.rb
===================================================================
--- lib/rdoc/markup/to_html.rb	(revision 15420)
+++ lib/rdoc/markup/to_html.rb	(revision 15421)
@@ -1,3 +1,4 @@
+require 'rdoc/markup/formatter'
 require 'rdoc/markup/fragments'
 require 'rdoc/markup/inline'
 
@@ -3,18 +4,20 @@
 require 'cgi'
 
-class RDoc::Markup::ToHtml
+class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
 
   LIST_TYPE_TO_HTML = {
-    :BULLET =>  [ "<ul>", "</ul>" ],
-    :NUMBER =>  [ "<ol>", "</ol>" ],
-    :UPPERALPHA =>  [ "<ol>", "</ol>" ],
-    :LOWERALPHA =>  [ "<ol>", "</ol>" ],
-    :LABELED => [ "<dl>", "</dl>" ],
-    :NOTE    => [ "<table>", "</table>" ],
+    :BULLET =>     %w[<ul> </ul>],
+    :NUMBER =>     %w[<ol> </ol>],
+    :UPPERALPHA => %w[<ol> </ol>],
+    :LOWERALPHA => %w[<ol> </ol>],
+    :LABELED =>    %w[<dl> </dl>],
+    :NOTE    =>    %w[<table> </table>],
   }
 
   InlineTag = Struct.new(:bit, :on, :off)
 
   def initialize
+    super
+
     init_tags
   end
@@ -94,8 +97,11 @@
     if tag = @in_list_entry.last
       @res << annotate(tag) << "\n"
     end
+
     @res << list_item_start(am, fragment)
+
     @res << wrap(convert_flow(am.flow(fragment.txt))) << "\n"
+
     @in_list_entry[-1] = list_end_for(fragment.type)
   end
 
Index: lib/rdoc/markup/to_flow.rb
===================================================================
--- lib/rdoc/markup/to_flow.rb	(revision 15420)
+++ lib/rdoc/markup/to_flow.rb	(revision 15421)
@@ -1,3 +1,4 @@
+require 'rdoc/markup/formatter'
 require 'rdoc/markup/fragments'
 require 'rdoc/markup/inline'
 require 'cgi'
@@ -22,7 +23,7 @@
     H = Struct.new(:level, :text)
   end
 
-  class ToFlow
+  class ToFlow < RDoc::Markup::Formatter
     LIST_TYPE_TO_HTML = {
       :BULLET     =>  [ "<ul>", "</ul>" ],
       :NUMBER     =>  [ "<ol>", "</ol>" ],
@@ -35,6 +36,8 @@
     InlineTag = Struct.new(:bit, :on, :off)
 
     def initialize
+      super
+
       init_tags
     end
 
Index: lib/rdoc/markup/lines.rb
===================================================================
--- lib/rdoc/markup/lines.rb	(revision 15420)
+++ lib/rdoc/markup/lines.rb	(revision 15421)
@@ -59,8 +59,8 @@
     end
 
     # Return true if this line is blank
-    def isBlank?
-      @text.length.zero?
+    def blank?
+      @text.empty?
     end
 
     # stamp a line with a type, a level, a prefix, and a flag
Index: lib/rdoc/markup/to_html_hyperlink.rb
===================================================================
--- lib/rdoc/markup/to_html_hyperlink.rb	(revision 0)
+++ lib/rdoc/markup/to_html_hyperlink.rb	(revision 15421)
@@ -0,0 +1,149 @@
+require 'rdoc/markup/to_html'
+
+##
+# Subclass of the RDoc::Markup::ToHtml class that supports looking up words in
+# the AllReferences list. Those that are found (like AllReferences in this
+# comment) will be hyperlinked
+
+class RDoc::Markup::ToHtmlHyperlink < RDoc::Markup::ToHtml
+
+  attr_accessor :context
+
+  ##
+  # We need to record the html path of our caller so we can generate
+  # correct relative paths for any hyperlinks that we find
+
+  def initialize(from_path, context, show_hash)
+    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)
+
+    # external hyperlinks
+    @markup.add_special(/((link:|https?:|mailto:|ftp:|www\.)\S+\w)/, :HYPERLINK)
+
+    # and links of the form  <text>[<url>]
+    @markup.add_special(/(((\{.*?\})|\b\S+?)\[\S+?\.\S+?\])/, :TIDYLINK)
+
+    @from_path = from_path
+    @context = context
+    @show_hash = show_hash
+
+    @seen = {}
+  end
+
+  ##
+  # We're invoked when any text matches the CROSSREF pattern
+  # (defined in MarkUp). If we fine the corresponding reference,
+  # generate a hyperlink. If the name we're looking for contains
+  # no punctuation, we look for it up the module/class chain. For
+  # example, HyperlinkHtml is found, even without the Generator::
+  # prefix, because we look for it in module Generator first.
+
+  def handle_special_CROSSREF(special)
+    name = special.text
+
+    return @seen[name] if @seen.include? name
+
+    if name[0,1] == '#' then
+      lookup = name[1..-1]
+      name = lookup unless @show_hash
+    else
+      lookup = name
+    end
+
+    # Find class, module, or method in class or module.
+    if /([A-Z]\w*)[.\#](\w+[!?=]?)/ =~ lookup then
+      container = $1
+      method = $2
+      ref = @context.find_symbol container, method
+    elsif /([A-Za-z]\w*)[.\#](\w+(\([\.\w+\*\/\+\-\=\<\>]+\))?)/ =~ lookup then
+      container = $1
+      method = $2
+      ref = @context.find_symbol container, method
+    else
+      ref = @context.find_symbol lookup
+    end
+
+    out = if lookup =~ /^\\/ then
+            $'
+          elsif ref and ref.document_self then
+            "<a href=\"#{ref.as_href(@from_path)}\">#{name}</a>"
+          else
+            name
+          end
+
+    @seen[name] = out
+
+    out
+  end
+
+  ##
+  # Generate a hyperlink for url, labeled with text. Handle the
+  # special cases for img: and link: described under handle_special_HYPEDLINK
+
+  def gen_url(url, text)
+    if url =~ /([A-Za-z]+):(.*)/ then
+      type = $1
+      path = $2
+    else
+      type = "http"
+      path = url
+      url  = "http://#{url}"
+    end
+
+    if type == "link" then
+      url = if path[0, 1] == '#' then # is this meaningful?
+              path
+            else
+              HTML.gen_url @from_path, path
+            end
+    end
+
+    if (type == "http" or type == "link") and
+       url =~ /\.(gif|png|jpg|jpeg|bmp)$/ then
+      "<img src=\"#{url}\" />"
+    else
+      "<a href=\"#{url}\">#{text.sub(%r{^#{type}:/*}, '')}</a>"
+    end
+  end
+
+  ##
+  # And we're invoked with a potential external hyperlink mailto:
+  # just gets inserted. http: links are checked to see if they
+  # reference an image. If so, that image gets inserted using an
+  # <img> tag. Otherwise a conventional <a href> is used.  We also
+  # support a special type of hyperlink, link:, which is a reference
+  # to a local file whose path is relative to the --op directory.
+
+  def handle_special_HYPERLINK(special)
+    url = special.text
+    gen_url url, url
+  end
+
+  ##
+  # Here's a hypedlink where the label is different to the URL
+  #  <label>[url]
+
+  def handle_special_TIDYLINK(special)
+    text = special.text
+
+    return text unless text =~ /\{(.*?)\}\[(.*?)\]/ or text =~ /(\S+)\[(.*?)\]/
+
+    label = $1
+    url   = $2
+    gen_url url, label
+  end
+
+end
+

Property changes on: lib/rdoc/markup/to_html_hyperlink.rb
___________________________________________________________________
Name: svn:eol-style
   + LF
Name: svn:keywords
   + Author Date Id Revision

Index: lib/rdoc/markup.rb
===================================================================
--- lib/rdoc/markup.rb	(revision 15420)
+++ lib/rdoc/markup.rb	(revision 15421)
@@ -146,16 +146,16 @@
 #     end
 #   end
 #   
-#   p = RDoc::Markup.new
-#   p.add_word_pair("{", "}", :STRIKE)
-#   p.add_html("no", :STRIKE)
+#   m = RDoc::Markup.new
+#   m.add_word_pair("{", "}", :STRIKE)
+#   m.add_html("no", :STRIKE)
 #   
-#   p.add_special(/\b([A-Z][a-z]+[A-Z]\w+)/, :WIKIWORD)
+#   m.add_special(/\b([A-Z][a-z]+[A-Z]\w+)/, :WIKIWORD)
 #   
 #   h = WikiHtml.new
 #   h.add_tag(:STRIKE, "<strike>", "</strike>")
 #   
-#   puts "<body>" + p.convert(ARGF.read, h) + "</body>"
+#   puts "<body>" + m.convert(ARGF.read, h) + "</body>"
 #
 #--
 # Author::   Dave Thomas,  dave@p...
@@ -194,7 +194,7 @@
   # identify significant chunks.
 
   def initialize
-    @am = AttributeManager.new
+    @am = RDoc::Markup::AttributeManager.new
     @output = nil
   end
 
@@ -234,15 +234,16 @@
   # display the result.
 
   def convert(str, op)
-    @lines = Lines.new(str.split(/\r?\n/).collect { |aLine|
-                         Line.new(aLine) })
+    lines = str.split(/\r?\n/).map { |line| Line.new line }
+    @lines = Lines.new lines
+
     return "" if @lines.empty?
     @lines.normalize
     assign_types_to_lines
     group = group_lines
     # call the output formatter to handle the result
-    #      group.to_a.each {|i| p i}
-    group.accept(@am, op)
+    #group.each { |line| p line }
+    group.accept @am, op
   end
 
   private
@@ -252,9 +253,8 @@
   # Blank, a paragraph, a list element, or verbatim text.
 
   def assign_types_to_lines(margin = 0, level = 0)
-
     while line = @lines.next
-      if line.isBlank?
+      if line.blank? then
         line.stamp :BLANK, level
         next
       end
@@ -289,7 +289,6 @@
       # text following them (* xxx, - xxx, and dd. xxx)
 
       if SIMPLE_LIST_RE =~ active_line
-
         offset = margin + $1.length
         prefix = $2
         prefix_length = prefix.length
@@ -308,7 +307,6 @@
         next
       end
 
-
       if LABEL_LIST_RE =~ active_line
         offset = margin + $1.length
         prefix = $2
@@ -366,22 +364,23 @@
     prefix_length = prefix.length
     text = line.text
     flag = nil
+
     case prefix
-    when /^\[/
+    when /^\[/ then
       flag = :LABELED
       prefix = prefix[1, prefix.length-2]
-    when /:$/
+    when /:$/ then
       flag = :NOTE
       prefix.chop!
-    else raise "Invalid List Type: #{self.inspect}"
+    else
+      raise "Invalid List Type: #{self.inspect}"
     end
 
     # body is on the next line
-
-    if text.length <= offset
+    if text.length <= offset then
       original_line = line
       line = @lines.next
-      return(false) unless line
+      return false unless line
       text = line.text
 
       for i in 0..margin
@@ -390,15 +389,24 @@
           return false
         end
       end
+
       i = margin
       i += 1 while text[i] == SPACE
-      if i >= text.length
+
+      if i >= text.length then
         @lines.unget
         return false
       else
         offset = i
         prefix_length = 0
-        @lines.delete(original_line)
+
+        if text[offset..-1] =~ SIMPLE_LIST_RE then
+          @lines.unget
+          line = original_line
+          line.text = ''
+        else
+          @lines.delete original_line
+        end
       end
     end
 
@@ -418,24 +426,26 @@
   def group_lines
     @lines.rewind
 
-    inList = false
-    wantedType = wantedLevel = nil
+    in_list = false
+    wanted_type = wanted_level = nil
 
     block = LineCollection.new
     group = nil
 
     while line = @lines.next
-      if line.level == wantedLevel and line.type == wantedType
+      if line.level == wanted_level and line.type == wanted_type
         group.add_text(line.text)
       else
         group = block.fragment_for(line)
         block.add(group)
+
         if line.type == :LIST
-          wantedType = :PARAGRAPH
+          wanted_type = :PARAGRAPH
         else
-          wantedType = line.type
+          wanted_type = line.type
         end
-        wantedLevel = line.type == :HEADING ? line.param : line.level
+
+        wanted_level = line.type == :HEADING ? line.param : line.level
       end
     end
 
@@ -462,4 +472,5 @@
 end
 
 require 'rdoc/markup/fragments'
+require 'rdoc/markup/inline'
 require 'rdoc/markup/lines'
Index: test/rdoc/test_rdoc_ri_overstrike_formatter.rb
===================================================================
--- test/rdoc/test_rdoc_ri_overstrike_formatter.rb	(revision 15420)
+++ test/rdoc/test_rdoc_ri_overstrike_formatter.rb	(revision 15421)
@@ -18,6 +18,14 @@
     @af = RDoc::RI::AttributeFormatter
   end
 
+  def test_display_verbatim_flow_item_bold
+    verbatim = RDoc::Markup::Flow::VERB.new "*a* b c"
+
+    @f.display_verbatim_flow_item verbatim
+
+    assert_equal "  *a* b c\n\n", @output.string
+  end
+
   def test_write_attribute_text_bold
     line = [RDoc::RI::AttributeFormatter::AttrChar.new('b', @af::BOLD)]
 
Index: test/rdoc/test_rdoc_markup_attribute_manager.rb
===================================================================
--- test/rdoc/test_rdoc_markup_attribute_manager.rb	(revision 15420)
+++ test/rdoc/test_rdoc_markup_attribute_manager.rb	(revision 15421)
@@ -31,9 +31,9 @@
     crossref_bitmap = RDoc::Markup::Attribute.bitmap_for(:_SPECIAL_) |
                       RDoc::Markup::Attribute.bitmap_for(:CROSSREF)
 
-    [ @am.changed_attribute_by_name([], [:CROSSREF] | [:_SPECIAL_]),
+    [ @am.changed_attribute_by_name([], [:CROSSREF, :_SPECIAL_]),
       RDoc::Markup::Special.new(crossref_bitmap, text),
-      @am.changed_attribute_by_name([:CROSSREF] | [:_SPECIAL_], [])
+      @am.changed_attribute_by_name([:CROSSREF, :_SPECIAL_], [])
     ]
   end
 
@@ -90,29 +90,44 @@
                   @am.flow("cat _a__nd_ *dog*"))
   end
 
-  def test_html_like
-    assert_equal(["cat ", @tt_on, "dog", @tt_off], @am.flow("cat <tt>dog</Tt>"))
+  def test_html_like_em_bold
+    assert_equal ["cat ", @em_on, "and ", @em_to_bold, "dog", @bold_off],
+                  @am.flow("cat <i>and </i><b>dog</b>")
+  end
 
-    assert_equal(["cat ", @em_on, "and", @em_off, " ", @bold_on, "dog", @bold_off],
-                  @am.flow("cat <i>and</i> <B>dog</b>"))
+  def test_html_like_em_bold_SGML
+    assert_equal ["cat ", @em_on, "and ", @em_to_bold, "dog", @bold_off],
+                  @am.flow("cat <i>and <b></i>dog</b>")
+  end
 
-    assert_equal(["cat ", @em_on, "and ", @em_then_bold, "dog", @bold_em_off],
-                  @am.flow("cat <i>and <B>dog</B></I>"))
+  def test_html_like_em_bold_nested_1
+    assert_equal(["cat ", @bold_em_on, "and", @bold_em_off, " dog"],
+                  @am.flow("cat <i><b>and</b></i> dog"))
+  end
 
-    assert_equal(["cat ", @em_on, "and ", @em_to_bold, "dog", @bold_off],
-                  @am.flow("cat <i>and </i><b>dog</b>"))
+  def test_html_like_em_bold_nested_2
+    assert_equal ["cat ", @em_on, "and ", @em_then_bold, "dog", @bold_em_off],
+                  @am.flow("cat <i>and <b>dog</b></i>")
+  end
 
-    assert_equal(["cat ", @em_on, "and ", @em_to_bold, "dog", @bold_off],
-                  @am.flow("cat <i>and <b></i>dog</b>"))
+  def test_html_like_em_bold_nested_mixed_case
+    assert_equal ["cat ", @em_on, "and ", @em_then_bold, "dog", @bold_em_off],
+                  @am.flow("cat <i>and <B>dog</B></I>")
+  end
 
-    assert_equal([@tt_on, "cat", @tt_off, " ", @em_on, "and ", @em_to_bold, "dog", @bold_off],
-                  @am.flow("<tt>cat</tt> <i>and <b></i>dog</b>"))
+  def test_html_like_em_bold_mixed_case
+    assert_equal ["cat ", @em_on, "and", @em_off, " ", @bold_on, "dog", @bold_off],
+                  @am.flow("cat <i>and</i> <B>dog</b>")
+  end
 
-    assert_equal(["cat ", @em_on, "and ", @em_then_bold, "dog", @bold_em_off],
-                  @am.flow("cat <i>and <b>dog</b></i>"))
+  def test_html_like_teletype
+    assert_equal ["cat ", @tt_on, "dog", @tt_off],
+                 @am.flow("cat <tt>dog</Tt>")
+  end
 
-    assert_equal(["cat ", @bold_em_on, "and", @bold_em_off, " dog"],
-                  @am.flow("cat <i><b>and</b></i> dog"))
+  def test_html_like_teletype_em_bold_SGML
+    assert_equal [@tt_on, "cat", @tt_off, " ", @em_on, "and ", @em_to_bold, "dog", @bold_off],
+                  @am.flow("<tt>cat</tt> <i>and <b></i>dog</b>")
   end
 
   def test_protect
Index: test/rdoc/test_rdoc_ri_formatter.rb
===================================================================
--- test/rdoc/test_rdoc_ri_formatter.rb	(revision 15420)
+++ test/rdoc/test_rdoc_ri_formatter.rb	(revision 15421)
@@ -250,6 +250,14 @@
     assert_equal "  a b c\n  d e f\n\n", @output.string
   end
 
+  def test_display_verbatim_flow_item_bold
+    verbatim = RDoc::Markup::Flow::VERB.new "*a* b c"
+
+    @f.display_verbatim_flow_item verbatim
+
+    assert_equal "  *a* b c\n\n", @output.string
+  end
+
   def test_draw_line
     @f.draw_line
 
Index: test/rdoc/test_rdoc_markup.rb
===================================================================
--- test/rdoc/test_rdoc_markup.rb	(revision 15420)
+++ test/rdoc/test_rdoc_markup.rb	(revision 15421)
@@ -12,10 +12,10 @@
   end
 
   def line_groups(str, expected)
-    p = RDoc::Markup.new
+    m = RDoc::Markup.new
     mock = RDoc::Markup::ToTest.new
 
-    block = p.convert(str, mock)
+    block = m.convert(str, mock)
 
     if block != expected
       rows = (0...([expected.size, block.size].max)).collect{|i|
@@ -29,10 +29,10 @@
   end
 
   def line_types(str, expected)
-    p = RDoc::Markup.new
+    m = RDoc::Markup.new
     mock = RDoc::Markup::ToTest.new
-    p.convert(str, mock)
-    assert_equal(expected, p.get_line_types.map{|type| type.to_s[0,1]}.join(''))
+    m.convert(str, mock)
+    assert_equal(expected, m.get_line_types.map{|type| type.to_s[0,1]}.join(''))
   end
 
   def test_groups
@@ -68,8 +68,8 @@
     line_groups(str,
                 [ "L0: Paragraph\nnow is",
                   "L1: ListStart\n",
-                  "L1: ListItem\nl1",
-                  "L1: ListItem\nl2",
+                  "L1: BULLET ListItem\nl1",
+                  "L1: BULLET ListItem\nl2",
                   "L1: ListEnd\n",
                   "L0: Paragraph\nthe time"
                 ])
@@ -83,8 +83,8 @@
     line_groups(str,
                 [ "L0: Paragraph\nnow is",
                   "L1: ListStart\n",
-                  "L1: ListItem\nl1 l1+",
-                  "L1: ListItem\nl2",
+                  "L1: BULLET ListItem\nl1 l1+",
+                  "L1: BULLET ListItem\nl2",
                   "L1: ListEnd\n",
                   "L0: Paragraph\nthe time"
                 ])
@@ -98,11 +98,11 @@
     line_groups(str,
                 [ "L0: Paragraph\nnow is",
                   "L1: ListStart\n",
-                  "L1: ListItem\nl1",
+                  "L1: BULLET ListItem\nl1",
                   "L2: ListStart\n",
-                  "L2: ListItem\nl1.1",
+                  "L2: BULLET ListItem\nl1.1",
                   "L2: ListEnd\n",
-                  "L1: ListItem\nl2",
+                  "L1: BULLET ListItem\nl2",
                   "L1: ListEnd\n",
                   "L0: Paragraph\nthe time"
                 ])
@@ -122,13 +122,13 @@
     line_groups(str,
                 [ "L0: Paragraph\nnow is",
                   "L1: ListStart\n",
-                  "L1: ListItem\nl1",
+                  "L1: BULLET ListItem\nl1",
                   "L2: ListStart\n",
-                  "L2: ListItem\nl1.1 text",
+                  "L2: BULLET ListItem\nl1.1 text",
                   "L2: Verbatim\n  code\n    code\n",
                   "L2: Paragraph\ntext",
                   "L2: ListEnd\n",
-                  "L1: ListItem\nl2",
+                  "L1: BULLET ListItem\nl2",
                   "L1: ListEnd\n",
                   "L0: Paragraph\nthe time"
                 ])
@@ -143,11 +143,11 @@
     line_groups(str,
                 [ "L0: Paragraph\nnow is",
                   "L1: ListStart\n",
-                  "L1: ListItem\nl1",
+                  "L1: NUMBER ListItem\nl1",
                   "L2: ListStart\n",
-                  "L2: ListItem\nl1.1",
+                  "L2: BULLET ListItem\nl1.1",
                   "L2: ListEnd\n",
-                  "L1: ListItem\nl2",
+                  "L1: NUMBER ListItem\nl2",
                   "L1: ListEnd\n",
                   "L0: Paragraph\nthe time"
                 ])
@@ -161,11 +161,11 @@
     line_groups(str,
                 [ "L0: Paragraph\nnow is",
                   "L1: ListStart\n",
-                  "L1: ListItem\nl1",
+                  "L1: LABELED ListItem\ncat: l1",
                   "L2: ListStart\n",
-                  "L2: ListItem\nl1.1",
+                  "L2: BULLET ListItem\nl1.1",
                   "L2: ListEnd\n",
-                  "L1: ListItem\nl2",
+                  "L1: LABELED ListItem\ndog: l2",
                   "L1: ListEnd\n",
                   "L0: Paragraph\nthe time"
                 ])
@@ -179,8 +179,8 @@
     line_groups(str,
                 [ "L0: Paragraph\nnow is",
                   "L1: ListStart\n",
-                  "L1: ListItem\nl1 continuation",
-                  "L1: ListItem\nl2",
+                  "L1: LABELED ListItem\ncat: l1 continuation",
+                  "L1: LABELED ListItem\ndog: l2",
                   "L1: ListEnd\n",
                   "L0: Paragraph\nthe time"
                 ])
@@ -218,6 +218,133 @@
 
   end
 
+  def test_list_alpha
+    str = "a. alpha\nb. baker\nB. ALPHA\nA. BAKER"
+
+    line_groups(str,
+                [ "L1: ListStart\n",
+                  "L1: LOWERALPHA ListItem\nalpha",
+                  "L1: LOWERALPHA ListItem\nbaker",
+                  "L1: ListEnd\n",
+                  "L1: ListStart\n",
+                  "L1: UPPERALPHA ListItem\nALPHA",
+                  "L1: UPPERALPHA ListItem\nBAKER",
+                  "L1: ListEnd\n" ])
+  end
+
+  def test_list_bullet_dash
+    str = "- one\n- two\n"
+
+    line_groups(str,
+                [ "L1: ListStart\n",
+                  "L1: BULLET ListItem\none",
+                  "L1: BULLET ListItem\ntwo",
+                  "L1: ListEnd\n" ])
+  end
+
+  def test_list_bullet_star
+    str = "* one\n* two\n"
+
+    line_groups(str,
+                [ "L1: ListStart\n",
+                  "L1: BULLET ListItem\none",
+                  "L1: BULLET ListItem\ntwo",
+                  "L1: ListEnd\n" ])
+  end
+
+  def test_list_labeled_bracket
+    str = "[one] item one\n[two] item two"
+
+    line_groups(str,
+                [ "L1: ListStart\n",
+                  "L1: LABELED ListItem\none: item one",
+                  "L1: LABELED ListItem\ntwo: item two",
+                  "L1: ListEnd\n" ])
+  end
+
+  def test_list_labeled_bracket_continued
+    str = "[one]\n  item one\n[two]\n  item two"
+
+    line_groups(str,
+                [ "L1: ListStart\n",
+                  "L1: LABELED ListItem\none: item one",
+                  "L1: LABELED ListItem\ntwo: item two",
+                  "L1: ListEnd\n" ])
+  end
+
+  def test_list_labeled_colon
+    str = "one:: item one\ntwo:: item two"
+
+    line_groups(str,
+                [ "L1: ListStart\n",
+                  "L1: NOTE ListItem\none:: item one",
+                  "L1: NOTE ListItem\ntwo:: item two",
+                  "L1: ListEnd\n" ])
+  end
+
+  def test_list_labeled_colon_continued
+    str = "one::\n  item one\ntwo::\n  item two"
+
+    line_groups(str,
+                [ "L1: ListStart\n",
+                  "L1: NOTE ListItem\none:: item one",
+                  "L1: NOTE ListItem\ntwo:: item two",
+                  "L1: ListEnd\n" ])
+  end
+
+  def test_list_nested_bullet_bullet
+    str = "* one\n* two\n  * cat\n  * dog"
+
+    line_groups(str,
+                [ "L1: ListStart\n",
+                  "L1: BULLET ListItem\none",
+                  "L1: BULLET ListItem\ntwo",
+                  "L2: ListStart\n",
+                  "L2: BULLET ListItem\ncat",
+                  "L2: BULLET ListItem\ndog",
+                  "L2: ListEnd\n",
+                  "L1: ListEnd\n" ])
+  end
+
+  def test_list_nested_labeled_bullet
+    str = "[one]\n  * cat\n  * dog"
+
+    line_groups(str,
+                [ "L1: ListStart\n",
+                  "L1: LABELED ListItem\none: ",
+                  "L2: ListStart\n",
+                  "L2: BULLET ListItem\ncat",
+                  "L2: BULLET ListItem\ndog",
+                  "L2: ListEnd\n",
+                  "L1: ListEnd\n" ])
+  end
+
+  def test_list_nested_labeled_bullet_bullet
+    str = "[one]\n  * cat\n    * dog"
+
+    line_groups(str,
+                [ "L1: ListStart\n",
+                  "L1: LABELED ListItem\none: ",
+                  "L2: ListStart\n",
+                  "L2: BULLET ListItem\ncat",
+                  "L3: ListStart\n",
+                  "L3: BULLET ListItem\ndog",
+                  "L3: ListEnd\n",
+                  "L2: ListEnd\n",
+                  "L1: ListEnd\n" ])
+  end
+
+  def test_list_number
+    str = "1. one\n2. two\n1. three"
+
+    line_groups(str,
+                [ "L1: ListStart\n",
+                  "L1: NUMBER ListItem\none",
+                  "L1: NUMBER ListItem\ntwo",
+                  "L1: NUMBER ListItem\nthree",
+                  "L1: ListEnd\n" ])
+  end
+
   def test_list_split
     str = %{\
        now is
@@ -229,20 +356,30 @@
     line_groups(str,
                 [ "L0: Paragraph\nnow is",
                   "L1: ListStart\n",
-                  "L1: ListItem\nl1",
+                  "L1: BULLET ListItem\nl1",
                   "L1: ListEnd\n",
                   "L1: ListStart\n",
-                  "L1: ListItem\nn1",
-                  "L1: ListItem\nn2",
+                  "L1: NUMBER ListItem\nn1",
+                  "L1: NUMBER ListItem\nn2",
                   "L1: ListEnd\n",
                   "L1: ListStart\n",
-                  "L1: ListItem\nl2",
+                  "L1: BULLET ListItem\nl2",
                   "L1: ListEnd\n",
                   "L0: Paragraph\nthe time"
                 ])
 
   end
 
+  def test_paragraph
+    str = "paragraph\n\n*bold* paragraph\n"
+
+    line_groups str, [
+      "L0: Paragraph\nparagraph",
+      "L0: BlankLine\n",
+      "L0: Paragraph\n*bold* paragraph"
+    ]
+  end
+
   def test_tabs
     str = "hello\n  dave"
     assert_equal(str, basic_conv(str))
@@ -345,6 +482,15 @@
     line_types(str, 'PLPLP')
   end
 
+  def test_verbatim
+    str = "paragraph\n  *bold* verbatim\n"
+
+    line_groups str, [
+      "L0: Paragraph\nparagraph",
+      "L0: Verbatim\n  *bold* verbatim\n"
+    ]
+  end
+
   def test_verbatim_merge
     str = %{\
        now is

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]