summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/rdoc/code_objects.rb22
-rw-r--r--lib/rdoc/generator.rb1024
-rw-r--r--lib/rdoc/generator/html.rb1691
-rw-r--r--lib/rdoc/generator/html/html.rb8
-rw-r--r--lib/rdoc/markup.rb67
-rw-r--r--lib/rdoc/markup/formatter.rb14
-rw-r--r--lib/rdoc/markup/fragments.rb19
-rw-r--r--lib/rdoc/markup/inline.rb28
-rw-r--r--lib/rdoc/markup/lines.rb4
-rw-r--r--lib/rdoc/markup/to_flow.rb5
-rw-r--r--lib/rdoc/markup/to_html.rb20
-rw-r--r--lib/rdoc/markup/to_html_hyperlink.rb149
-rw-r--r--lib/rdoc/markup/to_latex.rb3
-rw-r--r--lib/rdoc/markup/to_test.rb3
-rw-r--r--lib/rdoc/options.rb4
-rw-r--r--lib/rdoc/rdoc.rb10
16 files changed, 1562 insertions, 1509 deletions
diff --git a/lib/rdoc/code_objects.rb b/lib/rdoc/code_objects.rb
index dfc0fff9cc..da383d69c4 100644
--- a/lib/rdoc/code_objects.rb
+++ b/lib/rdoc/code_objects.rb
@@ -5,10 +5,10 @@ require 'rdoc/tokenstream'
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 @@ module RDoc
# 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 @@ module RDoc
# 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 @@ module RDoc
end
end
-
+ ##
# AnyMethod is the base class for objects representing methods
class AnyMethod < CodeObject
@@ -632,14 +631,18 @@ module RDoc
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 @@ $stderr.puts p
end
end
-
# Represent an alias, which is an old_name/ new_name pair associated
# with a particular context
class Alias < CodeObject
diff --git a/lib/rdoc/generator.rb b/lib/rdoc/generator.rb
index 7db23058e3..b62cd00506 100644
--- a/lib/rdoc/generator.rb
+++ b/lib/rdoc/generator.rb
@@ -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 @@ module RDoc::Generator
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
diff --git a/lib/rdoc/generator/html.rb b/lib/rdoc/generator/html.rb
index e6a6cd9ac9..95617725e9 100644
--- a/lib/rdoc/generator/html.rb
+++ b/lib/rdoc/generator/html.rb
@@ -3,1528 +3,367 @@ require 'fileutils'
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.
+
+class RDoc::Generator::HTML
+
+ include RDoc::Generator::MarkUp
##
- # 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
+ # 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
-
- 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
+ # Set up a new HTML generator. Basically all we do here is load up the
+ # correct output temlate
+ def initialize(options) #:not-new:
+ @options = options
+ load_html_template
end
##
- # Handle common markup tasks for the various Html classes
-
- module MarkUp
-
- ##
- # 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.
-
- def markup(str, remove_para=false)
- return '' unless str
- unless defined? @markup
- @markup = RDoc::Markup.new
-
- # 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)
-
- # 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)
-# @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
+ # 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
- if str =~ /^(?>\s*)[^\#]/
- content = str
- else
- content = str.gsub(/^\s*(#+)/) { $1.tr('#',' ') }
- end
+ private
- res = @markup.convert(content, @html_formatter)
- if remove_para
- res.sub!(/^<p>/, '')
- res.sub!(/<\/p>$/, '')
- end
- res
- end
+ ##
+ # Load up the HTML template specified in the options.
+ # If the template name contains a slash, use it literally
- ##
- # 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 load_html_template
+ template = @options.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
+ unless template =~ %r{/|\\} then
+ template = File.join('rdoc', 'generator', @options.generator.key,
+ template)
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.
+ require template
- def cvs_url(url, full_path)
- if /%s/ =~ url
- return sprintf( url, full_path )
- else
- return url + full_path
- end
- end
+ @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
##
- # 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 ContextUser
+ # Write out the style sheet used by the main frames
- include MarkUp
+ def write_style_sheet
+ return unless @template.constants.include? :STYLE or
+ @template.constants.include? 'STYLE'
- attr_reader :context
+ template = RDoc::TemplatePage.new @template::STYLE
- def initialize(context, options)
- @context = context
- @options = options
+ unless @options.css then
+ open RDoc::Generator::CSS_NAME, 'w' do |f|
+ values = {}
- # 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)
- 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 <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 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)
+ if @template.constants.include? :FONTS or
+ @template.constants.include? 'FONTS' then
+ values["fonts"] = @template::FONTS
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)
- HTML.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
+ template.write_html_on(f, values)
end
-
- @values['toc'] = toc unless toc.empty?
end
-
end
##
- # Wrap a ClassModule context
-
- class HtmlClass < ContextUser
-
- 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
- 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
+ # See the comments at the top for a description of the directory structure
+
+ 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
+ def build_indices
+ @files, @classes = RDoc::Generator::Context.build_indicies(@toplevels,
+ @options)
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 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
- 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
- @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
-
+ # 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
- class HtmlMethod
-
- include MarkUp
-
- 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
+ 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
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
+ end
- def <=>(other)
- @context <=> other.context
- end
+ def gen_file_index
+ gen_an_index @files, 'Files', @template::FILE_INDEX, "fr_file_index.html"
+ 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
+ def gen_class_index
+ gen_an_index(@classes, 'Classes', @template::CLASS_INDEX,
+ "fr_class_index.html")
+ end
- add_line_numbers(src) if @options.include_line_numbers
- src
- end
+ def gen_method_index
+ gen_an_index(RDoc::Generator::Method.all_methods, 'Methods',
+ @template::METHOD_INDEX, "fr_method_index.html")
+ 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
+ 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
-
- def aliases
- @context.aliases
- end
+ values = {
+ "entries" => res,
+ 'list_title' => CGI.escapeHTML(title),
+ 'index_url' => main_url,
+ 'charset' => @options.charset,
+ 'style_url' => style_url('', @options.css),
+ }
- def find_symbol(symbol, method=nil)
- res = @context.parent.find_symbol(symbol, method)
- if res
- res = res.viewer
- end
- res
+ open filename, 'w' do |f|
+ template.write_html_on(f, values)
end
-
end
##
- # 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.
-
- class HTML
-
- include MarkUp
+ # 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.
- ##
- # Converts a target url to one that is relative to a given path
+ def gen_main_index
+ template = RDoc::TemplatePage.new @template::INDEX
- def self.gen_url(path, target)
- from = File.dirname(path)
- to, to_file = File.split(target)
+ open 'index.html', 'w' do |f|
+ classes = @classes.sort.map { |klass| klass.value_hash }
- from = from.split("/")
- to = to.split("/")
+ 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,
+ }
- while from.size > 0 and to.size > 0 and from[0] == to[0]
- from.shift
- to.shift
- end
+ values['inline_source'] = @options.inline_source
- from.fill("..")
- from.concat(to)
- from << to_file
- File.join(*from)
+ template.write_html_on f, values
end
+ 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)
+ ##
+ # 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
- HTML.new(options)
+ $stderr.puts "Could not find main page #{@main_page}"
end
end
- class <<self
- protected :new
+ unless @main_page_path then
+ file = @files.find { |file| file.document_self }
+ @main_page_path = file.path if file
end
- ##
- # Set up a new HTML generator. Basically all we do here is load up the
- # correct output temlate
-
- def initialize(options) #:not-new:
- @options = options
- 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
+ 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
- ##
- # 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
-
+ @main_page_path
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 = {}
+end
- build_indices
- generate_xml
- end
+class RDoc::Generator::HTMLInOne < RDoc::Generator::HTML
- ##
- # 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 initialize(*args)
+ super
+ end
- def build_indices
+ ##
+ # Build the initial indices and output objects
+ # based on an array of TopLevel objects containing
+ # the extracted information.
- @toplevels.each do |toplevel|
- @files << HtmlFile.new(toplevel, @options, FILE_DIR)
- end
+ def generate(info)
+ @toplevels = info
+ @hyperlinks = {}
- RDoc::TopLevel.all_classes_and_modules.each do |cls|
- build_class_list(cls, @files[0], CLASS_DIR)
- end
- end
+ build_indices
+ generate_xml
+ 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:
+ #
+ # * 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 build_indices
+ @files, @classes = RDoc::Generator::Context.build_indices(@toplevels,
+ @options)
+ end
- ##
- # Generate all the HTML. For the one-file case, we generate
- # all the information in to one big hash
+ ##
+ # 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),
- }
+ 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
+ # this method is defined in the template file
+ write_extra_pages if defined? write_extra_pages
- template = RDoc::TemplatePage.new @template::ONE_PAGE
+ template = RDoc::TemplatePage.new @template::ONE_PAGE
- if @options.op_name
- opfile = File.open(@options.op_name, "w")
- else
- opfile = $stdout
- end
- template.write_html_on(opfile, values)
+ if @options.op_name
+ opfile = 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
+ 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_file_index
+ gen_an_index(@files, 'Files')
+ end
- def gen_class_index
- gen_an_index(@classes, 'Classes')
- 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_method_index
+ gen_an_index(RDoc::Generator::Method.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
+ 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
-
- return {
- "entries" => res,
- 'list_title' => title,
- 'index_url' => main_url,
- }
end
+ return {
+ "entries" => res,
+ 'list_title' => title,
+ 'index_url' => main_url,
+ }
end
end
+
diff --git a/lib/rdoc/generator/html/html.rb b/lib/rdoc/generator/html/html.rb
index 0a0b754919..8343454d30 100644
--- a/lib/rdoc/generator/html/html.rb
+++ b/lib/rdoc/generator/html/html.rb
@@ -478,14 +478,14 @@ EOF
<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"] %>
diff --git a/lib/rdoc/markup.rb b/lib/rdoc/markup.rb
index 8f978dc7bc..9334329d6b 100644
--- a/lib/rdoc/markup.rb
+++ b/lib/rdoc/markup.rb
@@ -146,16 +146,16 @@ require 'rdoc'
# 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@pragmaticprogrammer.com
@@ -194,7 +194,7 @@ class RDoc::Markup
# identify significant chunks.
def initialize
- @am = AttributeManager.new
+ @am = RDoc::Markup::AttributeManager.new
@output = nil
end
@@ -234,15 +234,16 @@ class RDoc::Markup
# 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 @@ class RDoc::Markup
# 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 @@ class RDoc::Markup
# 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 @@ class RDoc::Markup
next
end
-
if LABEL_LIST_RE =~ active_line
offset = margin + $1.length
prefix = $2
@@ -366,22 +364,23 @@ class RDoc::Markup
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 @@ class RDoc::Markup
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 @@ class RDoc::Markup
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 @@ class RDoc::Markup
end
require 'rdoc/markup/fragments'
+require 'rdoc/markup/inline'
require 'rdoc/markup/lines'
diff --git a/lib/rdoc/markup/formatter.rb b/lib/rdoc/markup/formatter.rb
new file mode 100644
index 0000000000..14cbae59f9
--- /dev/null
+++ b/lib/rdoc/markup/formatter.rb
@@ -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
+
diff --git a/lib/rdoc/markup/fragments.rb b/lib/rdoc/markup/fragments.rb
index 39b63cae22..1765861ad0 100644
--- a/lib/rdoc/markup/fragments.rb
+++ b/lib/rdoc/markup/fragments.rb
@@ -83,10 +83,16 @@ class RDoc::Markup
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 @@ class RDoc::Markup
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
diff --git a/lib/rdoc/markup/inline.rb b/lib/rdoc/markup/inline.rb
index cbf5032a68..8945e14b83 100644
--- a/lib/rdoc/markup/inline.rb
+++ b/lib/rdoc/markup/inline.rb
@@ -12,9 +12,9 @@ class RDoc::Markup
@@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 @@ class RDoc::Markup
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 @@ class RDoc::Markup
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 @@ class RDoc::Markup
self.text == o.text && self.type == o.type
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
+
+ def to_s
+ "Special: type=#{type}, name=#{RDoc::Markup::Attribute.as_string type}, text=#{text.dump}"
+ end
+
end
class AttributeManager
@@ -165,8 +166,10 @@ class RDoc::Markup
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 @@ class RDoc::Markup
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 @@ class RDoc::Markup
# 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 @@ class RDoc::Markup
end
def split_into_flow
-
display_attributes if $DEBUG_RDOC
res = []
diff --git a/lib/rdoc/markup/lines.rb b/lib/rdoc/markup/lines.rb
index 985304c225..069492122f 100644
--- a/lib/rdoc/markup/lines.rb
+++ b/lib/rdoc/markup/lines.rb
@@ -59,8 +59,8 @@ class RDoc::Markup
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
diff --git a/lib/rdoc/markup/to_flow.rb b/lib/rdoc/markup/to_flow.rb
index cb7da5fa88..3d87b3e9c3 100644
--- a/lib/rdoc/markup/to_flow.rb
+++ b/lib/rdoc/markup/to_flow.rb
@@ -1,3 +1,4 @@
+require 'rdoc/markup/formatter'
require 'rdoc/markup/fragments'
require 'rdoc/markup/inline'
require 'cgi'
@@ -22,7 +23,7 @@ class RDoc::Markup
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 @@ class RDoc::Markup
InlineTag = Struct.new(:bit, :on, :off)
def initialize
+ super
+
init_tags
end
diff --git a/lib/rdoc/markup/to_html.rb b/lib/rdoc/markup/to_html.rb
index 0238d5ae67..da42312ff5 100644
--- a/lib/rdoc/markup/to_html.rb
+++ b/lib/rdoc/markup/to_html.rb
@@ -1,22 +1,25 @@
+require 'rdoc/markup/formatter'
require 'rdoc/markup/fragments'
require 'rdoc/markup/inline'
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 @@ class RDoc::Markup::ToHtml
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
diff --git a/lib/rdoc/markup/to_html_hyperlink.rb b/lib/rdoc/markup/to_html_hyperlink.rb
new file mode 100644
index 0000000000..58adaa8cb0
--- /dev/null
+++ b/lib/rdoc/markup/to_html_hyperlink.rb
@@ -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
+
diff --git a/lib/rdoc/markup/to_latex.rb b/lib/rdoc/markup/to_latex.rb
index 8b7e33719b..a679b3b06e 100644
--- a/lib/rdoc/markup/to_latex.rb
+++ b/lib/rdoc/markup/to_latex.rb
@@ -1,3 +1,4 @@
+require 'rdoc/markup/formatter'
require 'rdoc/markup/fragments'
require 'rdoc/markup/inline'
@@ -6,7 +7,7 @@ require 'cgi'
##
# Convert SimpleMarkup to basic LaTeX report format.
-class RDoc::Markup::ToLaTeX
+class RDoc::Markup::ToLaTeX < RDoc::Markup::Formatter
BS = "\020" # \
OB = "\021" # {
diff --git a/lib/rdoc/markup/to_test.rb b/lib/rdoc/markup/to_test.rb
index eb183e5ba4..ce6aff6e9a 100644
--- a/lib/rdoc/markup/to_test.rb
+++ b/lib/rdoc/markup/to_test.rb
@@ -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 = []
diff --git a/lib/rdoc/options.rb b/lib/rdoc/options.rb
index bc14080630..364cd61d38 100644
--- a/lib/rdoc/options.rb
+++ b/lib/rdoc/options.rb
@@ -49,7 +49,7 @@ class RDoc::Options
##
# 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 @@ class RDoc::Options
##
# 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
diff --git a/lib/rdoc/rdoc.rb b/lib/rdoc/rdoc.rb
index ffaf56e427..4b9fa88707 100644
--- a/lib/rdoc/rdoc.rb
+++ b/lib/rdoc/rdoc.rb
@@ -53,13 +53,15 @@ module RDoc
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 @@ module RDoc
file_info
end
- public
-
##
# Format up one or more files according to the given arguments.
#
@@ -223,8 +223,6 @@ module RDoc
def document(argv)
TopLevel::reset
- @stats = Stats.new
-
options = Options.new GENERATORS
options.parse argv