summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog4
-rw-r--r--lib/rdoc.rb134
-rw-r--r--lib/rdoc/code_objects.rb324
-rw-r--r--lib/rdoc/generator.rb300
-rw-r--r--lib/rdoc/generator/html.rb63
-rw-r--r--lib/rdoc/generator/html/frameless.rb795
-rw-r--r--lib/rdoc/generator/html/hefss.rb22
-rw-r--r--lib/rdoc/generator/html/html.rb168
-rw-r--r--lib/rdoc/generator/html/kilmer.rb20
-rw-r--r--lib/rdoc/generator/html/one_page_html.rb18
-rw-r--r--lib/rdoc/generator/ri.rb2
-rw-r--r--lib/rdoc/generator/texinfo.rb84
-rw-r--r--lib/rdoc/generator/texinfo/class.texinfo.erb44
-rw-r--r--lib/rdoc/generator/texinfo/file.texinfo.erb6
-rw-r--r--lib/rdoc/generator/texinfo/method.texinfo.erb6
-rw-r--r--lib/rdoc/generator/texinfo/texinfo.erb28
-rw-r--r--lib/rdoc/known_classes.rb69
-rw-r--r--lib/rdoc/markup/attribute_manager.rb2
-rw-r--r--lib/rdoc/markup/fragments.rb4
-rw-r--r--lib/rdoc/markup/preprocess.rb16
-rw-r--r--lib/rdoc/markup/to_html.rb64
-rw-r--r--lib/rdoc/markup/to_html_crossref.rb26
-rw-r--r--lib/rdoc/markup/to_texinfo.rb69
-rw-r--r--lib/rdoc/options.rb14
-rw-r--r--lib/rdoc/parser.rb109
-rw-r--r--lib/rdoc/parser/c.rb666
-rw-r--r--lib/rdoc/parser/f95.rb1837
-rw-r--r--lib/rdoc/parser/ruby.rb (renamed from lib/rdoc/parsers/parse_rb.rb)2307
-rw-r--r--lib/rdoc/parser/simple.rb38
-rw-r--r--lib/rdoc/parsers/parse_c.rb775
-rw-r--r--lib/rdoc/parsers/parse_f95.rb1841
-rw-r--r--lib/rdoc/parsers/parse_simple.rb40
-rw-r--r--lib/rdoc/parsers/parserfactory.rb99
-rw-r--r--lib/rdoc/rdoc.rb44
-rw-r--r--lib/rdoc/ri.rb6
-rw-r--r--lib/rdoc/ri/descriptions.rb3
-rw-r--r--lib/rdoc/ri/driver.rb154
-rw-r--r--test/rdoc/test_rdoc_info_formatting.rb179
-rw-r--r--test/rdoc/test_rdoc_info_sections.rb93
-rw-r--r--test/rdoc/test_rdoc_markup_to_html.rb30
-rw-r--r--test/rdoc/test_rdoc_markup_to_html_crossref.rb18
-rw-r--r--test/rdoc/test_rdoc_parser_c.rb (renamed from test/rdoc/test_rdoc_c_parser.rb)9
-rw-r--r--test/rdoc/test_rdoc_parser_ruby.rb500
-rw-r--r--test/rdoc/test_rdoc_ri_default_display.rb33
-rw-r--r--test/rdoc/test_rdoc_ri_driver.rb100
45 files changed, 6859 insertions, 4304 deletions
diff --git a/ChangeLog b/ChangeLog
index 854bd77ff9..9996be52b8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+Fri Jul 18 09:44:30 2008
+
+ * lib/rdoc/*: Import RDoc r101.
+
Thu Jul 17 23:45:55 2008 Yusuke Endoh <mame@tsg.ne.jp>
* test/rdoc/test_rdoc_c_parser.rb (teardown): close tempfile.
diff --git a/lib/rdoc.rb b/lib/rdoc.rb
index 7aae05731f..53b72241f8 100644
--- a/lib/rdoc.rb
+++ b/lib/rdoc.rb
@@ -1,8 +1,8 @@
$DEBUG_RDOC = nil
##
-# = RDOC - Ruby Documentation System
-#
+# RDoc - Ruby Documentation System
+#
# This package contains RDoc and RDoc::Markup. RDoc is an application that
# produces documentation for one or more Ruby source files. We work similarly
# to JavaDoc, parsing the source, and extracting the definition for classes,
@@ -12,12 +12,12 @@ $DEBUG_RDOC = nil
# RDoc::Markup is a library that converts plain text into various output
# formats. The markup library is used to interpret the comment blocks that
# RDoc uses to document methods, classes, and so on.
-#
+#
# == Roadmap
-#
+#
# * If you want to use RDoc to create documentation for your Ruby source files,
# read on.
-# * If you want to include extensions written in C, see RDoc::C_Parser
+# * If you want to include extensions written in C, see RDoc::Parser::C
# * For information on the various markups available in comment blocks, see
# RDoc::Markup.
# * If you want to drive RDoc programmatically, see RDoc::RDoc.
@@ -25,63 +25,63 @@ $DEBUG_RDOC = nil
# at RDoc::Markup.
# * If you want to try writing your own HTML output template, see
# RDoc::Generator::HTML
-#
+#
# == Summary
-#
+#
# Once installed, you can create documentation using the 'rdoc' command
# (the command is 'rdoc.bat' under Windows)
-#
+#
# % rdoc [options] [names...]
-#
+#
# Type "rdoc --help" for an up-to-date option summary.
-#
+#
# A typical use might be to generate documentation for a package of Ruby
-# source (such as rdoc itself).
-#
+# source (such as rdoc itself).
+#
# % rdoc
-#
+#
# This command generates documentation for all the Ruby and C source
# files in and below the current directory. These will be stored in a
# documentation tree starting in the subdirectory 'doc'.
-#
+#
# You can make this slightly more useful for your readers by having the
# index page contain the documentation for the primary file. In our
# case, we could type
-#
+#
# % rdoc --main rdoc.rb
-#
+#
# You'll find information on the various formatting tricks you can use
# in comment blocks in the documentation this generates.
-#
+#
# RDoc uses file extensions to determine how to process each file. File names
# ending +.rb+ and <tt>.rbw</tt> are assumed to be Ruby source. Files
# ending +.c+ are parsed as C files. All other files are assumed to
# contain just Markup-style markup (with or without leading '#' comment
# markers). If directory names are passed to RDoc, they are scanned
# recursively for C and Ruby source files only.
-#
+#
# = Markup
-#
+#
# For information on how to make lists, hyperlinks, etc. with RDoc, see
# RDoc::Markup.
-#
+#
# Comment blocks can be written fairly naturally, either using '#' on
# successive lines of the comment, or by including the comment in
# an =begin/=end block. If you use the latter form, the =begin line must be
# flagged with an RDoc tag:
-#
+#
# =begin rdoc
# Documentation to be processed by RDoc.
#
# ...
# =end
-#
+#
# RDoc stops processing comments if it finds a comment line containing
# a <tt>--</tt>. This can be used to separate external from internal
# comments, or to stop a comment being associated with a method, class, or
# module. Commenting can be turned back on with a line that starts with a
# <tt>++</tt>.
-#
+#
# ##
# # Extract the age and calculate the date-of-birth.
# #--
@@ -92,40 +92,40 @@ $DEBUG_RDOC = nil
# def get_dob(person)
# # ...
# end
-#
+#
# Names of classes, source files, and any method names containing an
# underscore or preceded by a hash character are automatically hyperlinked
-# from comment text to their description.
-#
+# from comment text to their description.
+#
# Method parameter lists are extracted and displayed with the method
# description. If a method calls +yield+, then the parameters passed to yield
# will also be displayed:
-#
+#
# def fred
# ...
# yield line, address
-#
+#
# This will get documented as:
-#
+#
# fred() { |line, address| ... }
-#
+#
# You can override this using a comment containing ':yields: ...' immediately
# after the method definition
-#
+#
# def fred # :yields: index, position
# # ...
#
# yield line, address
-#
+#
# which will get documented as
-#
+#
# fred() { |index, position| ... }
-#
+#
# +:yields:+ is an example of a documentation directive. These appear
# immediately after the start of the document element they are modifying.
-#
+#
# == Directives
-#
+#
# [+:nodoc:+ / +:nodoc:+ all]
# Don't include this element in the documentation. For classes
# and modules, the methods, aliases, constants, and attributes
@@ -143,27 +143,27 @@ $DEBUG_RDOC = nil
# class Output
# end
# end
-#
-# In the above code, only class +MyModule::Input+ will be documented.
-# :nodoc: is global across all files the class or module appears in, so use
-# :stopdoc:/:startdoc: to only omit documentation for a particular set of
-# methods, etc.
-#
+#
+# In the above code, only class +MyModule::Input+ will be documented.The
+# The :nodoc: directive is global across all files the class or module
+# appears in, so use :stopdoc:/:startdoc: to only omit documentation for a
+# particular set of methods, etc.
+#
# [+:doc:+]
# Force a method or attribute to be documented even if it wouldn't otherwise
# be. Useful if, for example, you want to include documentation of a
# particular private method.
-#
+#
# [+:notnew:+]
# Only applicable to the +initialize+ instance method. Normally RDoc
-# assumes that the documentation and parameters for #initialize are
+# assumes that the documentation and parameters for #initialize are
# actually for the ::new method, and so fakes out a ::new for the class.
# The :notnew: modifier stops this. Remember that #initialize is protected,
# so you won't see the documentation unless you use the -a command line
# option.
-#
+#
# Comment blocks can contain other directives:
-#
+#
# [<tt>:section: title</tt>]
# Starts a new section in the output. The title following +:section:+ is
# used as the section heading, and the remainder of the comment containing
@@ -178,66 +178,66 @@ $DEBUG_RDOC = nil
# # This is the section that I wrote.
# # See it glisten in the noon-day sun.
# # ----------------------------------------
-#
+#
# [+:call-seq:+]
# Lines up to the next blank line in the comment are treated as the method's
# calling sequence, overriding the default parsing of method parameters and
# yield arguments.
-#
+#
# [+:include:+ _filename_]
# \Include the contents of the named file at this point. The file will be
# searched for in the directories listed by the +--include+ option, or in
# the current directory by default. The contents of the file will be
-# shifted to have the same indentation as the ':' at the start of the
-# :include: directive.
-#
+# shifted to have the same indentation as the ':' at the start of
+# the :include: directive.
+#
# [+:title:+ _text_]
# Sets the title for the document. Equivalent to the <tt>--title</tt>
# command line parameter. (The command line parameter overrides any :title:
# directive in the source).
-#
+#
# [+:enddoc:+]
# Document nothing further at the current level.
-#
+#
# [+:main:+ _name_]
# Equivalent to the <tt>--main</tt> command line parameter.
-#
+#
# [+:stopdoc:+ / +:startdoc:+]
# Stop and start adding new documentation elements to the current container.
# For example, if a class has a number of constants that you don't want to
# document, put a +:stopdoc:+ before the first, and a +:startdoc:+ after the
# last. If you don't specify a +:startdoc:+ by the end of the container,
# disables documentation for the entire class or module.
-#
+#
# = Other stuff
-#
+#
# RDoc is currently being maintained by Eric Hodel <drbrain@segment7.net>
#
# Dave Thomas <dave@pragmaticprogrammer.com> is the original author of RDoc.
-#
+#
# == Credits
-#
+#
# * The Ruby parser in rdoc/parse.rb is based heavily on the outstanding
# work of Keiju ISHITSUKA of Nippon Rational Inc, who produced the Ruby
# parser for irb and the rtags package.
-#
+#
# * Code to diagram classes and modules was written by Sergey A Yanovitsky
# (Jah) of Enticla.
-#
+#
# * Charset patch from MoonWolf.
-#
+#
# * Rich Kilmer wrote the kilmer.rb output template.
-#
+#
# * Dan Brickley led the design of the RDF format.
-#
+#
# == License
-#
+#
# RDoc is Copyright (c) 2001-2003 Dave Thomas, The Pragmatic Programmers. It
# is free software, and may be redistributed under the terms specified
# in the README file of the Ruby distribution.
-#
+#
# == Warranty
-#
+#
# This software is provided "as is" and without any express or implied
# warranties, including, without limitation, the implied warranties of
# merchantibility and fitness for a particular purpose.
@@ -254,7 +254,7 @@ module RDoc
##
# RDoc version you are using
- VERSION = "2.0.0"
+ VERSION = "2.1.0"
##
# Name of the dotfile that contains the description of files to be processed
diff --git a/lib/rdoc/code_objects.rb b/lib/rdoc/code_objects.rb
index b93ec99007..58adc5a351 100644
--- a/lib/rdoc/code_objects.rb
+++ b/lib/rdoc/code_objects.rb
@@ -6,8 +6,8 @@ require 'rdoc/tokenstream'
module RDoc
##
- # We contain the common stuff for contexts (which are containers)
- # and other elements (methods, attributes and so on)
+ # We contain the common stuff for contexts (which are containers) and other
+ # elements (methods, attributes and so on)
class CodeObject
@@ -31,6 +31,13 @@ module RDoc
attr_reader :document_self
+ def initialize
+ @document_self = true
+ @document_children = true
+ @force_documentation = false
+ @done_documenting = false
+ end
+
def document_self=(val)
@document_self = val
if !val
@@ -72,13 +79,6 @@ module RDoc
def remove_methods_etc
end
- def initialize
- @document_self = true
- @document_children = true
- @force_documentation = false
- @done_documenting = false
- end
-
# Access the code object's comment
attr_reader :comment
@@ -106,15 +106,24 @@ module RDoc
end
- # A Context is something that can hold modules, classes, methods,
- # attributes, aliases, requires, and includes. Classes, modules, and
- # files are all Contexts.
+ ##
+ # A Context is something that can hold modules, classes, methods,
+ # attributes, aliases, requires, and includes. Classes, modules, and files
+ # are all Contexts.
class Context < CodeObject
- attr_reader :name, :method_list, :attributes, :aliases, :constants
- attr_reader :requires, :includes, :in_files, :visibility
- attr_reader :sections
+ attr_reader :aliases
+ attr_reader :attributes
+ attr_reader :constants
+ attr_reader :current_section
+ attr_reader :in_files
+ attr_reader :includes
+ attr_reader :method_list
+ attr_reader :name
+ attr_reader :requires
+ attr_reader :sections
+ attr_reader :visibility
class Section
attr_reader :title, :comment, :sequence
@@ -129,12 +138,22 @@ module RDoc
set_comment(comment)
end
- private
+ def ==(other)
+ self.class === other and @sequence == other.sequence
+ end
+
+ def inspect
+ "#<%s:0x%x %s %p>" % [
+ self.class, object_id,
+ @sequence, title
+ ]
+ end
- # Set the comment for this section from the original comment block
- # If the first line contains :section:, strip it and use the rest. Otherwise
- # remove lines up to the line containing :section:, and look for
- # those lines again at the end and remove them. This lets us write
+ ##
+ # Set the comment for this section from the original comment block If
+ # the first line contains :section:, strip it and use the rest.
+ # Otherwise remove lines up to the line containing :section:, and look
+ # for those lines again at the end and remove them. This lets us write
#
# # ---------------------
# # :SECTION: The title
@@ -144,9 +163,10 @@ module RDoc
def set_comment(comment)
return unless comment
- if comment =~ /^.*?:section:.*$/
+ if comment =~ /^#[ \t]*:section:.*\n/
start = $`
rest = $'
+
if start.empty?
@comment = rest
else
@@ -157,13 +177,13 @@ module RDoc
end
@comment = nil if @comment.empty?
end
- end
+ end
def initialize
- super()
+ super
- @in_files = []
+ @in_files = []
@name ||= "unknown"
@comment ||= ""
@@ -177,29 +197,37 @@ module RDoc
initialize_classes_and_modules
end
+ ##
# map the class hash to an array externally
+
def classes
@classes.values
end
+ ##
# map the module hash to an array externally
+
def modules
@modules.values
end
+ ##
# Change the default visibility for new methods
+
def ongoing_visibility=(vis)
@visibility = vis
end
- # Given an array +methods+ of method names, set the
- # visibility of the corresponding AnyMethod object
+ ##
+ # Yields Method and Attr entries matching the list of names in +methods+.
+ # Attributes are only returned when +singleton+ is false.
- def set_visibility_for(methods, vis, singleton=false)
+ def methods_matching(methods, singleton = false)
count = 0
+
@method_list.each do |m|
- if methods.include?(m.name) && m.singleton == singleton
- m.visibility = vis
+ if methods.include? m.name and m.singleton == singleton then
+ yield m
count += 1
end
end
@@ -209,14 +237,23 @@ module RDoc
# perhaps we need to look at attributes
@attributes.each do |a|
- if methods.include?(a.name)
- a.visibility = vis
- count += 1
- end
+ yield a if methods.include? a.name
end
end
+ ##
+ # Given an array +methods+ of method names, set the visibility of the
+ # corresponding AnyMethod object
+
+ def set_visibility_for(methods, vis, singleton = false)
+ methods_matching methods, singleton do |m|
+ m.visibility = vis
+ end
+ end
+
+ ##
# Record the file that we happen to find it in
+
def record_location(toplevel)
@in_files << toplevel unless @in_files.include?(toplevel)
end
@@ -269,10 +306,10 @@ module RDoc
# Requires always get added to the top-level (file) context
def add_require(a_require)
- if self.kind_of? TopLevel
- add_to(@requires, a_require)
+ if TopLevel === self then
+ add_to @requires, a_require
else
- parent.add_require(a_require)
+ parent.add_require a_require
end
end
@@ -292,7 +329,7 @@ module RDoc
end
def add_to(array, thing)
- array << thing if @document_self && !@done_documenting
+ array << thing if @document_self and not @done_documenting
thing.parent = self
thing.section = @current_section
end
@@ -371,26 +408,30 @@ module RDoc
name <=> other.name
end
- # Look up the given symbol. If method is non-nil, then
- # we assume the symbol references a module that
- # contains that method
- def find_symbol(symbol, method=nil)
+ ##
+ # Look up +symbol+. If +method+ is non-nil, then we assume the symbol
+ # references a module that contains that method.
+
+ def find_symbol(symbol, method = nil)
result = nil
+
case symbol
- when /^::(.*)/
+ when /^::(.*)/ then
result = toplevel.find_symbol($1)
- when /::/
+ when /::/ then
modules = symbol.split(/::/)
- unless modules.empty?
+
+ unless modules.empty? then
module_name = modules.shift
result = find_module_named(module_name)
- if result
+ if result then
modules.each do |name|
result = result.find_module_named(name)
break unless result
end
end
end
+
else
# if a method is specified, then we're definitely looking for
# a module, otherwise it could be any symbol
@@ -408,22 +449,21 @@ module RDoc
end
end
end
- if result && method
- if !result.respond_to?(:find_local_symbol)
- #p result.name
- #p method
- fail
- end
+
+ if result and method then
+ fail unless result.respond_to? :find_local_symbol
result = result.find_local_symbol(method)
end
+
result
end
-
+
def find_local_symbol(symbol)
res = find_method_named(symbol) ||
find_constant_named(symbol) ||
find_attribute_named(symbol) ||
- find_module_named(symbol)
+ find_module_named(symbol) ||
+ find_file_named(symbol)
end
# Handle sections
@@ -454,7 +494,14 @@ module RDoc
def find_attribute_named(name)
@attributes.find {|m| m.name == name}
end
-
+
+ ##
+ # Find a named file, or return nil
+
+ def find_file_named(name)
+ toplevel.class.find_file_named(name)
+ end
+
end
##
@@ -465,22 +512,29 @@ module RDoc
attr_accessor :file_relative_name
attr_accessor :file_absolute_name
attr_accessor :diagram
-
+
@@all_classes = {}
@@all_modules = {}
+ @@all_files = {}
def self.reset
@@all_classes = {}
@@all_modules = {}
+ @@all_files = {}
end
def initialize(file_name)
super()
@name = "TopLevel"
- @file_relative_name = file_name
- @file_absolute_name = file_name
- @file_stat = File.stat(file_name)
- @diagram = nil
+ @file_relative_name = file_name
+ @file_absolute_name = file_name
+ @file_stat = File.stat(file_name)
+ @diagram = nil
+ @@all_files[file_name] = self
+ end
+
+ def file_base_name
+ File.basename @file_absolute_name
end
def full_name
@@ -497,7 +551,7 @@ module RDoc
cls = collection[name]
if cls
- puts "Reusing class/module #{name}" if $DEBUG_RDOC
+ puts "Reusing class/module #{name}" #if $DEBUG_RDOC
else
if class_type == NormalModule
all = @@all_modules
@@ -534,6 +588,10 @@ module RDoc
nil
end
+ def self.find_file_named(name)
+ @@all_files[name]
+ end
+
def find_local_symbol(symbol)
find_class_or_module_named(symbol) || super
end
@@ -553,8 +611,9 @@ module RDoc
end
- # ClassModule is the base class for objects representing either a
- # class or a module.
+ ##
+ # ClassModule is the base class for objects representing either a class or a
+ # module.
class ClassModule < Context
@@ -603,29 +662,63 @@ module RDoc
end
end
+ ##
# Anonymous classes
+
class AnonClass < ClassModule
end
+ ##
# Normal classes
+
class NormalClass < ClassModule
+
+ def inspect
+ superclass = @superclass ? " < #{@superclass}" : nil
+ "<%s:0x%x class %s%s includes: %p attributes: %p methods: %p aliases: %p>" % [
+ self.class, object_id,
+ @name, superclass, @includes, @attributes, @method_list, @aliases
+ ]
+ end
+
end
+ ##
# Singleton classes
+
class SingleClass < ClassModule
end
+ ##
# Module
+
class NormalModule < ClassModule
+
+ def comment=(comment)
+ return if comment.empty?
+ comment = @comment << "# ---\n" << comment unless @comment.empty?
+
+ super
+ end
+
+ def inspect
+ "#<%s:0x%x module %s includes: %p attributes: %p methods: %p aliases: %p>" % [
+ self.class, object_id,
+ @name, @includes, @attributes, @method_list, @aliases
+ ]
+ end
+
def is_module?
true
end
+
end
##
# AnyMethod is the base class for objects representing methods
class AnyMethod < CodeObject
+
attr_accessor :name
attr_accessor :visibility
attr_accessor :block_params
@@ -663,10 +756,20 @@ module RDoc
@name <=> other.name
end
- def to_s
- res = self.class.name + ": " + @name + " (" + @text + ")\n"
- res << @comment.to_s
- res
+ def add_alias(method)
+ @aliases << method
+ end
+
+ def inspect
+ alias_for = @is_alias_for ? " (alias for #{@is_alias_for.name})" : nil
+ "#<%s:0x%x %s%s%s (%s)%s>" % [
+ self.class, object_id,
+ @parent.name,
+ singleton ? '::' : '#',
+ name,
+ visibility,
+ alias_for,
+ ]
end
def param_seq
@@ -691,16 +794,34 @@ $stderr.puts p
p
end
- def add_alias(method)
- @aliases << method
+ def to_s
+ res = self.class.name + ": " + @name + " (" + @text + ")\n"
+ res << @comment.to_s
+ res
end
+
+ end
+
+ ##
+ # GhostMethod represents a method referenced only by a comment
+
+ class GhostMethod < AnyMethod
+ end
+
+ ##
+ # MetaMethod represents a meta-programmed method
+
+ class MetaMethod < AnyMethod
end
- # Represent an alias, which is an old_name/ new_name pair associated
- # with a particular context
+ ##
+ # Represent an alias, which is an old_name/ new_name pair associated with a
+ # particular context
+
class Alias < CodeObject
+
attr_accessor :text, :old_name, :new_name, :comment
-
+
def initialize(text, old_name, new_name, comment)
super()
@text = text
@@ -709,12 +830,22 @@ $stderr.puts p
self.comment = comment
end
+ def inspect
+ "#<%s:0x%x %s.alias_method %s, %s>" % [
+ self.class, object_id,
+ parent.name, @old_name, @new_name,
+ ]
+ end
+
def to_s
"alias: #{self.old_name} -> #{self.new_name}\n#{self.comment}"
end
+
end
+ ##
# Represent a constant
+
class Constant < CodeObject
attr_accessor :name, :value
@@ -726,7 +857,9 @@ $stderr.puts p
end
end
+ ##
# Represent attributes
+
class Attr < CodeObject
attr_accessor :text, :name, :rw, :visibility
@@ -739,16 +872,33 @@ $stderr.puts p
self.comment = comment
end
+ def <=>(other)
+ self.name <=> other.name
+ end
+
+ def inspect
+ attr = case rw
+ when 'RW' then :attr_accessor
+ when 'R' then :attr_reader
+ when 'W' then :attr_writer
+ else
+ " (#{rw})"
+ end
+
+ "#<%s:0x%x %s.%s :%s>" % [
+ self.class, object_id,
+ @parent.name, attr, @name,
+ ]
+ end
+
def to_s
"attr: #{self.name} #{self.rw}\n#{self.comment}"
end
- def <=>(other)
- self.name <=> other.name
- end
end
- # a required file
+ ##
+ # A required file
class Require < CodeObject
attr_accessor :name
@@ -759,16 +909,38 @@ $stderr.puts p
self.comment = comment
end
+ def inspect
+ "#<%s:0x%x require '%s' in %s>" % [
+ self.class,
+ object_id,
+ @name,
+ @parent.file_base_name,
+ ]
+ end
+
end
- # an included module
+ ##
+ # An included module
+
class Include < CodeObject
+
attr_accessor :name
def initialize(name, comment)
super()
@name = name
self.comment = comment
+
+ end
+
+ def inspect
+ "#<%s:0x%x %s.include %s>" % [
+ self.class,
+ object_id,
+ @parent.name,
+ @name,
+ ]
end
end
diff --git a/lib/rdoc/generator.rb b/lib/rdoc/generator.rb
index 7c4ccf924a..00a5ad45db 100644
--- a/lib/rdoc/generator.rb
+++ b/lib/rdoc/generator.rb
@@ -22,27 +22,6 @@ 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.
@@ -80,11 +59,6 @@ module RDoc::Generator
def markup(str, remove_para = false)
return '' unless str
- unless defined? @formatter then
- @formatter = RDoc::Markup::ToHtmlCrossref.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
@@ -93,7 +67,7 @@ module RDoc::Generator
content = str.gsub(/^\s*(#+)/) { $1.tr '#', ' ' }
end
- res = @formatter.convert content
+ res = formatter.convert content
if remove_para then
res.sub!(/^<p>/, '')
@@ -114,7 +88,7 @@ module RDoc::Generator
if %r{^(https?:/)?/} =~ css_name
css_name
else
- RDoc::Generator.gen_url path, css_name
+ RDoc::Markup::ToHtml.gen_relative_url path, css_name
end
end
@@ -186,6 +160,11 @@ module RDoc::Generator
@template = options.template_class
end
+ def formatter
+ @formatter ||= @options.formatter ||
+ RDoc::Markup::ToHtmlCrossref.new(path, self, @options.show_hash)
+ end
+
##
# convenience method to build a hyperlink
@@ -201,7 +180,7 @@ module RDoc::Generator
if @options.all_one_file
"#" + path
else
- RDoc::Generator.gen_url from_path, path
+ RDoc::Markup::ToHtml.gen_relative_url from_path, path
end
end
@@ -215,7 +194,7 @@ module RDoc::Generator
list = @context.method_list
unless @options.show_all then
- list = list.find_all do |m|
+ list = list.select do |m|
m.visibility == :public or
m.visibility == :protected or
m.force_documentation
@@ -230,17 +209,15 @@ module RDoc::Generator
##
# Build a summary list of all the methods in this context
- def build_method_summary_list(path_prefix="")
+ def build_method_summary_list(path_prefix = "")
collect_methods unless @methods
- meths = @methods.sort
- res = []
- meths.each do |meth|
- res << {
+
+ @methods.sort.map do |meth|
+ {
"name" => CGI.escapeHTML(meth.name),
"aref" => "#{path_prefix}\##{meth.aref}"
}
end
- res
end
##
@@ -248,36 +225,40 @@ module RDoc::Generator
# corresponding method
def build_alias_summary_list(section)
- values = []
- @context.aliases.each do |al|
+ @context.aliases.map 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 al.comment and not al.comment.empty? then
+ res['desc'] = markup al.comment, true
end
- values << res
- end
- values
+
+ res
+ end.compact
end
##
# Build a list of constants
def build_constants_summary_list(section)
- values = []
- @context.constants.each do |co|
+ @context.constants.map 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
+
+ if co.comment and not co.comment.empty? then
+ res['desc'] = markup co.comment, true
+ end
+
+ res
+ end.compact
end
def build_requires_list(context)
@@ -339,54 +320,58 @@ module RDoc::Generator
def build_method_detail_list(section)
outer = []
- methods = @methods.sort
+ methods = @methods.sort.select do |m|
+ m.document_self and m.section == section
+ end
+
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
+ next unless m.visibility == vis and m.singleton == singleton
+
+ row = {}
+
+ if m.call_seq then
+ 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 then # won't be if the alias is private
+ alias_names << {
+ 'name' => other.name,
+ 'aref' => other.viewer.as_href(path)
+ }
end
+ 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
+ row["aka"] = alias_names unless alias_names.empty?
+
+ if @options.inline_source then
+ code = m.source_code
+ row["sourcecode"] = code if code
+ else
+ code = m.src_url
+ if code then
+ row["codeurl"] = code
+ row["imgurl"] = m.img_url
end
- res << row
end
+
+ res << row
end
- if res.size > 0
+
+ if res.size > 0 then
outer << {
"type" => vis.to_s.capitalize,
"category" => singleton ? "Class" : "Instance",
@@ -395,6 +380,7 @@ module RDoc::Generator
end
end
end
+
outer
end
@@ -403,8 +389,8 @@ module RDoc::Generator
# in this context.
def build_class_list(level, from, section, infile=nil)
- res = ""
- prefix = "&nbsp;&nbsp;::" * level;
+ prefix = '&nbsp;&nbsp;::' * level;
+ res = ''
from.modules.sort.each do |mod|
next unless mod.section == section
@@ -412,8 +398,8 @@ module RDoc::Generator
if mod.document_self
res <<
prefix <<
- "Module " <<
- href(url(mod.viewer.path), "link", mod.full_name) <<
+ 'Module ' <<
+ href(url(mod.viewer.path), 'link', mod.full_name) <<
"<br />\n" <<
build_class_list(level + 1, mod, section, infile)
end
@@ -421,12 +407,13 @@ module RDoc::Generator
from.classes.sort.each do |cls|
next unless cls.section == section
- next if infile && !cls.defined_in?(infile)
+ next if infile and not cls.defined_in?(infile)
+
if cls.document_self
- res <<
+ res <<
prefix <<
- "Class " <<
- href(url(cls.viewer.path), "link", cls.full_name) <<
+ 'Class ' <<
+ href(url(cls.viewer.path), 'link', cls.full_name) <<
"<br />\n" <<
build_class_list(level + 1, cls, section, infile)
end
@@ -436,7 +423,7 @@ module RDoc::Generator
end
def url(target)
- RDoc::Generator.gen_url path, target
+ RDoc::Markup::ToHtml.gen_relative_url path, target
end
def aref_to(target)
@@ -475,7 +462,7 @@ module RDoc::Generator
def add_table_of_sections
toc = []
@context.sections.each do |section|
- if section.title
+ if section.title then
toc << {
'secname' => section.title,
'href' => section.sequence
@@ -495,11 +482,13 @@ module RDoc::Generator
attr_reader :methods
attr_reader :path
+ attr_reader :values
def initialize(context, html_file, prefix, options)
- super(context, options)
+ super context, options
@html_file = html_file
+ @html_class = self
@is_module = context.is_module?
@values = {}
@@ -540,11 +529,19 @@ module RDoc::Generator
name
end
- def write_on(f)
+ def write_on(f, file_list, class_list, method_list, overrides = {})
value_hash
+
+ @values['file_list'] = file_list
+ @values['class_list'] = class_list
+ @values['method_list'] = method_list
+
+ @values.update overrides
+
template = RDoc::TemplatePage.new(@template::BODY,
@template::CLASS_PAGE,
@template::METHOD_LIST)
+
template.write_html_on(f, @values)
end
@@ -561,30 +558,29 @@ module RDoc::Generator
ml = build_method_summary_list @path
@values["methods"] = ml unless ml.empty?
- il = build_include_list(@context)
+ 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)
+ "seccomment" => markup(section.comment),
}
- al = build_alias_summary_list(section)
+ al = build_alias_summary_list section
secdata["aliases"] = al unless al.empty?
- co = build_constants_summary_list(section)
+ co = build_constants_summary_list section
secdata["constants"] = co unless co.empty?
- al = build_attribute_list(section)
+ al = build_attribute_list section
secdata["attributes"] = al unless al.empty?
- cl = build_class_list(0, @context, section)
+ cl = build_class_list 0, @context, section
secdata["classlist"] = cl unless cl.empty?
- mdl = build_method_detail_list(section)
+ mdl = build_method_detail_list section
secdata["method_list"] = mdl unless mdl.empty?
secdata
@@ -594,23 +590,25 @@ module RDoc::Generator
end
def build_attribute_list(section)
- atts = @context.attributes.sort
- res = []
- atts.each do |att|
+ @context.attributes.sort.map do |att|
next unless att.section == section
- if att.visibility == :public || att.visibility == :protected || @options.show_all
+
+ if att.visibility == :public or att.visibility == :protected or
+ @options.show_all then
+
entry = {
"name" => CGI.escapeHTML(att.name),
"rw" => att.rw,
"a_desc" => markup(att.comment, true)
}
- unless att.visibility == :public || att.visibility == :protected
+
+ unless att.visibility == :public or att.visibility == :protected then
entry["rw"] << "-"
end
- res << entry
+
+ entry
end
- end
- res
+ end.compact
end
def class_attribute_values
@@ -680,9 +678,10 @@ module RDoc::Generator
attr_reader :path
attr_reader :name
+ attr_reader :values
def initialize(context, options, file_dir)
- super(context, options)
+ super context, options
@values = {}
@@ -755,7 +754,7 @@ module RDoc::Generator
}
cl = build_class_list(0, @context, section, file_context)
- @values["classlist"] = cl unless cl.empty?
+ secdata["classlist"] = cl unless cl.empty?
mdl = build_method_detail_list(section)
secdata["method_list"] = mdl unless mdl.empty?
@@ -764,7 +763,7 @@ module RDoc::Generator
secdata["aliases"] = al unless al.empty?
co = build_constants_summary_list(section)
- @values["constants"] = co unless co.empty?
+ secdata["constants"] = co unless co.empty?
secdata
end
@@ -772,9 +771,15 @@ module RDoc::Generator
@values
end
- def write_on(f)
+ def write_on(f, file_list, class_list, method_list, overrides = {})
value_hash
+ @values['file_list'] = file_list
+ @values['class_list'] = class_list
+ @values['method_list'] = method_list
+
+ @values.update overrides
+
template = RDoc::TemplatePage.new(@template::BODY,
@template::FILE_PAGE,
@template::METHOD_LIST)
@@ -829,15 +834,17 @@ module RDoc::Generator
end
def initialize(context, html_class, options)
+ # TODO: rethink the class hierarchy here...
@context = context
@html_class = html_class
@options = options
+ @@seq = @@seq.succ
+ @seq = @@seq
+
# HACK ugly
@template = options.template_class
- @@seq = @@seq.succ
- @seq = @@seq
@@all_methods << self
context.viewer = self
@@ -846,7 +853,7 @@ module RDoc::Generator
@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'
+ @img_url = RDoc::Markup::ToHtml.gen_relative_url path, 'source.png'
end
end
@@ -861,10 +868,32 @@ module RDoc::Generator
if @options.all_one_file
"#" + path
else
- RDoc::Generator.gen_url from_path, path
+ RDoc::Markup::ToHtml.gen_relative_url from_path, path
end
end
+ def formatter
+ @formatter ||= @options.formatter ||
+ RDoc::Markup::ToHtmlCrossref.new(path, self, @options.show_hash)
+ end
+
+ def inspect
+ alias_for = if @context.is_alias_for then
+ " (alias_for #{@context.is_alias_for})"
+ else
+ nil
+ end
+
+ "#<%s:0x%x %s%s%s (%s)%s>" % [
+ self.class, object_id,
+ @context.parent.name,
+ @context.singleton ? '::' : '#',
+ name,
+ @context.visibility,
+ alias_for
+ ]
+ end
+
def name
@context.name
end
@@ -961,7 +990,7 @@ module RDoc::Generator
template.write_html_on(f, values)
end
- RDoc::Generator.gen_url path, file_path
+ RDoc::Markup::ToHtml.gen_relative_url path, file_path
end
def <=>(other)
@@ -976,19 +1005,18 @@ module RDoc::Generator
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"
+ when RDoc::RubyToken::TkCONSTANT then "ruby-constant"
+ when RDoc::RubyToken::TkKW then "ruby-keyword kw"
+ when RDoc::RubyToken::TkIVAR then "ruby-ivar"
+ when RDoc::RubyToken::TkOp then "ruby-operator"
+ when RDoc::RubyToken::TkId then "ruby-identifier"
+ when RDoc::RubyToken::TkNode then "ruby-node"
+ when RDoc::RubyToken::TkCOMMENT then "ruby-comment cmt"
+ when RDoc::RubyToken::TkREGEXP then "ruby-regexp re"
+ when RDoc::RubyToken::TkSTRING then "ruby-value str"
+ when RDoc::RubyToken::TkVal then "ruby-value"
else
nil
end
diff --git a/lib/rdoc/generator/html.rb b/lib/rdoc/generator/html.rb
index b99af4d47b..a9e030a896 100644
--- a/lib/rdoc/generator/html.rb
+++ b/lib/rdoc/generator/html.rb
@@ -82,7 +82,7 @@ class RDoc::Generator::HTML
@classes = []
write_style_sheet
- gen_sub_directories()
+ gen_sub_directories
build_indices
generate_html
end
@@ -157,6 +157,7 @@ class RDoc::Generator::HTML
# the individual descriptions for files and classes
gen_into(@files)
gen_into(@classes)
+
# and the index files
gen_file_index
gen_class_index
@@ -168,14 +169,21 @@ class RDoc::Generator::HTML
end
def gen_into(list)
+ @file_list ||= index_to_links @files
+ @class_list ||= index_to_links @classes
+ @method_list ||= index_to_links RDoc::Generator::Method.all_methods
+
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) }
+ next unless item.document_self
+
+ op_file = item.path
+
+ FileUtils.mkdir_p File.dirname(op_file)
+
+ open op_file, 'w' do |io|
+ item.write_on io, @file_list, @class_list, @method_list
end
end
-
end
def gen_file_index
@@ -221,9 +229,23 @@ class RDoc::Generator::HTML
# line.
def gen_main_index
- template = RDoc::TemplatePage.new @template::INDEX
+ if @template.const_defined? :FRAMELESS then
+ main = @files.find do |file|
+ @main_page == file.name
+ end
+
+ if main.nil? then
+ main = @classes.find do |klass|
+ main_page == klass.context.full_name
+ end
+ end
+ else
+ main = RDoc::TemplatePage.new @template::INDEX
+ end
open 'index.html', 'w' do |f|
+ style_url = style_url '', @options.css
+
classes = @classes.sort.map { |klass| klass.value_hash }
values = {
@@ -237,18 +259,31 @@ class RDoc::Generator::HTML
values['inline_source'] = @options.inline_source
- template.write_html_on f, values
+ if main.respond_to? :write_on then
+ main.write_on f, @file_list, @class_list, @method_list, values
+ else
+ main.write_html_on f, values
+ end
end
end
+ def index_to_links(collection)
+ collection.sort.map do |f|
+ next unless f.document_self
+ { "href" => f.path, "name" => f.index_name }
+ end.compact
+ end
+
##
# Returns the url of the main page
def main_url
@main_page = @options.main_page
@main_page_ref = nil
- if @main_page
+
+ if @main_page then
@main_page_ref = RDoc::Generator::AllReferences[@main_page]
+
if @main_page_ref then
@main_page_path = @main_page_ref.path
else
@@ -351,15 +386,8 @@ class RDoc::Generator::HTMLInOne < RDoc::Generator::HTML
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,
+ "entries" => index_to_links(collection),
'list_title' => title,
'index_url' => main_url,
}
@@ -367,4 +395,3 @@ class RDoc::Generator::HTMLInOne < RDoc::Generator::HTML
end
-
diff --git a/lib/rdoc/generator/html/frameless.rb b/lib/rdoc/generator/html/frameless.rb
new file mode 100644
index 0000000000..2af890ce04
--- /dev/null
+++ b/lib/rdoc/generator/html/frameless.rb
@@ -0,0 +1,795 @@
+require 'rdoc/generator/html'
+require 'rdoc/generator/html/one_page_html'
+
+##
+# = CSS2 RDoc HTML template
+#
+# This is a template for RDoc that uses XHTML 1.0 Transitional and dictates a
+# bit more of the appearance of the output to cascading stylesheets than the
+# default. It was designed for clean inline code display, and uses DHTMl to
+# toggle the visbility of each method's source with each click on the '[source]'
+# link.
+#
+# == Authors
+#
+# * Michael Granger <ged@FaerieMUD.org>
+#
+# Copyright (c) 2002, 2003 The FaerieMUD Consortium. Some rights reserved.
+#
+# This work is licensed under the Creative Commons Attribution License. To view
+# a copy of this license, visit http://creativecommons.org/licenses/by/1.0/ or
+# send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California
+# 94305, USA.
+
+module RDoc::Generator::HTML::FRAMELESS
+
+ FRAMELESS = true
+
+ FONTS = "Verdana,Arial,Helvetica,sans-serif"
+
+ STYLE = <<-EOF
+body {
+ font-family: #{FONTS};
+ font-size: 90%;
+ margin: 0;
+ margin-left: 40px;
+ padding: 0;
+ background: white;
+}
+
+h1, h2, h3, h4 {
+ margin: 0;
+ color: #efefef;
+ background: transparent;
+}
+
+h1 {
+ font-size: 150%;
+}
+
+h2,h3,h4 {
+ margin-top: 1em;
+}
+
+:link, :visited {
+ background: #eef;
+ color: #039;
+ text-decoration: none;
+}
+
+:link:hover, :visited:hover {
+ background: #039;
+ color: #eef;
+}
+
+/* Override the base stylesheet's Anchor inside a table cell */
+td > :link, td > :visited {
+ background: transparent;
+ color: #039;
+ text-decoration: none;
+}
+
+/* and inside a section title */
+.section-title > :link, .section-title > :visited {
+ background: transparent;
+ color: #eee;
+ text-decoration: none;
+}
+
+/* === Structural elements =================================== */
+
+.index {
+ margin: 0;
+ margin-left: -40px;
+ padding: 0;
+ font-size: 90%;
+}
+
+.index :link, .index :visited {
+ margin-left: 0.7em;
+}
+
+.index .section-bar {
+ margin-left: 0px;
+ padding-left: 0.7em;
+ background: #ccc;
+ font-size: small;
+}
+
+#classHeader, #fileHeader {
+ width: auto;
+ color: white;
+ padding: 0.5em 1.5em 0.5em 1.5em;
+ margin: 0;
+ margin-left: -40px;
+ border-bottom: 3px solid #006;
+}
+
+#classHeader :link, #fileHeader :link,
+#classHeader :visited, #fileHeader :visited {
+ background: inherit;
+ color: white;
+}
+
+#classHeader td, #fileHeader td {
+ background: inherit;
+ color: white;
+}
+
+#fileHeader {
+ background: #057;
+}
+
+#classHeader {
+ background: #048;
+}
+
+.class-name-in-header {
+ font-size: 180%;
+ font-weight: bold;
+}
+
+#bodyContent {
+ padding: 0 1.5em 0 1.5em;
+}
+
+#description {
+ padding: 0.5em 1.5em;
+ background: #efefef;
+ border: 1px dotted #999;
+}
+
+#description h1, #description h2, #description h3,
+#description h4, #description h5, #description h6 {
+ color: #125;
+ background: transparent;
+}
+
+#copyright {
+ color: #333;
+ background: #efefef;
+ font: 0.75em sans-serif;
+ margin-top: 5em;
+ margin-bottom: 0;
+ padding: 0.5em 2em;
+}
+
+/* === Classes =================================== */
+
+table.header-table {
+ color: white;
+ font-size: small;
+}
+
+.type-note {
+ font-size: small;
+ color: #dedede;
+}
+
+.xxsection-bar {
+ background: #eee;
+ color: #333;
+ padding: 3px;
+}
+
+.section-bar {
+ color: #333;
+ border-bottom: 1px solid #999;
+ margin-left: -20px;
+}
+
+.section-title {
+ background: #79a;
+ color: #eee;
+ padding: 3px;
+ margin-top: 2em;
+ margin-left: -30px;
+ border: 1px solid #999;
+}
+
+.top-aligned-row {
+ vertical-align: top
+}
+
+.bottom-aligned-row {
+ vertical-align: bottom
+}
+
+/* --- Context section classes ----------------------- */
+
+.context-row { }
+
+.context-item-name {
+ font-family: monospace;
+ font-weight: bold;
+ color: black;
+}
+
+.context-item-value {
+ font-size: small;
+ color: #448;
+}
+
+.context-item-desc {
+ color: #333;
+ padding-left: 2em;
+}
+
+/* --- Method classes -------------------------- */
+
+.method-detail {
+ background: #efefef;
+ padding: 0;
+ margin-top: 0.5em;
+ margin-bottom: 1em;
+ border: 1px dotted #ccc;
+}
+
+.method-heading {
+ color: black;
+ background: #ccc;
+ border-bottom: 1px solid #666;
+ padding: 0.2em 0.5em 0 0.5em;
+}
+
+.method-signature {
+ color: black;
+ background: inherit;
+}
+
+.method-name {
+ font-weight: bold;
+}
+
+.method-args {
+ font-style: italic;
+}
+
+.method-description {
+ padding: 0 0.5em 0 0.5em;
+}
+
+/* --- Source code sections -------------------- */
+
+:link.source-toggle, :visited.source-toggle {
+ font-size: 90%;
+}
+
+div.method-source-code {
+ background: #262626;
+ color: #ffdead;
+ margin: 1em;
+ padding: 0.5em;
+ border: 1px dashed #999;
+ overflow: hidden;
+}
+
+div.method-source-code pre {
+ color: #ffdead;
+ overflow: hidden;
+}
+
+/* --- Ruby keyword styles --------------------- */
+
+.standalone-code {
+ background: #221111;
+ color: #ffdead;
+ overflow: hidden;
+}
+
+.ruby-constant {
+ color: #7fffd4;
+ background: transparent;
+}
+
+.ruby-keyword {
+ color: #00ffff;
+ background: transparent;
+}
+
+.ruby-ivar {
+ color: #eedd82;
+ background: transparent;
+}
+
+.ruby-operator {
+ color: #00ffee;
+ background: transparent;
+}
+
+.ruby-identifier {
+ color: #ffdead;
+ background: transparent;
+}
+
+.ruby-node {
+ color: #ffa07a;
+ background: transparent;
+}
+
+.ruby-comment {
+ color: #b22222;
+ font-weight: bold;
+ background: transparent;
+}
+
+.ruby-regexp {
+ color: #ffa07a;
+ background: transparent;
+}
+
+.ruby-value {
+ color: #7fffd4;
+ background: transparent;
+}
+
+EOF
+
+ ##
+ # Header template
+
+ XHTML_PREAMBLE = <<-EOF
+<?xml version="1.0" encoding="<%= values["charset"] %>"?>
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+ EOF
+
+ HEADER = XHTML_PREAMBLE + <<-EOF
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title><%= values["title"] %></title>
+ <meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
+ <meta http-equiv="Content-Script-Type" content="text/javascript" />
+ <link rel="stylesheet" href="<%= values["style_url"] %>" type="text/css" media="screen" />
+ <script type="text/javascript">
+ // <![CDATA[
+
+ function popupCode( url ) {
+ window.open(url, "Code", "resizable=yes,scrollbars=yes,toolbar=no,status=no,height=150,width=400")
+ }
+
+ function toggleCode( id ) {
+ if ( document.getElementById )
+ elem = document.getElementById( id );
+ else if ( document.all )
+ elem = eval( "document.all." + id );
+ else
+ return false;
+
+ elemStyle = elem.style;
+
+ if ( elemStyle.display != "block" ) {
+ elemStyle.display = "block"
+ } else {
+ elemStyle.display = "none"
+ }
+
+ return true;
+ }
+
+ // Make codeblocks hidden by default
+ document.writeln( "<style type=\\"text/css\\">div.method-source-code { display: none }</style>" )
+
+ // ]]>
+ </script>
+
+</head>
+<body>
+EOF
+
+ ##
+ # Context content template
+
+ CONTEXT_CONTENT = %{
+}
+
+ ##
+ # Footer template
+
+ FOOTER = <<-EOF
+ <div id="popupmenu" class="index">
+ <ul>
+ <li class="index-entries section-bar">Classes
+ <ul>
+<% values["class_list"].each do |klass| %>
+ <li><a href="<%= klass["href"] %>"><%= klass["name"] %></a>
+<% end %>
+ </ul>
+ </li>
+
+ <li class="index-entries section-bar">Methods
+ <ul>
+<% values["method_list"].each do |file| %>
+ <li><a href="<%= file["href"] %>"><%= file["name"] %></a>
+<% end %>
+ </ul>
+ </li>
+
+ <li class="index-entries section-bar">Files
+ <ul>
+<% values["file_list"].each do |file| %>
+ <li><a href="<%= file["href"] %>"><%= file["name"] %></a>
+<% end %>
+ </ul>
+ </li>
+ </ul>
+ </li>
+
+</body>
+</html>
+ EOF
+
+ ##
+ # File page header template
+
+ FILE_PAGE = <<-EOF
+ <div id="fileHeader">
+ <h1><%= values["short_name"] %></h1>
+
+ <table class="header-table">
+ <tr class="top-aligned-row">
+ <td><strong>Path:</strong></td>
+ <td><%= values["full_path"] %>
+<% if values["cvsurl"] then %>
+ &nbsp;(<a href="<%= values["cvsurl"] %>"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
+<% end %>
+ </td>
+ </tr>
+
+ <tr class="top-aligned-row">
+ <td><strong>Last Update:</strong></td>
+ <td><%= values["dtm_modified"] %></td>
+ </tr>
+ </table>
+ </div>
+ EOF
+
+ ##
+ # Class page header template
+
+ CLASS_PAGE = <<-EOF
+ <div id="classHeader">
+ <table class="header-table">
+ <tr class="top-aligned-row">
+ <td><strong><%= values["classmod"] %></strong></td>
+ <td class="class-name-in-header"><%= values["full_name"] %></td>
+ </tr>
+
+ <tr class="top-aligned-row">
+ <td><strong>In:</strong></td>
+ <td>
+<% values["infiles"].each do |infiles| %>
+<% if infiles["full_path_url"] then %>
+ <a href="<%= infiles["full_path_url"] %>">
+<% end %>
+ <%= infiles["full_path"] %>
+<% if infiles["full_path_url"] then %>
+ </a>
+<% end %>
+<% if infiles["cvsurl"] then %>
+ &nbsp;(<a href="<%= infiles["cvsurl"] %>"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
+<% end %>
+ <br />
+<% end %><%# values["infiles"] %>
+ </td>
+ </tr>
+
+<% if values["parent"] then %>
+ <tr class="top-aligned-row">
+ <td><strong>Parent:</strong></td>
+ <td>
+<% if values["par_url"] then %>
+ <a href="<%= values["par_url"] %>">
+<% end %>
+ <%= values["parent"] %>
+<% if values["par_url"] then %>
+ </a>
+<% end %>
+ </td>
+ </tr>
+<% end %>
+ </table>
+ </div>
+ EOF
+
+ ##
+ # Method list template
+
+ METHOD_LIST = <<-EOF
+
+ <div id="contextContent">
+<% if values["diagram"] then %>
+ <div id="diagram">
+ <%= values["diagram"] %>
+ </div>
+<% end %>
+
+<% if values["description"] then %>
+ <div id="description">
+ <%= values["description"] %>
+ </div>
+<% end %>
+
+<% if values["requires"] then %>
+ <div id="requires-list">
+ <h3 class="section-bar">Required files</h3>
+
+ <div class="name-list">
+<% values["requires"].each do |requires| %>
+ <%= href requires["aref"], requires["name"] %>&nbsp;&nbsp;
+<% end %><%# values["requires"] %>
+ </div>
+ </div>
+<% end %>
+
+<% if values["toc"] then %>
+ <div id="contents-list">
+ <h3 class="section-bar">Contents</h3>
+ <ul>
+<% values["toc"].each do |toc| %>
+ <li><a href="#<%= values["href"] %>"><%= values["secname"] %></a></li>
+<% end %><%# values["toc"] %>
+ </ul>
+<% end %>
+ </div>
+
+<% if values["methods"] then %>
+ <div id="method-list">
+ <h3 class="section-bar">Methods</h3>
+
+ <div class="name-list">
+<% values["methods"].each do |methods| %>
+ <%= href methods["aref"], methods["name"] %>&nbsp;&nbsp;
+<% end %><%# values["methods"] %>
+ </div>
+ </div>
+<% end %>
+
+ </div>
+
+
+ <!-- if includes -->
+<% if values["includes"] then %>
+ <div id="includes">
+ <h3 class="section-bar">Included Modules</h3>
+
+ <div id="includes-list">
+<% values["includes"].each do |includes| %>
+ <span class="include-name"><%= href includes["aref"], includes["name"] %></span>
+<% end %><%# values["includes"] %>
+ </div>
+ </div>
+<% end %>
+
+<% values["sections"].each do |sections| %>
+ <div id="section">
+<% if sections["sectitle"] then %>
+ <h2 class="section-title"><a name="<%= sections["secsequence"] %>"><%= sections["sectitle"] %></a></h2>
+<% if sections["seccomment"] then %>
+ <div class="section-comment">
+ <%= sections["seccomment"] %>
+ </div>
+<% end %>
+<% end %>
+
+<% if values["classlist"] then %>
+ <div id="class-list">
+ <h3 class="section-bar">Classes and Modules</h3>
+
+ <%= values["classlist"] %>
+ </div>
+<% end %>
+
+<% if values["constants"] then %>
+ <div id="constants-list">
+ <h3 class="section-bar">Constants</h3>
+
+ <div class="name-list">
+ <table summary="Constants">
+<% values["constants"].each do |constants| %>
+ <tr class="top-aligned-row context-row">
+ <td class="context-item-name"><%= constants["name"] %></td>
+ <td>=</td>
+ <td class="context-item-value"><%= constants["value"] %></td>
+<% if values["desc"] then %>
+ <td width="3em">&nbsp;</td>
+ <td class="context-item-desc"><%= constants["desc"] %></td>
+<% end %>
+ </tr>
+<% end %><%# values["constants"] %>
+ </table>
+ </div>
+ </div>
+<% end %>
+
+<% if values["aliases"] then %>
+ <div id="aliases-list">
+ <h3 class="section-bar">External Aliases</h3>
+
+ <div class="name-list">
+ <table summary="aliases">
+<% values["aliases"].each do |aliases| $stderr.puts({ :aliases => aliases }.inspect) %>
+ <tr class="top-aligned-row context-row">
+ <td class="context-item-name"><%= values["old_name"] %></td>
+ <td>-&gt;</td>
+ <td class="context-item-value"><%= values["new_name"] %></td>
+ </tr>
+<% if values["desc"] then %>
+ <tr class="top-aligned-row context-row">
+ <td>&nbsp;</td>
+ <td colspan="2" class="context-item-desc"><%= values["desc"] %></td>
+ </tr>
+<% end %>
+<% end %><%# values["aliases"] %>
+ </table>
+ </div>
+ </div>
+<% end %>
+
+
+<% if values["attributes"] then %>
+ <div id="attribute-list">
+ <h3 class="section-bar">Attributes</h3>
+
+ <div class="name-list">
+ <table>
+<% values["attributes"].each do |attributes| $stderr.puts({ :attributes => attributes }.inspect) %>
+ <tr class="top-aligned-row context-row">
+ <td class="context-item-name"><%= values["name"] %></td>
+<% if values["rw"] then %>
+ <td class="context-item-value">&nbsp;[<%= values["rw"] %>]&nbsp;</td>
+<% end %>
+<% unless values["rw"] then %>
+ <td class="context-item-value">&nbsp;&nbsp;</td>
+<% end %>
+ <td class="context-item-desc"><%= values["a_desc"] %></td>
+ </tr>
+<% end %><%# values["attributes"] %>
+ </table>
+ </div>
+ </div>
+<% end %>
+
+ <!-- if method_list -->
+<% if sections["method_list"] then %>
+ <div id="methods">
+<% sections["method_list"].each do |method_list| %>
+<% if method_list["methods"] then %>
+ <h3 class="section-bar"><%= method_list["type"] %> <%= method_list["category"] %> methods</h3>
+
+<% method_list["methods"].each do |methods| %>
+ <div id="method-<%= methods["aref"] %>" class="method-detail">
+ <a name="<%= methods["aref"] %>"></a>
+
+ <div class="method-heading">
+<% if methods["codeurl"] then %>
+ <a href="<%= methods["codeurl"] %>" target="Code" class="method-signature"
+ onclick="popupCode('<%= methods["codeurl"] %>');return false;">
+<% end %>
+<% if methods["sourcecode"] then %>
+ <a href="#<%= methods["aref"] %>" class="method-signature">
+<% end %>
+<% if methods["callseq"] then %>
+ <span class="method-name"><%= methods["callseq"] %></span>
+<% end %>
+<% unless methods["callseq"] then %>
+ <span class="method-name"><%= methods["name"] %></span><span class="method-args"><%= methods["params"] %></span>
+<% end %>
+<% if methods["codeurl"] then %>
+ </a>
+<% end %>
+<% if methods["sourcecode"] then %>
+ </a>
+<% end %>
+ </div>
+
+ <div class="method-description">
+<% if methods["m_desc"] then %>
+ <%= methods["m_desc"] %>
+<% end %>
+<% if methods["sourcecode"] then %>
+ <p><a class="source-toggle" href="#"
+ onclick="toggleCode('<%= methods["aref"] %>-source');return false;">[Source]</a></p>
+ <div class="method-source-code" id="<%= methods["aref"] %>-source">
+<pre>
+<%= methods["sourcecode"] %>
+</pre>
+ </div>
+<% end %>
+ </div>
+ </div>
+
+<% end %><%# method_list["methods"] %>
+<% end %>
+<% end %><%# sections["method_list"] %>
+
+ </div>
+<% end %>
+<% end %><%# values["sections"] %>
+ EOF
+
+ ##
+ # Body template
+
+ BODY = HEADER + %{
+
+<%= template_include %> <!-- banner header -->
+
+ <div id="bodyContent">
+
+} + METHOD_LIST + %{
+
+ </div>
+
+} + FOOTER
+
+ ##
+ # Source code template
+
+ SRC_PAGE = XHTML_PREAMBLE + <<-EOF
+<html>
+<head>
+ <title><%= values["title"] %></title>
+ <meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
+ <link rel="stylesheet" href="<%= values["style_url"] %>" type="text/css" media="screen" />
+</head>
+<body class="standalone-code">
+ <pre><%= values["code"] %></pre>
+</body>
+</html>
+ EOF
+
+ ##
+ # Index file templates
+
+ FR_INDEX_BODY = %{
+<%= template_include %>
+}
+
+ FILE_INDEX = XHTML_PREAMBLE + <<-EOF
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title><%= values["list_title"] %></title>
+ <meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
+ <link rel="stylesheet" href="<%= values["style_url"] %>" type="text/css" />
+ <base target="docwin" />
+</head>
+<body>
+<div class="index">
+ <h1 class="section-bar"><%= values["list_title"] %></h1>
+ <div class="index-entries">
+<% values["entries"].each do |entries| %>
+ <a href="<%= entries["href"] %>"><%= entries["name"] %></a><br />
+<% end %><%# values["entries"] %>
+ </div>
+</div>
+</body>
+</html>
+ EOF
+
+ CLASS_INDEX = FILE_INDEX
+ METHOD_INDEX = FILE_INDEX
+
+ INDEX = <<-EOF
+<?xml version="1.0" encoding="<%= values["charset"] %>"?>
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title><%= values["title"] %></title>
+ <meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
+</head>
+<frameset rows="20%, 80%">
+ <frameset cols="45%,55%">
+ <frame src="fr_class_index.html" name="Classes" />
+ <frame src="fr_method_index.html" name="Methods" />
+ </frameset>
+ <frame src="<%= values["initial_page"] %>" name="docwin" />
+</frameset>
+</html>
+ EOF
+
+end
+
diff --git a/lib/rdoc/generator/html/hefss.rb b/lib/rdoc/generator/html/hefss.rb
index 294d5b62f6..e186a40384 100644
--- a/lib/rdoc/generator/html/hefss.rb
+++ b/lib/rdoc/generator/html/hefss.rb
@@ -141,7 +141,7 @@ td { font-family: Verdana, Arial, Helvetica, sans-serif;
<div class="name-list">
<% values["requires"].each do |requires| %>
<%= href requires["aref"], requires["name"] %>
-<% end # values["requires"] %>
+<% end %><%# values["requires"] %>
<% end %>
</div>
@@ -156,10 +156,10 @@ td { font-family: Verdana, Arial, Helvetica, sans-serif;
<div class="name-list">
<% method_list["methods"].each do |methods| %>
<a href="<%= methods["codeurl"] %>" target="source"><%= methods["name"] %></a>
-<% end # values["methods"] %>
+<% end %><%# values["methods"] %>
</div>
<% end %>
-<% end # values["method_list"] %>
+<% end %><%# values["method_list"] %>
<% end %>
<% if sections["attributes"] then %>
@@ -178,10 +178,10 @@ td { font-family: Verdana, Arial, Helvetica, sans-serif;
<td class="attr-name"><%= attributes["name"] %></td>
<td><%= attributes["a_desc"] %></td>
</tr>
-<% end # values["attributes"] %>
+<% end %><%# values["attributes"] %>
</table>
<% end %>
-<% end # values["sections"] %>
+<% end %><%# values["sections"] %>
<% end %>
<% if values["classlist"] then %>
@@ -237,7 +237,7 @@ td { font-family: Verdana, Arial, Helvetica, sans-serif;
<% if infiles["cvsurl"] then %>
&nbsp;(<a href="<%= infiles["cvsurl"] %>"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
<% end %>
-<% end # values["infiles"] %>
+<% end %><%# values["infiles"] %>
</td>
</tr>
<% if values["parent"] then %>
@@ -266,7 +266,7 @@ td { font-family: Verdana, Arial, Helvetica, sans-serif;
<div class="name-list">
<% values["includes"].each do |includes| %>
<span class="method-name"><%= href includes["aref"], includes["name"] %></span>
-<% end # values["includes"] %>
+<% end %><%# values["includes"] %>
</div>
<% end %>
@@ -293,11 +293,11 @@ td { font-family: Verdana, Arial, Helvetica, sans-serif;
<%= method_list["m_desc"] %>
</div>
<% end %>
-<% end # method_list["methods"] %>
+<% end %><%# method_list["methods"] %>
<% end %>
-<% end # sections["method_list"] %>
+<% end %><%# sections["method_list"] %>
<% end %>
-<% end # values["sections"] %>
+<% end %><%# values["sections"] %>
<% end %>
EOF
@@ -365,7 +365,7 @@ div.banner {
<div class="banner"><%= values["list_title"] %></div>
<% values["entries"].each do |entries| %>
<a href="<%= entries["href"] %>"><%= entries["name"] %></a><br />
-<% end # values["entries"] %>
+<% end %><%# values["entries"] %>
</body></html>
EOF
diff --git a/lib/rdoc/generator/html/html.rb b/lib/rdoc/generator/html/html.rb
index 63c83ee5fd..1ab90c6264 100644
--- a/lib/rdoc/generator/html/html.rb
+++ b/lib/rdoc/generator/html/html.rb
@@ -7,8 +7,8 @@ require 'rdoc/generator/html/one_page_html'
# This is a template for RDoc that uses XHTML 1.0 Transitional and dictates a
# bit more of the appearance of the output to cascading stylesheets than the
# default. It was designed for clean inline code display, and uses DHTMl to
-# toggle the visibility of each method's source with each click on the '[source]'
-# link.
+# toggle the visibility of each method's source with each click on the
+# '[source]' link.
#
# == Authors
#
@@ -16,10 +16,10 @@ require 'rdoc/generator/html/one_page_html'
#
# Copyright (c) 2002, 2003 The FaerieMUD Consortium. Some rights reserved.
#
-# This work is licensed under the Creative Commons Attribution License. To view
-# a copy of this license, visit http://creativecommons.org/licenses/by/1.0/ or
-# send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California
-# 94305, USA.
+# This work is licensed under the Creative Commons Attribution License. To
+# view a copy of this license, visit
+# http://creativecommons.org/licenses/by/1.0/ or send a letter to Creative
+# Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
module RDoc::Generator::HTML::HTML
@@ -361,7 +361,7 @@ EOF
&nbsp;(<a href="<%= infiles["cvsurl"] %>"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
<% end %>
<br />
-<% end # values["infiles"] %>
+<% end %><%# values["infiles"] %>
</td>
</tr>
@@ -388,39 +388,38 @@ EOF
#####################################################################
METHOD_LIST = <<-EOF
-
<div id="contextContent">
<% if values["diagram"] then %>
<div id="diagram">
<%= values["diagram"] %>
</div>
-<% end %>
+<% end
-<% if values["description"] then %>
+ if values["description"] then %>
<div id="description">
<%= values["description"] %>
</div>
-<% end %>
+<% end
-<% if values["requires"] then %>
+ if values["requires"] then %>
<div id="requires-list">
<h3 class="section-bar">Required files</h3>
<div class="name-list">
<% values["requires"].each do |requires| %>
<%= href requires["aref"], requires["name"] %>&nbsp;&nbsp;
-<% end # values["requires"] %>
+<% end %><%# values["requires"] %>
</div>
</div>
-<% end %>
+<% end
-<% if values["toc"] then %>
+ if values["toc"] then %>
<div id="contents-list">
<h3 class="section-bar">Contents</h3>
<ul>
<% values["toc"].each do |toc| %>
- <li><a href="#<%= values["href"] %>"><%= values["secname"] %></a></li>
-<% end # values["toc"] %>
+ <li><a href="#<%= toc["href"] %>"><%= toc["secname"] %></a></li>
+<% end %><%# values["toc"] %>
</ul>
<% end %>
</div>
@@ -430,16 +429,14 @@ EOF
<h3 class="section-bar">Methods</h3>
<div class="name-list">
-<% values["methods"].each do |methods| %>
+<% values["methods"].each do |methods| %>
<%= href methods["aref"], methods["name"] %>&nbsp;&nbsp;
-<% end # values["methods"] %>
+<% end %><%# values["methods"] %>
</div>
</div>
<% end %>
-
</div>
-
<!-- if includes -->
<% if values["includes"] then %>
<div id="includes">
@@ -448,140 +445,137 @@ EOF
<div id="includes-list">
<% values["includes"].each do |includes| %>
<span class="include-name"><%= href includes["aref"], includes["name"] %></span>
-<% end # values["includes"] %>
+<% end %><%# values["includes"] %>
</div>
</div>
-<% end %>
+<% end
-<% values["sections"].each do |sections| %>
+ values["sections"].each do |sections| %>
<div id="section">
-<% if sections["sectitle"] then %>
+<% if sections["sectitle"] then %>
<h2 class="section-title"><a name="<%= sections["secsequence"] %>"><%= sections["sectitle"] %></a></h2>
-<% if sections["seccomment"] then %>
+<% if sections["seccomment"] then %>
<div class="section-comment">
<%= sections["seccomment"] %>
</div>
-<% end %>
-<% end %>
+<% end
+ end
-<% if values["classlist"] then %>
+ if sections["classlist"] then %>
<div id="class-list">
<h3 class="section-bar">Classes and Modules</h3>
- <%= values["classlist"] %>
+ <%= sections["classlist"] %>
</div>
-<% end %>
+<% end
-<% if values["constants"] then %>
+ if sections["constants"] then %>
<div id="constants-list">
<h3 class="section-bar">Constants</h3>
<div class="name-list">
<table summary="Constants">
-<% values["constants"].each do |constants| %>
+<% sections["constants"].each do |constants| %>
<tr class="top-aligned-row context-row">
<td class="context-item-name"><%= constants["name"] %></td>
<td>=</td>
<td class="context-item-value"><%= constants["value"] %></td>
-<% if values["desc"] then %>
+<% if sections["desc"] then %>
<td width="3em">&nbsp;</td>
<td class="context-item-desc"><%= constants["desc"] %></td>
-<% end %>
+<% end %>
</tr>
-<% end # values["constants"] %>
+<% end %><%# sections["constants"] %>
</table>
</div>
</div>
-<% end %>
+<% end
-<% if values["aliases"] then %>
+ if sections["aliases"] then %>
<div id="aliases-list">
<h3 class="section-bar">External Aliases</h3>
<div class="name-list">
- <table summary="aliases">
-<% values["aliases"].each do |aliases| $stderr.puts({ :aliases => aliases }.inspect) %>
+ <table summary="aliases">
+<% sections["aliases"].each do |aliases| %>
<tr class="top-aligned-row context-row">
- <td class="context-item-name"><%= values["old_name"] %></td>
+ <td class="context-item-name"><%= aliases["old_name"] %></td>
<td>-&gt;</td>
- <td class="context-item-value"><%= values["new_name"] %></td>
+ <td class="context-item-value"><%= aliases["new_name"] %></td>
</tr>
-<% if values["desc"] then %>
+<% if aliases["desc"] then %>
<tr class="top-aligned-row context-row">
<td>&nbsp;</td>
- <td colspan="2" class="context-item-desc"><%= values["desc"] %></td>
+ <td colspan="2" class="context-item-desc"><%= aliases["desc"] %></td>
</tr>
-<% end %>
-<% end # values["aliases"] %>
+<% end
+ end %><%# sections["aliases"] %>
</table>
</div>
</div>
-<% end %>
-
+<% end %>
-<% if values["attributes"] then %>
+<% if sections["attributes"] then %>
<div id="attribute-list">
<h3 class="section-bar">Attributes</h3>
<div class="name-list">
<table>
-<% values["attributes"].each do |attributes| $stderr.puts({ :attributes => attributes }.inspect) %>
+<% sections["attributes"].each do |attribute| %>
<tr class="top-aligned-row context-row">
- <td class="context-item-name"><%= values["name"] %></td>
-<% if values["rw"] then %>
- <td class="context-item-value">&nbsp;[<%= values["rw"] %>]&nbsp;</td>
-<% end %>
-<% unless values["rw"] then %>
+ <td class="context-item-name"><%= attribute["name"] %></td>
+<% if attribute["rw"] then %>
+ <td class="context-item-value">&nbsp;[<%= attribute["rw"] %>]&nbsp;</td>
+<% end
+ unless attribute["rw"] then %>
<td class="context-item-value">&nbsp;&nbsp;</td>
-<% end %>
- <td class="context-item-desc"><%= values["a_desc"] %></td>
+<% end %>
+ <td class="context-item-desc"><%= attribute["a_desc"] %></td>
</tr>
-<% end # values["attributes"] %>
+<% end %><%# sections["attributes"] %>
</table>
</div>
</div>
-<% end %>
-
-
+<% end %>
<!-- if method_list -->
-<% if sections["method_list"] then %>
+<% if sections["method_list"] then %>
<div id="methods">
-<% sections["method_list"].each do |method_list| %>
-<% if method_list["methods"] then %>
+<% sections["method_list"].each do |method_list|
+ if method_list["methods"] then %>
<h3 class="section-bar"><%= method_list["type"] %> <%= method_list["category"] %> methods</h3>
-<% method_list["methods"].each do |methods| %>
+<% method_list["methods"].each do |methods| %>
<div id="method-<%= methods["aref"] %>" class="method-detail">
<a name="<%= methods["aref"] %>"></a>
<div class="method-heading">
-<% if methods["codeurl"] then %>
+<% if methods["codeurl"] then %>
<a href="<%= methods["codeurl"] %>" target="Code" class="method-signature"
onclick="popupCode('<%= methods["codeurl"] %>');return false;">
-<% end %>
-<% if methods["sourcecode"] then %>
+<% end
+ if methods["sourcecode"] then %>
<a href="#<%= methods["aref"] %>" class="method-signature">
-<% end %>
-<% if methods["callseq"] then %>
+<% end
+ if methods["callseq"] then %>
<span class="method-name"><%= methods["callseq"] %></span>
-<% end %>
-<% unless methods["callseq"] then %>
+<% end
+ unless methods["callseq"] then %>
<span class="method-name"><%= methods["name"] %></span><span class="method-args"><%= methods["params"] %></span>
-<% end %>
-<% if methods["codeurl"] then %>
+<% end
+ if methods["codeurl"] then %>
</a>
-<% end %>
-<% if methods["sourcecode"] then %>
+<% end
+ if methods["sourcecode"] then %>
</a>
-<% end %>
+<% end %>
</div>
<div class="method-description">
-<% if methods["m_desc"] then %>
+<% if methods["m_desc"] then %>
<%= methods["m_desc"] %>
-<% end %>
-<% if methods["sourcecode"] then %>
+<% end
+ if methods["sourcecode"] then %>
<p><a class="source-toggle" href="#"
onclick="toggleCode('<%= methods["aref"] %>-source');return false;">[Source]</a></p>
<div class="method-source-code" id="<%= methods["aref"] %>-source">
@@ -589,17 +583,17 @@ EOF
<%= methods["sourcecode"] %>
</pre>
</div>
-<% end %>
+<% end %>
</div>
</div>
-<% end # method_list["methods"] %>
-<% end %>
-<% end # sections["method_list"] %>
+<% end %><%# method_list["methods"] %><%
+ end
+ end %><%# sections["method_list"] %>
</div>
-<% end %>
-<% end # values["sections"] %>
+<% end %>
+<% end %><%# values["sections"] %>
EOF
#####################################################################
@@ -663,7 +657,7 @@ EOF
<div id="index-entries">
<% values["entries"].each do |entries| %>
<a href="<%= entries["href"] %>"><%= entries["name"] %></a><br />
-<% end # values["entries"] %>
+<% end %><%# values["entries"] %>
</div>
</div>
</body>
diff --git a/lib/rdoc/generator/html/kilmer.rb b/lib/rdoc/generator/html/kilmer.rb
index b6c9c4e339..6479abaf8b 100644
--- a/lib/rdoc/generator/html/kilmer.rb
+++ b/lib/rdoc/generator/html/kilmer.rb
@@ -119,7 +119,7 @@ body,td,p { font-family: <%= values["fonts"] %>;
<div class="name-list">
<% values["requires"].each do |requires| %>
<%= href requires["aref"], requires["name"] %>
-<% end # values["requires"] %>
+<% end %><%# values["requires"] %>
<% end %>
</div>
@@ -130,7 +130,7 @@ body,td,p { font-family: <%= values["fonts"] %>;
<div class="name-list">
<% values["methods"].each do |methods| %>
<%= href methods["aref"], methods["name"] %>,
-<% end # values["methods"] %>
+<% end %><%# values["methods"] %>
</div>
<% end %>
@@ -162,7 +162,7 @@ body,td,p { font-family: <%= values["fonts"] %>;
<td class="attr-name"><%= attributes["name"] %></td>
<td><%= attributes["a_desc"] %></td>
</tr>
-<% end # sections["attributes"] %>
+<% end %><%# sections["attributes"] %>
</table>
<% end %>
@@ -175,7 +175,7 @@ body,td,p { font-family: <%= values["fonts"] %>;
<%= template_include %> <!-- method descriptions -->
-<% end # values["sections"] %>
+<% end %><%# values["sections"] %>
</body>
</html>
@@ -221,7 +221,7 @@ body,td,p { font-family: <%= values["fonts"] %>;
<% if infiles["cvsurl"] then %>
&nbsp;(<a href="<%= infiles["cvsurl"] %>"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
<% end %>
-<% end # values["infiles"] %>
+<% end %><%# values["infiles"] %>
</td>
</tr>
<% if values["parent"] then %>
@@ -250,7 +250,7 @@ body,td,p { font-family: <%= values["fonts"] %>;
<div class="name-list">
<% values["includes"].each do |includes| %>
<span class="method-name"><%= href includes["aref"], includes["name"] %></span>
-<% end # values["includes"] %>
+<% end %><%# values["includes"] %>
</div>
<% end %>
@@ -285,7 +285,7 @@ body,td,p { font-family: <%= values["fonts"] %>;
This method is also aliased as
<% values["aka"].each do |aka| $stderr.puts({ :aka => aka }.inspect) %>
<a href="<%= values["aref"] %>"><%= values["name"] %></a>
-<% end # values["aka"] %>
+<% end %><%# values["aka"] %>
</div>
<% end %>
<% if values["sourcecode"] then %>
@@ -293,9 +293,9 @@ This method is also aliased as
<%= values["sourcecode"] %>
</pre>
<% end %>
-<% end # values["methods"] %>
+<% end %><%# values["methods"] %>
<% end %>
-<% end # values["method_list"] %>
+<% end %><%# values["method_list"] %>
<% end %>
EOF
@@ -364,7 +364,7 @@ div.banner {
<div class="banner"><%= values["list_title"] %></div>
<% values["entries"].each do |entries| %>
<a href="<%= entries["href"] %>"><%= entries["name"] %></a><br />
-<% end # values["entries"] %>
+<% end %><%# values["entries"] %>
</body></html>
EOF
diff --git a/lib/rdoc/generator/html/one_page_html.rb b/lib/rdoc/generator/html/one_page_html.rb
index 885d0dcf6b..c4dd95529d 100644
--- a/lib/rdoc/generator/html/one_page_html.rb
+++ b/lib/rdoc/generator/html/one_page_html.rb
@@ -17,7 +17,7 @@ module RDoc::Generator::HTML::ONE_PAGE_HTML
<% unless requires["aref"] then %>
<li><%= requires["name"] %></li>
<% end %>
-<% end # files["requires"] %>
+<% end %><%# files["requires"] %>
</ul>
<% end %>
@@ -31,7 +31,7 @@ module RDoc::Generator::HTML::ONE_PAGE_HTML
<% unless includes["aref"] then %>
<li><%= includes["name"] %></li>
<% end %>
-<% end # classes["includes"] %>
+<% end %><%# classes["includes"] %>
</ul>
<% end %>
@@ -42,7 +42,7 @@ module RDoc::Generator::HTML::ONE_PAGE_HTML
<table>
<% sections["attributes"].each do |attributes| %>
<tr><td><%= attributes["name"] %></td><td><%= attributes["rw"] %></td><td><%= attributes["a_desc"] %></td></tr>
-<% end # sections["attributes"] %>
+<% end %><%# sections["attributes"] %>
</table>
<% end %>
@@ -68,11 +68,11 @@ module RDoc::Generator::HTML::ONE_PAGE_HTML
<%= methods["sourcecode"] %>
</pre></blockquote>
<% end %>
-<% end # method_list["methods"] %>
+<% end %><%# method_list["methods"] %>
<% end %>
-<% end # sections["method_list"] %>
+<% end %><%# sections["method_list"] %>
<% end %>
-<% end # classes["sections"] %>
+<% end %><%# classes["sections"] %>
<% end %>
EOF
@@ -91,7 +91,7 @@ module RDoc::Generator::HTML::ONE_PAGE_HTML
<tr><td>Modified:</td><td><%= files["dtm_modified"] %></td></tr>
</table>
} + CONTENTS_XML + %{
-<% end # values["files"] %>
+<% end %><%# values["files"] %>
<% if values["classes"] then %>
<h2>Classes</h2>
@@ -107,11 +107,11 @@ module RDoc::Generator::HTML::ONE_PAGE_HTML
(in files
<% classes["infiles"].each do |infiles| %>
<%= href infiles["full_path_url"], infiles["full_path"] %>
-<% end # classes["infiles"] %>
+<% end %><%# classes["infiles"] %>
)
<% end %>
} + CONTENTS_XML + %{
-<% end # values["classes"] %>
+<% end %><%# values["classes"] %>
<% end %>
</body>
</html>
diff --git a/lib/rdoc/generator/ri.rb b/lib/rdoc/generator/ri.rb
index ea0bbeffcc..d347505e0b 100644
--- a/lib/rdoc/generator/ri.rb
+++ b/lib/rdoc/generator/ri.rb
@@ -45,7 +45,7 @@ class RDoc::Generator::RI
def process_class(from_class)
generate_class_info(from_class)
- # now recurse into this classes constituent classes
+ # now recurse into this class' constituent classes
from_class.each_classmodule do |mod|
process_class(mod)
end
diff --git a/lib/rdoc/generator/texinfo.rb b/lib/rdoc/generator/texinfo.rb
new file mode 100644
index 0000000000..0b79820228
--- /dev/null
+++ b/lib/rdoc/generator/texinfo.rb
@@ -0,0 +1,84 @@
+require 'rdoc/rdoc'
+require 'rdoc/generator'
+require 'rdoc/markup/to_texinfo'
+
+module RDoc
+ RDoc::GENERATORS['texinfo'] = RDoc::Generator.new("rdoc/generator/texinfo",
+ :Texinfo,
+ 'texinfo')
+ module Generator
+ # This generates Texinfo files for viewing with GNU Info or Emacs
+ # from RDoc extracted from Ruby source files.
+ class Texinfo
+ # What should the .info file be named by default?
+ DEFAULT_INFO_FILENAME = 'rdoc.info'
+
+ include Generator::MarkUp
+
+ # Accept some options
+ def initialize(options)
+ @options = options
+ @options.inline_source = true
+ @options.op_name ||= 'rdoc.texinfo'
+ @options.formatter = ::RDoc::Markup::ToTexInfo.new
+ end
+
+ # Generate the +texinfo+ files
+ def generate(toplevels)
+ @toplevels = toplevels
+ @files, @classes = ::RDoc::Generator::Context.build_indicies(@toplevels,
+ @options)
+
+ (@files + @classes).each { |x| x.value_hash }
+
+ open(@options.op_name, 'w') do |f|
+ f.puts TexinfoTemplate.new('files' => @files,
+ 'classes' => @classes,
+ 'filename' => @options.op_name.gsub(/texinfo/, 'info'),
+ 'title' => @options.title).render
+ end
+ # TODO: create info files and install?
+ end
+
+ class << self
+ # Factory? We don't need no stinkin' factory!
+ alias_method :for, :new
+ end
+ end
+
+ # Basically just a wrapper around ERB.
+ # Should probably use RDoc::TemplatePage instead
+ class TexinfoTemplate
+ BASE_DIR = ::File.expand_path(::File.dirname(__FILE__)) # have to calculate this when the file's loaded.
+
+ def initialize(values, file = 'texinfo.erb')
+ @v, @file = [values, file]
+ end
+
+ def template
+ ::File.read(::File.join(BASE_DIR, 'texinfo', @file))
+ end
+
+ # Go!
+ def render
+ ERB.new(template).result binding
+ end
+
+ def href(location, text)
+ text # TODO: how does texinfo do hyperlinks?
+ end
+
+ def target(name, text)
+ text # TODO: how do hyperlink targets work?
+ end
+
+ # TODO: this is probably implemented elsewhere?
+ def method_prefix(section)
+ { 'Class' => '.',
+ 'Module' => '::',
+ 'Instance' => '#',
+ }[section['category']]
+ end
+ end
+ end
+end
diff --git a/lib/rdoc/generator/texinfo/class.texinfo.erb b/lib/rdoc/generator/texinfo/class.texinfo.erb
new file mode 100644
index 0000000000..07f17eaef2
--- /dev/null
+++ b/lib/rdoc/generator/texinfo/class.texinfo.erb
@@ -0,0 +1,44 @@
+@node <%= @v['class']['full_name'].gsub(/::/, '-') %>
+@chapter <%= @v['class']["classmod"] %> <%= @v['class']['full_name'] %>
+
+<% if @v['class']["parent"] and @v['class']['par_url'] %>
+Inherits <%= href @v['class']["par_url"], @v['class']["parent"] %><% end %>
+
+<%= @v['class']["description"] %>
+
+<% if @v['class']["includes"] %>
+Includes
+<% @v['class']["includes"].each do |include| %>
+* <%= href include["aref"], include["name"] %>
+<% end # @v['class']["includes"] %>
+<% end %>
+
+<% if @v['class']["sections"] %>
+<% @v['class']["sections"].each do |section| %>
+<% if section["attributes"] %>
+Attributes
+<% section["attributes"].each do |attributes| %>
+* <%= attributes["name"] %> <%= attributes["rw"] %> <%= attributes["a_desc"] %>
+<% end # section["attributes"] %>
+<% end %>
+<% end %>
+
+<% @v['class']["sections"].each do |section| %>
+<% if section["method_list"] %>
+Methods
+@menu
+<% section["method_list"].each_with_index do |method_list, i| %>
+<%= i %>
+<% (method_list["methods"] || []).each do |method| %>
+* <%= @v['class']['full_name'].gsub(/::/, '-') %><%= method_prefix method_list %><%= method['name'] %>::<% end %>
+<% end %>
+@end menu
+
+<% section["method_list"].each do |method_list| %>
+<% (method_list["methods"] || []).uniq.each do |method| %>
+<%= TexinfoTemplate.new(@v.merge({'method' => method, 'list' => method_list}),
+ 'method.texinfo.erb').render %><% end %>
+<% end # section["method_list"] %>
+<% end %>
+<% end # @v['class']["sections"] %>
+<% end %>
diff --git a/lib/rdoc/generator/texinfo/file.texinfo.erb b/lib/rdoc/generator/texinfo/file.texinfo.erb
new file mode 100644
index 0000000000..b619b94bd2
--- /dev/null
+++ b/lib/rdoc/generator/texinfo/file.texinfo.erb
@@ -0,0 +1,6 @@
+<% if false %>
+<h2>File: <%= @v['file']["short_name"] %></h2>
+Path: <%= @v['file']["full_path"] %>
+
+<%= TexinfoTemplate.new(@v, 'content.texinfo.erb').render %>
+<% end %>
diff --git a/lib/rdoc/generator/texinfo/method.texinfo.erb b/lib/rdoc/generator/texinfo/method.texinfo.erb
new file mode 100644
index 0000000000..f5c2b73a4b
--- /dev/null
+++ b/lib/rdoc/generator/texinfo/method.texinfo.erb
@@ -0,0 +1,6 @@
+@node <%= @v['class']['full_name'].gsub(/::/, '-') %><%= method_prefix @v['list'] %><%= @v['method']['name'] %>
+@section <%= @v['class']["classmod"] %> <%= @v['class']['full_name'] %><%= method_prefix @v['list'] %><%= @v['method']['name'] %>
+<%= @v['method']["type"] %> <%= @v['method']["category"] %> method:
+<%= target @v['method']["aref"], @v['method']['callseq'] ||
+ @v['method']["name"] + @v['method']["params"] %>
+<%= @v['method']["m_desc"] %>
diff --git a/lib/rdoc/generator/texinfo/texinfo.erb b/lib/rdoc/generator/texinfo/texinfo.erb
new file mode 100644
index 0000000000..235f63d73c
--- /dev/null
+++ b/lib/rdoc/generator/texinfo/texinfo.erb
@@ -0,0 +1,28 @@
+\input texinfo @c -*-texinfo-*-
+@c %**start of header
+@setfilename <%= @v['filename'] %>
+@settitle <%= @v['title'] %>
+@c %**end of header
+
+@contents @c TODO: whitespace is a mess... =\
+
+@ifnottex
+@node Top
+
+@top <%= @v['title'] %>
+@end ifnottex
+
+<% if @f = @v['files'].detect { |f| f.name =~ /Readme/i } %>
+<%= @f.values['description'] %><% end %>
+
+@menu
+<% @v['classes'].each do |klass| %>
+* <%= klass.name.gsub(/::/, '-') %>::<% end %>
+@c TODO: add files
+@end menu
+
+<% (@v['classes'] || []).each_with_index do |klass, i| %>
+<%= TexinfoTemplate.new(@v.merge('class' => klass.values),
+ 'class.texinfo.erb').render %><% end %>
+
+@bye
diff --git a/lib/rdoc/known_classes.rb b/lib/rdoc/known_classes.rb
new file mode 100644
index 0000000000..4c52f58ad2
--- /dev/null
+++ b/lib/rdoc/known_classes.rb
@@ -0,0 +1,69 @@
+module RDoc
+
+ ##
+ # Ruby's built-in classes, modules and exceptions
+
+ KNOWN_CLASSES = {
+ "rb_cArray" => "Array",
+ "rb_cBignum" => "Bignum",
+ "rb_cClass" => "Class",
+ "rb_cData" => "Data",
+ "rb_cDir" => "Dir",
+ "rb_cFalseClass" => "FalseClass",
+ "rb_cFile" => "File",
+ "rb_cFixnum" => "Fixnum",
+ "rb_cFloat" => "Float",
+ "rb_cHash" => "Hash",
+ "rb_cIO" => "IO",
+ "rb_cInteger" => "Integer",
+ "rb_cModule" => "Module",
+ "rb_cNilClass" => "NilClass",
+ "rb_cNumeric" => "Numeric",
+ "rb_cObject" => "Object",
+ "rb_cProc" => "Proc",
+ "rb_cRange" => "Range",
+ "rb_cRegexp" => "Regexp",
+ "rb_cRubyVM" => "RubyVM",
+ "rb_cString" => "String",
+ "rb_cStruct" => "Struct",
+ "rb_cSymbol" => "Symbol",
+ "rb_cThread" => "Thread",
+ "rb_cTime" => "Time",
+ "rb_cTrueClass" => "TrueClass",
+
+ "rb_eArgError" => "ArgError",
+ "rb_eEOFError" => "EOFError",
+ "rb_eException" => "Exception",
+ "rb_eFatal" => "Fatal",
+ "rb_eFloatDomainError" => "FloatDomainError",
+ "rb_eIOError" => "IOError",
+ "rb_eIndexError" => "IndexError",
+ "rb_eInterrupt" => "Interrupt",
+ "rb_eLoadError" => "LoadError",
+ "rb_eNameError" => "NameError",
+ "rb_eNoMemError" => "NoMemError",
+ "rb_eNotImpError" => "NotImpError",
+ "rb_eRangeError" => "RangeError",
+ "rb_eRuntimeError" => "RuntimeError",
+ "rb_eScriptError" => "ScriptError",
+ "rb_eSecurityError" => "SecurityError",
+ "rb_eSignal" => "Signal",
+ "rb_eStandardError" => "StandardError",
+ "rb_eSyntaxError" => "SyntaxError",
+ "rb_eSystemCallError" => "SystemCallError",
+ "rb_eSystemExit" => "SystemExit",
+ "rb_eTypeError" => "TypeError",
+ "rb_eZeroDivError" => "ZeroDivError",
+
+ "rb_mComparable" => "Comparable",
+ "rb_mEnumerable" => "Enumerable",
+ "rb_mErrno" => "Errno",
+ "rb_mFileTest" => "FileTest",
+ "rb_mGC" => "GC",
+ "rb_mKernel" => "Kernel",
+ "rb_mMath" => "Math",
+ "rb_mPrecision" => "Precision",
+ "rb_mProcess" => "Process"
+ }
+
+end
diff --git a/lib/rdoc/markup/attribute_manager.rb b/lib/rdoc/markup/attribute_manager.rb
index 72f70dadd7..ae8b77f22e 100644
--- a/lib/rdoc/markup/attribute_manager.rb
+++ b/lib/rdoc/markup/attribute_manager.rb
@@ -144,8 +144,6 @@ class RDoc::Markup::AttributeManager
add_html("b", :BOLD)
add_html("tt", :TT)
add_html("code", :TT)
-
- add_special(/<!--(.*?)-->/, :COMMENT)
end
def add_word_pair(start, stop, name)
diff --git a/lib/rdoc/markup/fragments.rb b/lib/rdoc/markup/fragments.rb
index 99780de169..b7f9b605c8 100644
--- a/lib/rdoc/markup/fragments.rb
+++ b/lib/rdoc/markup/fragments.rb
@@ -11,8 +11,8 @@ class RDoc::Markup
attr_reader :level, :param, :txt
attr_accessor :type
- ######
- # This is a simple factory system that lets us associate fragment
+ ##
+ # This is a simple factory system that lets us associate fragement
# types (a string) with a subclass of fragment
TYPE_MAP = {}
diff --git a/lib/rdoc/markup/preprocess.rb b/lib/rdoc/markup/preprocess.rb
index 760351d386..00dd4be4ad 100644
--- a/lib/rdoc/markup/preprocess.rb
+++ b/lib/rdoc/markup/preprocess.rb
@@ -14,21 +14,25 @@ class RDoc::Markup::PreProcess
##
# Look for common options in a chunk of text. Options that we don't handle
- # are passed back to our caller as |directive, param|
+ # are yielded to the caller.
def handle(text)
- text.gsub!(/^([ \t#]*):(\w+):\s*(.+)?\n/) do
+ text.gsub!(/^([ \t]*#?[ \t]*):(\w+):([ \t]*)(.+)?\n/) do
+ next $& if $3.empty? and $4 and $4[0, 1] == ':'
+
prefix = $1
directive = $2.downcase
- param = $3
+ param = $4
case directive
- when "include"
+ when 'include' then
filename = param.split[0]
- include_file(filename, prefix)
+ include_file filename, prefix
else
- yield(directive, param)
+ result = yield directive, param
+ result = "#{prefix}:#{directive}: #{param}\n" unless result
+ result
end
end
end
diff --git a/lib/rdoc/markup/to_html.rb b/lib/rdoc/markup/to_html.rb
index 3c08d7bf6a..ca29373db1 100644
--- a/lib/rdoc/markup/to_html.rb
+++ b/lib/rdoc/markup/to_html.rb
@@ -1,7 +1,6 @@
require 'rdoc/markup/formatter'
require 'rdoc/markup/fragments'
require 'rdoc/markup/inline'
-require 'rdoc/generator'
require 'cgi'
@@ -21,6 +20,11 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
def initialize
super
+ # @in_tt - tt nested levels count
+ # @tt_bit - cache
+ @in_tt = 0
+ @tt_bit = RDoc::Markup::Attribute.bitmap_for :TT
+
# external hyperlinks
@markup.add_special(/((link:|https?:|mailto:|ftp:|www\.)\S+\w)/, :HYPERLINK)
@@ -31,6 +35,27 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
end
##
+ # Converts a target url to one that is relative to a given path
+
+ def self.gen_relative_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
+
+ ##
# Generate a hyperlink for url, labeled with text. Handle the
# special cases for img: and link: described under handle_special_HYPEDLINK
@@ -48,7 +73,7 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
url = if path[0, 1] == '#' then # is this meaningful?
path
else
- RDoc::Generator.gen_url @from_path, path
+ self.class.gen_relative_url @from_path, path
end
end
@@ -88,6 +113,20 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
end
##
+ # are we currently inside <tt> tags?
+
+ def in_tt?
+ @in_tt > 0
+ end
+
+ ##
+ # is +tag+ a <tt> tag?
+
+ def tt?(tag)
+ tag.bit == @tt_bit
+ end
+
+ ##
# Set up the standard mapping of attributes to HTML tags
def init_tags
@@ -216,6 +255,7 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
@attr_tags.each do |tag|
if attr_mask & tag.bit != 0
res << annotate(tag.on)
+ @in_tt += 1 if tt?(tag)
end
end
end
@@ -226,6 +266,7 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
@attr_tags.reverse_each do |tag|
if attr_mask & tag.bit != 0
+ @in_tt -= 1 if tt?(tag)
res << annotate(tag.off)
end
end
@@ -251,27 +292,33 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
res
end
+ def convert_string(item)
+ in_tt? ? convert_string_simple(item) : convert_string_fancy(item)
+ end
+
+ def convert_string_simple(item)
+ CGI.escapeHTML item
+ end
+
##
# some of these patterns are taken from SmartyPants...
- def convert_string(item)
- CGI.escapeHTML(item).
-
+ def convert_string_fancy(item)
# convert -- to em-dash, (-- to en-dash)
- gsub(/---?/, '&#8212;'). #gsub(/--/, '&#8211;').
+ item.gsub(/---?/, '&#8212;'). #gsub(/--/, '&#8211;').
# convert ... to elipsis (and make sure .... becomes .<elipsis>)
gsub(/\.\.\.\./, '.&#8230;').gsub(/\.\.\./, '&#8230;').
# convert single closing quote
- gsub(%r{([^ \t\r\n\[\{\(])\'}, '\1&#8217;').
+ gsub(%r{([^ \t\r\n\[\{\(])\'}, '\1&#8217;'). # }
gsub(%r{\'(?=\W|s\b)}, '&#8217;').
# convert single opening quote
gsub(/'/, '&#8216;').
# convert double closing quote
- gsub(%r{([^ \t\r\n\[\{\(])\'(?=\W)}, '\1&#8221;').
+ gsub(%r{([^ \t\r\n\[\{\(])\'(?=\W)}, '\1&#8221;'). # }
# convert double opening quote
gsub(/'/, '&#8220;').
@@ -281,7 +328,6 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
# convert and registered trademark
gsub(/\(r\)/, '&#174;')
-
end
def convert_special(special)
diff --git a/lib/rdoc/markup/to_html_crossref.rb b/lib/rdoc/markup/to_html_crossref.rb
index 32c922e70b..a6f29c5c2c 100644
--- a/lib/rdoc/markup/to_html_crossref.rb
+++ b/lib/rdoc/markup/to_html_crossref.rb
@@ -14,6 +14,7 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
# correct relative paths for any hyperlinks that we find
def initialize(from_path, context, show_hash)
+ raise ArgumentError, 'from_path cannot be nil' if from_path.nil?
super()
# class names, variable names, or instance variables
@@ -47,28 +48,43 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
def handle_special_CROSSREF(special)
name = special.text
+ return name if name =~ /\A[a-z]*\z/
+
return @seen[name] if @seen.include? name
- if name[0,1] == '#' then
+ 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
+ #
+ # Do not, however, use an if/elsif/else chain to do so. Instead, test
+ # each possible pattern until one matches. The reason for this is that a
+ # string like "YAML.txt" could be the txt() class method of class YAML (in
+ # which case it would match the first pattern, which splits the string
+ # into container and method components and looks up both) or a filename
+ # (in which case it would match the last pattern, which just checks
+ # whether the string as a whole is a known symbol).
+
+ if /([A-Z][\w:]*)[.\#](\w+[!?=]?)/ =~ lookup then
container = $1
method = $2
ref = @context.find_symbol container, method
- elsif /([A-Za-z]\w*)[.\#](\w+(\([\.\w+\*\/\+\-\=\<\>]+\))?)/ =~ lookup then
+ end
+
+ if !ref and
+ /([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
+ ref = @context.find_symbol lookup unless ref
+
out = if lookup =~ /^\\/ then
$'
elsif ref and ref.document_self then
diff --git a/lib/rdoc/markup/to_texinfo.rb b/lib/rdoc/markup/to_texinfo.rb
new file mode 100644
index 0000000000..533d3e34a0
--- /dev/null
+++ b/lib/rdoc/markup/to_texinfo.rb
@@ -0,0 +1,69 @@
+require 'rdoc/markup/formatter'
+require 'rdoc/markup/fragments'
+require 'rdoc/markup/inline'
+
+require 'rdoc/markup'
+require 'rdoc/markup/formatter'
+
+##
+# Convert SimpleMarkup to basic TexInfo format
+#
+# TODO: WTF is AttributeManager for?
+#
+class RDoc::Markup::ToTexInfo < RDoc::Markup::Formatter
+
+ def start_accepting
+ @text = []
+ end
+
+ def end_accepting
+ @text.join("\n")
+ end
+
+ def accept_paragraph(attributes, text)
+ @text << format(text)
+ end
+
+ def accept_verbatim(attributes, text)
+ @text << "@verb{|#{format(text)}|}"
+ end
+
+ def accept_heading(attributes, text)
+ heading = ['@majorheading', '@chapheading'][text.head_level - 1] || '@heading'
+ @text << "#{heading}{#{format(text)}}"
+ end
+
+ def accept_list_start(attributes, text)
+ @text << '@itemize @bullet'
+ end
+
+ def accept_list_end(attributes, text)
+ @text << '@end itemize'
+ end
+
+ def accept_list_item(attributes, text)
+ @text << "@item\n#{format(text)}"
+ end
+
+ def accept_blank_line(attributes, text)
+ @text << "\n"
+ end
+
+ def accept_rule(attributes, text)
+ @text << '-----'
+ end
+
+ def format(text)
+ text.txt.
+ gsub(/@/, "@@").
+ gsub(/\{/, "@{").
+ gsub(/\}/, "@}").
+ # gsub(/,/, "@,"). # technically only required in cross-refs
+ gsub(/\+([\w]+)\+/, "@code{\\1}").
+ gsub(/\<tt\>([^<]+)\<\/tt\>/, "@code{\\1}").
+ gsub(/\*([\w]+)\*/, "@strong{\\1}").
+ gsub(/\<b\>([^<]+)\<\/b\>/, "@strong{\\1}").
+ gsub(/_([\w]+)_/, "@emph{\\1}").
+ gsub(/\<em\>([^<]+)\<\/em\>/, "@emph{\\1}")
+ end
+end
diff --git a/lib/rdoc/options.rb b/lib/rdoc/options.rb
index bc17fba8ad..4fedb40b9c 100644
--- a/lib/rdoc/options.rb
+++ b/lib/rdoc/options.rb
@@ -39,7 +39,7 @@ class RDoc::Options
##
# Pattern for additional attr_... style methods
- attr_reader :extra_accessors
+ attr_accessor :extra_accessors
##
# Should we draw fileboxes in diagrams
@@ -62,6 +62,11 @@ class RDoc::Options
attr_accessor :generator
##
+ # Formatter to mark up text with
+
+ attr_accessor :formatter
+
+ ##
# image format for diagrams
attr_reader :image_format
@@ -95,7 +100,7 @@ class RDoc::Options
##
# The name to use for the output
- attr_reader :op_name
+ attr_accessor :op_name
##
# Are we promiscuous about showing module contents across multiple files
@@ -105,7 +110,7 @@ class RDoc::Options
##
# Don't display progress as we process the files
- attr_reader :quiet
+ attr_accessor :quiet
##
# Array of directories to search for files to satisfy an :include:
@@ -175,7 +180,6 @@ class RDoc::Options
@extra_accessor_flags = {}
@promiscuous = false
@force_update = false
- @title = "RDoc Documentation"
@css = nil
@webcvs = nil
@@ -513,6 +517,8 @@ Usage: #{opt.program_name} [options] [names...]
end
end
+ argv.insert(0, *ENV['RDOCOPT'].split) if ENV['RDOCOPT']
+
opts.parse! argv
@files = argv.dup
diff --git a/lib/rdoc/parser.rb b/lib/rdoc/parser.rb
new file mode 100644
index 0000000000..ea4676c626
--- /dev/null
+++ b/lib/rdoc/parser.rb
@@ -0,0 +1,109 @@
+require 'rdoc'
+require 'rdoc/code_objects'
+require 'rdoc/markup/preprocess'
+require 'rdoc/stats'
+
+##
+# A parser is simple a class that implements
+#
+# #initialize(file_name, body, options)
+#
+# and
+#
+# #scan
+#
+# The initialize method takes a file name to be used, the body of the file,
+# and an RDoc::Options object. The scan method is then called to return an
+# appropriately parsed TopLevel code object.
+#
+# The ParseFactory is used to redirect to the correct parser given a
+# filename extension. This magic works because individual parsers have to
+# register themselves with us as they are loaded in. The do this using the
+# following incantation
+#
+# require "rdoc/parser"
+#
+# class RDoc::Parser::Xyz < RDoc::Parser
+# parse_files_matching /\.xyz$/ # <<<<
+#
+# def initialize(file_name, body, options)
+# ...
+# end
+#
+# def scan
+# ...
+# end
+# end
+#
+# Just to make life interesting, if we suspect a plain text file, we also
+# look for a shebang line just in case it's a potential shell script
+
+class RDoc::Parser
+
+ @parsers = []
+
+ class << self
+ attr_reader :parsers
+ end
+
+ attr_writer :progress
+
+ ##
+ # Alias an extension to another extension. After this call, files ending
+ # "new_ext" will be parsed using the same parser as "old_ext"
+
+ def self.alias_extension(old_ext, new_ext)
+ parser = can_parse "xxx.#{old_ext}"
+ return false unless parser
+
+ RDoc::Parser.parsers.unshift [/\.#{new_ext}$/, parser.last]
+
+ true
+ end
+
+ ##
+ # Return a parser that can handle a particular extension
+
+ def self.can_parse(file_name)
+ RDoc::Parser.parsers.find { |regexp, parser| regexp =~ file_name }.last
+ end
+
+ ##
+ # Find the correct parser for a particular file name. Return a SimpleParser
+ # for ones that we don't know
+
+ def self.for(top_level, file_name, body, options, stats)
+ # If no extension, look for shebang
+ if file_name !~ /\.\w+$/ && body =~ %r{\A#!(.+)} then
+ shebang = $1
+ case shebang
+ when %r{env\s+ruby}, %r{/ruby}
+ file_name = "dummy.rb"
+ end
+ end
+
+ parser = can_parse file_name
+
+ parser.new top_level, file_name, body, options, stats
+ end
+
+ ##
+ # Record which file types this parser can understand.
+
+ def self.parse_files_matching(regexp)
+ RDoc::Parser.parsers.unshift [regexp, self]
+ end
+
+ def initialize(top_level, file_name, content, options, stats)
+ @top_level = top_level
+ @file_name = file_name
+ @content = content
+ @options = options
+ @stats = stats
+ @progress = $stderr unless options.quiet
+ end
+
+end
+
+require 'rdoc/parser/simple'
+
diff --git a/lib/rdoc/parser/c.rb b/lib/rdoc/parser/c.rb
new file mode 100644
index 0000000000..1db1e442c7
--- /dev/null
+++ b/lib/rdoc/parser/c.rb
@@ -0,0 +1,666 @@
+require 'rdoc/parser'
+require 'rdoc/known_classes'
+
+##
+# We attempt to parse C extension files. Basically we look for
+# the standard patterns that you find in extensions: <tt>rb_define_class,
+# rb_define_method</tt> and so on. We also try to find the corresponding
+# C source for the methods and extract comments, but if we fail
+# we don't worry too much.
+#
+# The comments associated with a Ruby method are extracted from the C
+# comment block associated with the routine that _implements_ that
+# method, that is to say the method whose name is given in the
+# <tt>rb_define_method</tt> call. For example, you might write:
+#
+# /*
+# * Returns a new array that is a one-dimensional flattening of this
+# * array (recursively). That is, for every element that is an array,
+# * extract its elements into the new array.
+# *
+# * s = [ 1, 2, 3 ] #=> [1, 2, 3]
+# * t = [ 4, 5, 6, [7, 8] ] #=> [4, 5, 6, [7, 8]]
+# * a = [ s, t, 9, 10 ] #=> [[1, 2, 3], [4, 5, 6, [7, 8]], 9, 10]
+# * a.flatten #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+# */
+# static VALUE
+# rb_ary_flatten(ary)
+# VALUE ary;
+# {
+# ary = rb_obj_dup(ary);
+# rb_ary_flatten_bang(ary);
+# return ary;
+# }
+#
+# ...
+#
+# void
+# Init_Array()
+# {
+# ...
+# rb_define_method(rb_cArray, "flatten", rb_ary_flatten, 0);
+#
+# Here RDoc will determine from the rb_define_method line that there's a
+# method called "flatten" in class Array, and will look for the implementation
+# in the method rb_ary_flatten. It will then use the comment from that
+# method in the HTML output. This method must be in the same source file
+# as the rb_define_method.
+#
+# C classes can be diagrammed (see /tc/dl/ruby/ruby/error.c), and RDoc
+# integrates C and Ruby source into one tree
+#
+# The comment blocks may include special directives:
+#
+# [Document-class: <i>name</i>]
+# This comment block is documentation for the given class. Use this
+# when the <tt>Init_xxx</tt> method is not named after the class.
+#
+# [Document-method: <i>name</i>]
+# This comment documents the named method. Use when RDoc cannot
+# automatically find the method from it's declaration
+#
+# [call-seq: <i>text up to an empty line</i>]
+# Because C source doesn't give descripive names to Ruby-level parameters,
+# you need to document the calling sequence explicitly
+#
+# In addition, RDoc assumes by default that the C method implementing a
+# Ruby function is in the same source file as the rb_define_method call.
+# If this isn't the case, add the comment:
+#
+# rb_define_method(....); // in: filename
+#
+# As an example, we might have an extension that defines multiple classes
+# in its Init_xxx method. We could document them using
+#
+# /*
+# * Document-class: MyClass
+# *
+# * Encapsulate the writing and reading of the configuration
+# * file. ...
+# */
+#
+# /*
+# * Document-method: read_value
+# *
+# * call-seq:
+# * cfg.read_value(key) -> value
+# * cfg.read_value(key} { |key| } -> value
+# *
+# * Return the value corresponding to +key+ from the configuration.
+# * In the second form, if the key isn't found, invoke the
+# * block and return its value.
+# */
+
+class RDoc::Parser::C < RDoc::Parser
+
+ parse_files_matching(/\.(?:([CcHh])\1?|c([+xp])\2|y)\z/)
+
+ attr_writer :progress
+
+ @@enclosure_classes = {}
+ @@known_bodies = {}
+
+ ##
+ # Prepare to parse a C file
+
+ def initialize(top_level, file_name, content, options, stats)
+ super
+
+ @known_classes = RDoc::KNOWN_CLASSES.dup
+ @content = handle_tab_width handle_ifdefs_in(@content)
+ @classes = Hash.new
+ @file_dir = File.dirname(@file_name)
+ end
+
+ def do_aliases
+ @content.scan(%r{rb_define_alias\s*\(\s*(\w+),\s*"([^"]+)",\s*"([^"]+)"\s*\)}m) do
+ |var_name, new_name, old_name|
+ @stats.num_methods += 1
+ class_name = @known_classes[var_name] || var_name
+ class_obj = find_class(var_name, class_name)
+
+ class_obj.add_alias RDoc::Alias.new("", old_name, new_name, "")
+ end
+ end
+
+ def do_classes
+ @content.scan(/(\w+)\s* = \s*rb_define_module\s*\(\s*"(\w+)"\s*\)/mx) do
+ |var_name, class_name|
+ handle_class_module(var_name, "module", class_name, nil, nil)
+ end
+
+ # The '.' lets us handle SWIG-generated files
+ @content.scan(/([\w\.]+)\s* = \s*rb_define_class\s*
+ \(
+ \s*"(\w+)",
+ \s*(\w+)\s*
+ \)/mx) do |var_name, class_name, parent|
+ handle_class_module(var_name, "class", class_name, parent, nil)
+ end
+
+ @content.scan(/(\w+)\s*=\s*boot_defclass\s*\(\s*"(\w+?)",\s*(\w+?)\s*\)/) do
+ |var_name, class_name, parent|
+ parent = nil if parent == "0"
+ handle_class_module(var_name, "class", class_name, parent, nil)
+ end
+
+ @content.scan(/(\w+)\s* = \s*rb_define_module_under\s*
+ \(
+ \s*(\w+),
+ \s*"(\w+)"
+ \s*\)/mx) do |var_name, in_module, class_name|
+ handle_class_module(var_name, "module", class_name, nil, in_module)
+ end
+
+ @content.scan(/([\w\.]+)\s* = \s*rb_define_class_under\s*
+ \(
+ \s*(\w+),
+ \s*"(\w+)",
+ \s*(\w+)\s*
+ \s*\)/mx) do |var_name, in_module, class_name, parent|
+ handle_class_module(var_name, "class", class_name, parent, in_module)
+ end
+ end
+
+ def do_constants
+ @content.scan(%r{\Wrb_define_
+ (
+ variable |
+ readonly_variable |
+ const |
+ global_const |
+ )
+ \s*\(
+ (?:\s*(\w+),)?
+ \s*"(\w+)",
+ \s*(.*?)\s*\)\s*;
+ }xm) do |type, var_name, const_name, definition|
+ var_name = "rb_cObject" if !var_name or var_name == "rb_mKernel"
+ handle_constants(type, var_name, const_name, definition)
+ end
+ end
+
+ ##
+ # Look for includes of the form:
+ #
+ # rb_include_module(rb_cArray, rb_mEnumerable);
+
+ def do_includes
+ @content.scan(/rb_include_module\s*\(\s*(\w+?),\s*(\w+?)\s*\)/) do |c,m|
+ if cls = @classes[c]
+ m = @known_classes[m] || m
+ cls.add_include RDoc::Include.new(m, "")
+ end
+ end
+ end
+
+ def do_methods
+ @content.scan(%r{rb_define_
+ (
+ singleton_method |
+ method |
+ module_function |
+ private_method
+ )
+ \s*\(\s*([\w\.]+),
+ \s*"([^"]+)",
+ \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
+ \s*(-?\w+)\s*\)
+ (?:;\s*/[*/]\s+in\s+(\w+?\.[cy]))?
+ }xm) do
+ |type, var_name, meth_name, meth_body, param_count, source_file|
+
+ # Ignore top-object and weird struct.c dynamic stuff
+ next if var_name == "ruby_top_self"
+ next if var_name == "nstr"
+ next if var_name == "envtbl"
+ next if var_name == "argf" # it'd be nice to handle this one
+
+ var_name = "rb_cObject" if var_name == "rb_mKernel"
+ handle_method(type, var_name, meth_name,
+ meth_body, param_count, source_file)
+ end
+
+ @content.scan(%r{rb_define_attr\(
+ \s*([\w\.]+),
+ \s*"([^"]+)",
+ \s*(\d+),
+ \s*(\d+)\s*\);
+ }xm) do |var_name, attr_name, attr_reader, attr_writer|
+ #var_name = "rb_cObject" if var_name == "rb_mKernel"
+ handle_attr(var_name, attr_name,
+ attr_reader.to_i != 0,
+ attr_writer.to_i != 0)
+ end
+
+ @content.scan(%r{rb_define_global_function\s*\(
+ \s*"([^"]+)",
+ \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
+ \s*(-?\w+)\s*\)
+ (?:;\s*/[*/]\s+in\s+(\w+?\.[cy]))?
+ }xm) do |meth_name, meth_body, param_count, source_file|
+ handle_method("method", "rb_mKernel", meth_name,
+ meth_body, param_count, source_file)
+ end
+
+ @content.scan(/define_filetest_function\s*\(
+ \s*"([^"]+)",
+ \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
+ \s*(-?\w+)\s*\)/xm) do
+ |meth_name, meth_body, param_count|
+
+ handle_method("method", "rb_mFileTest", meth_name, meth_body, param_count)
+ handle_method("singleton_method", "rb_cFile", meth_name, meth_body, param_count)
+ end
+ end
+
+ def find_attr_comment(attr_name)
+ if @content =~ %r{((?>/\*.*?\*/\s+))
+ rb_define_attr\((?:\s*(\w+),)?\s*"#{attr_name}"\s*,.*?\)\s*;}xmi
+ $1
+ elsif @content =~ %r{Document-attr:\s#{attr_name}\s*?\n((?>.*?\*/))}m
+ $1
+ else
+ ''
+ end
+ end
+
+ ##
+ # Find the C code corresponding to a Ruby method
+
+ def find_body(meth_name, meth_obj, body, quiet = false)
+ case body
+ when %r"((?>/\*.*?\*/\s*))(?:static\s+)?VALUE\s+#{meth_name}
+ \s*(\([^)]*\))\s*\{.*?^\}"xm
+ comment, params = $1, $2
+ body_text = $&
+
+ remove_private_comments(comment) if comment
+
+ # see if we can find the whole body
+
+ re = Regexp.escape(body_text) + '[^(]*^\{.*?^\}'
+ if Regexp.new(re, Regexp::MULTILINE).match(body)
+ body_text = $&
+ end
+
+ # The comment block may have been overridden with a 'Document-method'
+ # block. This happens in the interpreter when multiple methods are
+ # vectored through to the same C method but those methods are logically
+ # distinct (for example Kernel.hash and Kernel.object_id share the same
+ # implementation
+
+ override_comment = find_override_comment(meth_obj.name)
+ comment = override_comment if override_comment
+
+ find_modifiers(comment, meth_obj) if comment
+
+# meth_obj.params = params
+ meth_obj.start_collecting_tokens
+ meth_obj.add_token(RDoc::RubyToken::Token.new(1,1).set_text(body_text))
+ meth_obj.comment = mangle_comment(comment)
+ when %r{((?>/\*.*?\*/\s*))^\s*\#\s*define\s+#{meth_name}\s+(\w+)}m
+ comment = $1
+ find_body($2, meth_obj, body, true)
+ find_modifiers(comment, meth_obj)
+ meth_obj.comment = mangle_comment(comment) + meth_obj.comment
+ when %r{^\s*\#\s*define\s+#{meth_name}\s+(\w+)}m
+ unless find_body($1, meth_obj, body, true)
+ warn "No definition for #{meth_name}" unless quiet
+ return false
+ end
+ else
+
+ # No body, but might still have an override comment
+ comment = find_override_comment(meth_obj.name)
+
+ if comment
+ find_modifiers(comment, meth_obj)
+ meth_obj.comment = mangle_comment(comment)
+ else
+ warn "No definition for #{meth_name}" unless quiet
+ return false
+ end
+ end
+ true
+ end
+
+ def find_class(raw_name, name)
+ unless @classes[raw_name]
+ if raw_name =~ /^rb_m/
+ container = @top_level.add_module RDoc::NormalModule, name
+ else
+ container = @top_level.add_class RDoc::NormalClass, name, nil
+ end
+
+ container.record_location @top_level
+ @classes[raw_name] = container
+ end
+ @classes[raw_name]
+ end
+
+ ##
+ # Look for class or module documentation above Init_+class_name+(void),
+ # in a Document-class +class_name+ (or module) comment or above an
+ # rb_define_class (or module). If a comment is supplied above a matching
+ # Init_ and a rb_define_class the Init_ comment is used.
+ #
+ # /*
+ # * This is a comment for Foo
+ # */
+ # Init_Foo(void) {
+ # VALUE cFoo = rb_define_class("Foo", rb_cObject);
+ # }
+ #
+ # /*
+ # * Document-class: Foo
+ # * This is a comment for Foo
+ # */
+ # Init_foo(void) {
+ # VALUE cFoo = rb_define_class("Foo", rb_cObject);
+ # }
+ #
+ # /*
+ # * This is a comment for Foo
+ # */
+ # VALUE cFoo = rb_define_class("Foo", rb_cObject);
+
+ def find_class_comment(class_name, class_meth)
+ comment = nil
+ if @content =~ %r{((?>/\*.*?\*/\s+))
+ (static\s+)?void\s+Init_#{class_name}\s*(?:_\(\s*)?\(\s*(?:void\s*)\)}xmi
+ comment = $1
+ elsif @content =~ %r{Document-(class|module):\s#{class_name}\s*?\n((?>.*?\*/))}m
+ comment = $2
+ else
+ if @content =~ /rb_define_(class|module)/m then
+ class_name = class_name.split("::").last
+ comments = []
+ @content.split(/(\/\*.*?\*\/)\s*?\n/m).each_with_index do |chunk, index|
+ comments[index] = chunk
+ if chunk =~ /rb_define_(class|module).*?"(#{class_name})"/m then
+ comment = comments[index-1]
+ break
+ end
+ end
+ end
+ end
+ class_meth.comment = mangle_comment(comment) if comment
+ end
+
+ ##
+ # Finds a comment matching +type+ and +const_name+ either above the
+ # comment or in the matching Document- section.
+
+ def find_const_comment(type, const_name)
+ if @content =~ %r{((?>^\s*/\*.*?\*/\s+))
+ rb_define_#{type}\((?:\s*(\w+),)?\s*"#{const_name}"\s*,.*?\)\s*;}xmi
+ $1
+ elsif @content =~ %r{Document-(?:const|global|variable):\s#{const_name}\s*?\n((?>.*?\*/))}m
+ $1
+ else
+ ''
+ end
+ end
+
+ ##
+ # If the comment block contains a section that looks like:
+ #
+ # call-seq:
+ # Array.new
+ # Array.new(10)
+ #
+ # use it for the parameters.
+
+ def find_modifiers(comment, meth_obj)
+ if comment.sub!(/:nodoc:\s*^\s*\*?\s*$/m, '') or
+ comment.sub!(/\A\/\*\s*:nodoc:\s*\*\/\Z/, '')
+ meth_obj.document_self = false
+ end
+ if comment.sub!(/call-seq:(.*?)^\s*\*?\s*$/m, '') or
+ comment.sub!(/\A\/\*\s*call-seq:(.*?)\*\/\Z/, '')
+ seq = $1
+ seq.gsub!(/^\s*\*\s*/, '')
+ meth_obj.call_seq = seq
+ end
+ end
+
+ def find_override_comment(meth_name)
+ name = Regexp.escape(meth_name)
+ if @content =~ %r{Document-method:\s#{name}\s*?\n((?>.*?\*/))}m
+ $1
+ end
+ end
+
+ def handle_attr(var_name, attr_name, reader, writer)
+ rw = ''
+ if reader
+ #@stats.num_methods += 1
+ rw << 'R'
+ end
+ if writer
+ #@stats.num_methods += 1
+ rw << 'W'
+ end
+
+ class_name = @known_classes[var_name]
+
+ return unless class_name
+
+ class_obj = find_class(var_name, class_name)
+
+ if class_obj
+ comment = find_attr_comment(attr_name)
+ unless comment.empty?
+ comment = mangle_comment(comment)
+ end
+ att = RDoc::Attr.new '', attr_name, rw, comment
+ class_obj.add_attribute(att)
+ end
+ end
+
+ def handle_class_module(var_name, class_mod, class_name, parent, in_module)
+ progress(class_mod[0, 1])
+
+ parent_name = @known_classes[parent] || parent
+
+ if in_module
+ enclosure = @classes[in_module] || @@enclosure_classes[in_module]
+ unless enclosure
+ if enclosure = @known_classes[in_module]
+ handle_class_module(in_module, (/^rb_m/ =~ in_module ? "module" : "class"),
+ enclosure, nil, nil)
+ enclosure = @classes[in_module]
+ end
+ end
+ unless enclosure
+ warn("Enclosing class/module '#{in_module}' for " +
+ "#{class_mod} #{class_name} not known")
+ return
+ end
+ else
+ enclosure = @top_level
+ end
+
+ if class_mod == "class"
+ cm = enclosure.add_class RDoc::NormalClass, class_name, parent_name
+ @stats.num_classes += 1
+ else
+ cm = enclosure.add_module RDoc::NormalModule, class_name
+ @stats.num_modules += 1
+ end
+ cm.record_location(enclosure.toplevel)
+
+ find_class_comment(cm.full_name, cm)
+ @classes[var_name] = cm
+ @@enclosure_classes[var_name] = cm
+ @known_classes[var_name] = cm.full_name
+ end
+
+ ##
+ # Adds constant comments. By providing some_value: at the start ofthe
+ # comment you can override the C value of the comment to give a friendly
+ # definition.
+ #
+ # /* 300: The perfect score in bowling */
+ # rb_define_const(cFoo, "PERFECT", INT2FIX(300);
+ #
+ # Will override +INT2FIX(300)+ with the value +300+ in the output RDoc.
+ # Values may include quotes and escaped colons (\:).
+
+ def handle_constants(type, var_name, const_name, definition)
+ #@stats.num_constants += 1
+ class_name = @known_classes[var_name]
+
+ return unless class_name
+
+ class_obj = find_class(var_name, class_name)
+
+ unless class_obj
+ warn("Enclosing class/module '#{const_name}' for not known")
+ return
+ end
+
+ comment = find_const_comment(type, const_name)
+
+ # In the case of rb_define_const, the definition and comment are in
+ # "/* definition: comment */" form. The literal ':' and '\' characters
+ # can be escaped with a backslash.
+ if type.downcase == 'const' then
+ elements = mangle_comment(comment).split(':')
+ if elements.nil? or elements.empty? then
+ con = RDoc::Constant.new(const_name, definition,
+ mangle_comment(comment))
+ else
+ new_definition = elements[0..-2].join(':')
+ if new_definition.empty? then # Default to literal C definition
+ new_definition = definition
+ else
+ new_definition.gsub!("\:", ":")
+ new_definition.gsub!("\\", '\\')
+ end
+ new_definition.sub!(/\A(\s+)/, '')
+ new_comment = $1.nil? ? elements.last : "#{$1}#{elements.last.lstrip}"
+ con = RDoc::Constant.new(const_name, new_definition,
+ mangle_comment(new_comment))
+ end
+ else
+ con = RDoc::Constant.new const_name, definition, mangle_comment(comment)
+ end
+
+ class_obj.add_constant(con)
+ end
+
+ ##
+ # Removes #ifdefs that would otherwise confuse us
+
+ def handle_ifdefs_in(body)
+ body.gsub(/^#ifdef HAVE_PROTOTYPES.*?#else.*?\n(.*?)#endif.*?\n/m, '\1')
+ end
+
+ def handle_method(type, var_name, meth_name, meth_body, param_count,
+ source_file = nil)
+ progress(".")
+
+ @stats.num_methods += 1
+ class_name = @known_classes[var_name]
+
+ return unless class_name
+
+ class_obj = find_class(var_name, class_name)
+
+ if class_obj
+ if meth_name == "initialize"
+ meth_name = "new"
+ type = "singleton_method"
+ end
+ meth_obj = RDoc::AnyMethod.new("", meth_name)
+ meth_obj.singleton =
+ %w{singleton_method module_function}.include?(type)
+
+ p_count = (Integer(param_count) rescue -1)
+
+ if p_count < 0
+ meth_obj.params = "(...)"
+ elsif p_count == 0
+ meth_obj.params = "()"
+ else
+ meth_obj.params = "(" + (1..p_count).map{|i| "p#{i}"}.join(", ") + ")"
+ end
+
+ if source_file
+ file_name = File.join(@file_dir, source_file)
+ body = (@@known_bodies[source_file] ||= File.read(file_name))
+ else
+ body = @content
+ end
+ if find_body(meth_body, meth_obj, body) and meth_obj.document_self
+ class_obj.add_method(meth_obj)
+ end
+ end
+ end
+
+ def handle_tab_width(body)
+ if /\t/ =~ body
+ tab_width = @options.tab_width
+ body.split(/\n/).map do |line|
+ 1 while line.gsub!(/\t+/) { ' ' * (tab_width*$&.length - $`.length % tab_width)} && $~ #`
+ line
+ end .join("\n")
+ else
+ body
+ end
+ end
+
+ ##
+ # Remove the /*'s and leading asterisks from C comments
+
+ def mangle_comment(comment)
+ comment.sub!(%r{/\*+}) { " " * $&.length }
+ comment.sub!(%r{\*+/}) { " " * $&.length }
+ comment.gsub!(/^[ \t]*\*/m) { " " * $&.length }
+ comment
+ end
+
+ def progress(char)
+ unless @options.quiet
+ @progress.print(char)
+ @progress.flush
+ end
+ end
+
+ ##
+ # Removes lines that are commented out that might otherwise get picked up
+ # when scanning for classes and methods
+
+ def remove_commented_out_lines
+ @content.gsub!(%r{//.*rb_define_}, '//')
+ end
+
+ def remove_private_comments(comment)
+ comment.gsub!(/\/?\*--(.*?)\/?\*\+\+/m, '')
+ comment.sub!(/\/?\*--.*/m, '')
+ end
+
+ ##
+ # Extract the classes/modules and methods from a C file and return the
+ # corresponding top-level object
+
+ def scan
+ remove_commented_out_lines
+ do_classes
+ do_constants
+ do_methods
+ do_includes
+ do_aliases
+ @top_level
+ end
+
+ def warn(msg)
+ $stderr.puts
+ $stderr.puts msg
+ $stderr.flush
+ end
+
+end
+
diff --git a/lib/rdoc/parser/f95.rb b/lib/rdoc/parser/f95.rb
new file mode 100644
index 0000000000..6312bbaaff
--- /dev/null
+++ b/lib/rdoc/parser/f95.rb
@@ -0,0 +1,1837 @@
+require 'rdoc/parser'
+
+##
+# = Fortran95 RDoc Parser
+#
+# == Overview
+#
+# This parser parses Fortran95 files with suffixes "f90", "F90", "f95" and
+# "F95". Fortran95 files are expected to be conformed to Fortran95 standards.
+#
+# == Rules
+#
+# Fundamental rules are same as that of the Ruby parser. But comment markers
+# are '!' not '#'.
+#
+# === Correspondence between RDoc documentation and Fortran95 programs
+#
+# F95 parses main programs, modules, subroutines, functions, derived-types,
+# public variables, public constants, defined operators and defined
+# assignments. These components are described in items of RDoc documentation,
+# as follows.
+#
+# Files :: Files (same as Ruby)
+# Classes:: Modules
+# Methods:: Subroutines, functions, variables, constants, derived-types,
+# defined operators, defined assignments
+# Required files:: Files in which imported modules, external subroutines and
+# external functions are defined.
+# Included Modules:: List of imported modules
+# Attributes:: List of derived-types, List of imported modules all of whose
+# components are published again
+#
+# Components listed in 'Methods' (subroutines, functions, ...) defined in
+# modules are described in the item of 'Classes'. On the other hand,
+# components defined in main programs or as external procedures are described
+# in the item of 'Files'.
+#
+# === Components parsed by default
+#
+# By default, documentation on public components (subroutines, functions,
+# variables, constants, derived-types, defined operators, defined assignments)
+# are generated.
+#
+# With "--all" option, documentation on all components are generated (almost
+# same as the Ruby parser).
+#
+# === Information parsed automatically
+#
+# The following information is automatically parsed.
+#
+# * Types of arguments
+# * Types of variables and constants
+# * Types of variables in the derived types, and initial values
+# * NAMELISTs and types of variables in them, and initial values
+#
+# Aliases by interface statement are described in the item of 'Methods'.
+#
+# Components which are imported from other modules and published again are
+# described in the item of 'Methods'.
+#
+# === Format of comment blocks
+#
+# Comment blocks should be written as follows.
+#
+# Comment blocks are considered to be ended when the line without '!' appears.
+#
+# The indentation is not necessary.
+#
+# ! (Top of file)
+# !
+# ! Comment blocks for the files.
+# !
+# !--
+# ! The comment described in the part enclosed by
+# ! "!--" and "!++" is ignored.
+# !++
+# !
+# module hogehoge
+# !
+# ! Comment blocks for the modules (or the programs).
+# !
+#
+# private
+#
+# logical :: a ! a private variable
+# real, public :: b ! a public variable
+# integer, parameter :: c = 0 ! a public constant
+#
+# public :: c
+# public :: MULTI_ARRAY
+# public :: hoge, foo
+#
+# type MULTI_ARRAY
+# !
+# ! Comment blocks for the derived-types.
+# !
+# real, pointer :: var(:) =>null() ! Comments block for the variables.
+# integer :: num = 0
+# end type MULTI_ARRAY
+#
+# contains
+#
+# subroutine hoge( in, & ! Comment blocks between continuation lines are ignored.
+# & out )
+# !
+# ! Comment blocks for the subroutines or functions
+# !
+# character(*),intent(in):: in ! Comment blocks for the arguments.
+# character(*),intent(out),allocatable,target :: in
+# ! Comment blocks can be
+# ! written under Fortran statements.
+#
+# character(32) :: file ! This comment parsed as a variable in below NAMELIST.
+# integer :: id
+#
+# namelist /varinfo_nml/ file, id
+# !
+# ! Comment blocks for the NAMELISTs.
+# ! Information about variables are described above.
+# !
+#
+# ....
+#
+# end subroutine hoge
+#
+# integer function foo( in )
+# !
+# ! This part is considered as comment block.
+#
+# ! Comment blocks under blank lines are ignored.
+# !
+# integer, intent(in):: inA ! This part is considered as comment block.
+#
+# ! This part is ignored.
+#
+# end function foo
+#
+# subroutine hide( in, &
+# & out ) !:nodoc:
+# !
+# ! If "!:nodoc:" is described at end-of-line in subroutine
+# ! statement as above, the subroutine is ignored.
+# ! This assignment can be used to modules, subroutines,
+# ! functions, variables, constants, derived-types,
+# ! defined operators, defined assignments,
+# ! list of imported modules ("use" statement).
+# !
+#
+# ....
+#
+# end subroutine hide
+#
+# end module hogehoge
+
+class RDoc::Parser::F95 < RDoc::Parser
+
+ parse_files_matching(/\.((f|F)9(0|5)|F)$/)
+
+ class Token
+
+ NO_TEXT = "??".freeze
+
+ def initialize(line_no, char_no)
+ @line_no = line_no
+ @char_no = char_no
+ @text = NO_TEXT
+ end
+ # Because we're used in contexts that expect to return a token,
+ # we set the text string and then return ourselves
+ def set_text(text)
+ @text = text
+ self
+ end
+
+ attr_reader :line_no, :char_no, :text
+
+ end
+
+ @@external_aliases = []
+ @@public_methods = []
+
+ ##
+ # "false":: Comments are below source code
+ # "true" :: Comments are upper source code
+
+ COMMENTS_ARE_UPPER = false
+
+ ##
+ # Internal alias message
+
+ INTERNAL_ALIAS_MES = "Alias for"
+
+ ##
+ # External alias message
+
+ EXTERNAL_ALIAS_MES = "The entity is"
+
+ ##
+ # Define code constructs
+
+ def scan
+ # remove private comment
+ remaining_code = remove_private_comments(@content)
+
+ # continuation lines are united to one line
+ remaining_code = united_to_one_line(remaining_code)
+
+ # semicolons are replaced to line feed
+ remaining_code = semicolon_to_linefeed(remaining_code)
+
+ # collect comment for file entity
+ whole_comment, remaining_code = collect_first_comment(remaining_code)
+ @top_level.comment = whole_comment
+
+ # String "remaining_code" is converted to Array "remaining_lines"
+ remaining_lines = remaining_code.split("\n")
+
+ # "module" or "program" parts are parsed (new)
+ #
+ level_depth = 0
+ block_searching_flag = nil
+ block_searching_lines = []
+ pre_comment = []
+ module_program_trailing = ""
+ module_program_name = ""
+ other_block_level_depth = 0
+ other_block_searching_flag = nil
+ remaining_lines.collect!{|line|
+ if !block_searching_flag && !other_block_searching_flag
+ if line =~ /^\s*?module\s+(\w+)\s*?(!.*?)?$/i
+ block_searching_flag = :module
+ block_searching_lines << line
+ module_program_name = $1
+ module_program_trailing = find_comments($2)
+ next false
+ elsif line =~ /^\s*?program\s+(\w+)\s*?(!.*?)?$/i ||
+ line =~ /^\s*?\w/ && !block_start?(line)
+ block_searching_flag = :program
+ block_searching_lines << line
+ module_program_name = $1 || ""
+ module_program_trailing = find_comments($2)
+ next false
+
+ elsif block_start?(line)
+ other_block_searching_flag = true
+ next line
+
+ elsif line =~ /^\s*?!\s?(.*)/
+ pre_comment << line
+ next line
+ else
+ pre_comment = []
+ next line
+ end
+ elsif other_block_searching_flag
+ other_block_level_depth += 1 if block_start?(line)
+ other_block_level_depth -= 1 if block_end?(line)
+ if other_block_level_depth < 0
+ other_block_level_depth = 0
+ other_block_searching_flag = nil
+ end
+ next line
+ end
+
+ block_searching_lines << line
+ level_depth += 1 if block_start?(line)
+ level_depth -= 1 if block_end?(line)
+ if level_depth >= 0
+ next false
+ end
+
+ # "module_program_code" is formatted.
+ # ":nodoc:" flag is checked.
+ #
+ module_program_code = block_searching_lines.join("\n")
+ module_program_code = remove_empty_head_lines(module_program_code)
+ if module_program_trailing =~ /^:nodoc:/
+ # next loop to search next block
+ level_depth = 0
+ block_searching_flag = false
+ block_searching_lines = []
+ pre_comment = []
+ next false
+ end
+
+ # NormalClass is created, and added to @top_level
+ #
+ if block_searching_flag == :module
+ module_name = module_program_name
+ module_code = module_program_code
+ module_trailing = module_program_trailing
+ progress "m"
+ @stats.num_modules += 1
+ f9x_module = @top_level.add_module NormalClass, module_name
+ f9x_module.record_location @top_level
+
+ f9x_comment = COMMENTS_ARE_UPPER ?
+ find_comments(pre_comment.join("\n")) + "\n" + module_trailing :
+ module_trailing + "\n" + find_comments(module_code.sub(/^.*$\n/i, ''))
+ f9x_module.comment = f9x_comment
+ parse_program_or_module(f9x_module, module_code)
+
+ TopLevel.all_files.each do |name, toplevel|
+ if toplevel.include_includes?(module_name, @options.ignore_case)
+ if !toplevel.include_requires?(@file_name, @options.ignore_case)
+ toplevel.add_require(Require.new(@file_name, ""))
+ end
+ end
+ toplevel.each_classmodule{|m|
+ if m.include_includes?(module_name, @options.ignore_case)
+ if !m.include_requires?(@file_name, @options.ignore_case)
+ m.add_require(Require.new(@file_name, ""))
+ end
+ end
+ }
+ end
+ elsif block_searching_flag == :program
+ program_name = module_program_name
+ program_code = module_program_code
+ program_trailing = module_program_trailing
+ progress "p"
+ program_comment = COMMENTS_ARE_UPPER ?
+ find_comments(pre_comment.join("\n")) + "\n" + program_trailing :
+ program_trailing + "\n" + find_comments(program_code.sub(/^.*$\n/i, ''))
+ program_comment = "\n\n= <i>Program</i> <tt>#{program_name}</tt>\n\n" \
+ + program_comment
+ @top_level.comment << program_comment
+ parse_program_or_module(@top_level, program_code, :private)
+ end
+
+ # next loop to search next block
+ level_depth = 0
+ block_searching_flag = false
+ block_searching_lines = []
+ pre_comment = []
+ next false
+ }
+
+ remaining_lines.delete_if{ |line|
+ line == false
+ }
+
+ # External subprograms and functions are parsed
+ #
+ parse_program_or_module(@top_level, remaining_lines.join("\n"),
+ :public, true)
+
+ @top_level
+ end # End of scan
+
+ private
+
+ def parse_program_or_module(container, code,
+ visibility=:public, external=nil)
+ return unless container
+ return unless code
+ remaining_lines = code.split("\n")
+ remaining_code = "#{code}"
+
+ #
+ # Parse variables before "contains" in module
+ #
+ level_depth = 0
+ before_contains_lines = []
+ before_contains_code = nil
+ before_contains_flag = nil
+ remaining_lines.each{ |line|
+ if !before_contains_flag
+ if line =~ /^\s*?module\s+\w+\s*?(!.*?)?$/i
+ before_contains_flag = true
+ end
+ else
+ break if line =~ /^\s*?contains\s*?(!.*?)?$/i
+ level_depth += 1 if block_start?(line)
+ level_depth -= 1 if block_end?(line)
+ break if level_depth < 0
+ before_contains_lines << line
+ end
+ }
+ before_contains_code = before_contains_lines.join("\n")
+ if before_contains_code
+ before_contains_code.gsub!(/^\s*?interface\s+.*?\s+end\s+interface.*?$/im, "")
+ before_contains_code.gsub!(/^\s*?type[\s\,]+.*?\s+end\s+type.*?$/im, "")
+ end
+
+ #
+ # Parse global "use"
+ #
+ use_check_code = "#{before_contains_code}"
+ cascaded_modules_list = []
+ while use_check_code =~ /^\s*?use\s+(\w+)(.*?)(!.*?)?$/i
+ use_check_code = $~.pre_match
+ use_check_code << $~.post_match
+ used_mod_name = $1.strip.chomp
+ used_list = $2 || ""
+ used_trailing = $3 || ""
+ next if used_trailing =~ /!:nodoc:/
+ if !container.include_includes?(used_mod_name, @options.ignore_case)
+ progress "."
+ container.add_include Include.new(used_mod_name, "")
+ end
+ if ! (used_list =~ /\,\s*?only\s*?:/i )
+ cascaded_modules_list << "\#" + used_mod_name
+ end
+ end
+
+ #
+ # Parse public and private, and store information.
+ # This information is used when "add_method" and
+ # "set_visibility_for" are called.
+ #
+ visibility_default, visibility_info =
+ parse_visibility(remaining_lines.join("\n"), visibility, container)
+ @@public_methods.concat visibility_info
+ if visibility_default == :public
+ if !cascaded_modules_list.empty?
+ cascaded_modules =
+ Attr.new("Cascaded Modules",
+ "Imported modules all of whose components are published again",
+ "",
+ cascaded_modules_list.join(", "))
+ container.add_attribute(cascaded_modules)
+ end
+ end
+
+ #
+ # Check rename elements
+ #
+ use_check_code = "#{before_contains_code}"
+ while use_check_code =~ /^\s*?use\s+(\w+)\s*?\,(.+)$/i
+ use_check_code = $~.pre_match
+ use_check_code << $~.post_match
+ used_mod_name = $1.strip.chomp
+ used_elements = $2.sub(/\s*?only\s*?:\s*?/i, '')
+ used_elements.split(",").each{ |used|
+ if /\s*?(\w+)\s*?=>\s*?(\w+)\s*?/ =~ used
+ local = $1
+ org = $2
+ @@public_methods.collect!{ |pub_meth|
+ if local == pub_meth["name"] ||
+ local.upcase == pub_meth["name"].upcase &&
+ @options.ignore_case
+ pub_meth["name"] = org
+ pub_meth["local_name"] = local
+ end
+ pub_meth
+ }
+ end
+ }
+ end
+
+ #
+ # Parse private "use"
+ #
+ use_check_code = remaining_lines.join("\n")
+ while use_check_code =~ /^\s*?use\s+(\w+)(.*?)(!.*?)?$/i
+ use_check_code = $~.pre_match
+ use_check_code << $~.post_match
+ used_mod_name = $1.strip.chomp
+ used_trailing = $3 || ""
+ next if used_trailing =~ /!:nodoc:/
+ if !container.include_includes?(used_mod_name, @options.ignore_case)
+ progress "."
+ container.add_include Include.new(used_mod_name, "")
+ end
+ end
+
+ container.each_includes{ |inc|
+ TopLevel.all_files.each do |name, toplevel|
+ indicated_mod = toplevel.find_symbol(inc.name,
+ nil, @options.ignore_case)
+ if indicated_mod
+ indicated_name = indicated_mod.parent.file_relative_name
+ if !container.include_requires?(indicated_name, @options.ignore_case)
+ container.add_require(Require.new(indicated_name, ""))
+ end
+ break
+ end
+ end
+ }
+
+ #
+ # Parse derived-types definitions
+ #
+ derived_types_comment = ""
+ remaining_code = remaining_lines.join("\n")
+ while remaining_code =~ /^\s*?
+ type[\s\,]+(public|private)?\s*?(::)?\s*?
+ (\w+)\s*?(!.*?)?$
+ (.*?)
+ ^\s*?end\s+type.*?$
+ /imx
+ remaining_code = $~.pre_match
+ remaining_code << $~.post_match
+ typename = $3.chomp.strip
+ type_elements = $5 || ""
+ type_code = remove_empty_head_lines($&)
+ type_trailing = find_comments($4)
+ next if type_trailing =~ /^:nodoc:/
+ type_visibility = $1
+ type_comment = COMMENTS_ARE_UPPER ?
+ find_comments($~.pre_match) + "\n" + type_trailing :
+ type_trailing + "\n" + find_comments(type_code.sub(/^.*$\n/i, ''))
+ type_element_visibility_public = true
+ type_code.split("\n").each{ |line|
+ if /^\s*?private\s*?$/ =~ line
+ type_element_visibility_public = nil
+ break
+ end
+ } if type_code
+
+ args_comment = ""
+ type_args_info = nil
+
+ if @options.show_all
+ args_comment = find_arguments(nil, type_code, true)
+ else
+ type_public_args_list = []
+ type_args_info = definition_info(type_code)
+ type_args_info.each{ |arg|
+ arg_is_public = type_element_visibility_public
+ arg_is_public = true if arg.include_attr?("public")
+ arg_is_public = nil if arg.include_attr?("private")
+ type_public_args_list << arg.varname if arg_is_public
+ }
+ args_comment = find_arguments(type_public_args_list, type_code)
+ end
+
+ type = AnyMethod.new("type #{typename}", typename)
+ type.singleton = false
+ type.params = ""
+ type.comment = "<b><em> Derived Type </em></b> :: <tt></tt>\n"
+ type.comment << args_comment if args_comment
+ type.comment << type_comment if type_comment
+ progress "t"
+ @stats.num_methods += 1
+ container.add_method type
+
+ set_visibility(container, typename, visibility_default, @@public_methods)
+
+ if type_visibility
+ type_visibility.gsub!(/\s/,'')
+ type_visibility.gsub!(/\,/,'')
+ type_visibility.gsub!(/:/,'')
+ type_visibility.downcase!
+ if type_visibility == "public"
+ container.set_visibility_for([typename], :public)
+ elsif type_visibility == "private"
+ container.set_visibility_for([typename], :private)
+ end
+ end
+
+ check_public_methods(type, container.name)
+
+ if @options.show_all
+ derived_types_comment << ", " unless derived_types_comment.empty?
+ derived_types_comment << typename
+ else
+ if type.visibility == :public
+ derived_types_comment << ", " unless derived_types_comment.empty?
+ derived_types_comment << typename
+ end
+ end
+
+ end
+
+ if !derived_types_comment.empty?
+ derived_types_table =
+ Attr.new("Derived Types", "Derived_Types", "",
+ derived_types_comment)
+ container.add_attribute(derived_types_table)
+ end
+
+ #
+ # move interface scope
+ #
+ interface_code = ""
+ while remaining_code =~ /^\s*?
+ interface(
+ \s+\w+ |
+ \s+operator\s*?\(.*?\) |
+ \s+assignment\s*?\(\s*?=\s*?\)
+ )?\s*?$
+ (.*?)
+ ^\s*?end\s+interface.*?$
+ /imx
+ interface_code << remove_empty_head_lines($&) + "\n"
+ remaining_code = $~.pre_match
+ remaining_code << $~.post_match
+ end
+
+ #
+ # Parse global constants or variables in modules
+ #
+ const_var_defs = definition_info(before_contains_code)
+ const_var_defs.each{|defitem|
+ next if defitem.nodoc
+ const_or_var_type = "Variable"
+ const_or_var_progress = "v"
+ if defitem.include_attr?("parameter")
+ const_or_var_type = "Constant"
+ const_or_var_progress = "c"
+ end
+ const_or_var = AnyMethod.new(const_or_var_type, defitem.varname)
+ const_or_var.singleton = false
+ const_or_var.params = ""
+ self_comment = find_arguments([defitem.varname], before_contains_code)
+ const_or_var.comment = "<b><em>" + const_or_var_type + "</em></b> :: <tt></tt>\n"
+ const_or_var.comment << self_comment if self_comment
+ progress const_or_var_progress
+ @stats.num_methods += 1
+ container.add_method const_or_var
+
+ set_visibility(container, defitem.varname, visibility_default, @@public_methods)
+
+ if defitem.include_attr?("public")
+ container.set_visibility_for([defitem.varname], :public)
+ elsif defitem.include_attr?("private")
+ container.set_visibility_for([defitem.varname], :private)
+ end
+
+ check_public_methods(const_or_var, container.name)
+
+ } if const_var_defs
+
+ remaining_lines = remaining_code.split("\n")
+
+ # "subroutine" or "function" parts are parsed (new)
+ #
+ level_depth = 0
+ block_searching_flag = nil
+ block_searching_lines = []
+ pre_comment = []
+ procedure_trailing = ""
+ procedure_name = ""
+ procedure_params = ""
+ procedure_prefix = ""
+ procedure_result_arg = ""
+ procedure_type = ""
+ contains_lines = []
+ contains_flag = nil
+ remaining_lines.collect!{|line|
+ if !block_searching_flag
+ # subroutine
+ if line =~ /^\s*?
+ (recursive|pure|elemental)?\s*?
+ subroutine\s+(\w+)\s*?(\(.*?\))?\s*?(!.*?)?$
+ /ix
+ block_searching_flag = :subroutine
+ block_searching_lines << line
+
+ procedure_name = $2.chomp.strip
+ procedure_params = $3 || ""
+ procedure_prefix = $1 || ""
+ procedure_trailing = $4 || "!"
+ next false
+
+ # function
+ elsif line =~ /^\s*?
+ (recursive|pure|elemental)?\s*?
+ (
+ character\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | type\s*?\([\w\s]+?\)\s+
+ | integer\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | real\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | double\s+precision\s+
+ | logical\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | complex\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ )?
+ function\s+(\w+)\s*?
+ (\(.*?\))?(\s+result\((.*?)\))?\s*?(!.*?)?$
+ /ix
+ block_searching_flag = :function
+ block_searching_lines << line
+
+ procedure_prefix = $1 || ""
+ procedure_type = $2 ? $2.chomp.strip : nil
+ procedure_name = $8.chomp.strip
+ procedure_params = $9 || ""
+ procedure_result_arg = $11 ? $11.chomp.strip : procedure_name
+ procedure_trailing = $12 || "!"
+ next false
+ elsif line =~ /^\s*?!\s?(.*)/
+ pre_comment << line
+ next line
+ else
+ pre_comment = []
+ next line
+ end
+ end
+ contains_flag = true if line =~ /^\s*?contains\s*?(!.*?)?$/
+ block_searching_lines << line
+ contains_lines << line if contains_flag
+
+ level_depth += 1 if block_start?(line)
+ level_depth -= 1 if block_end?(line)
+ if level_depth >= 0
+ next false
+ end
+
+ # "procedure_code" is formatted.
+ # ":nodoc:" flag is checked.
+ #
+ procedure_code = block_searching_lines.join("\n")
+ procedure_code = remove_empty_head_lines(procedure_code)
+ if procedure_trailing =~ /^!:nodoc:/
+ # next loop to search next block
+ level_depth = 0
+ block_searching_flag = nil
+ block_searching_lines = []
+ pre_comment = []
+ procedure_trailing = ""
+ procedure_name = ""
+ procedure_params = ""
+ procedure_prefix = ""
+ procedure_result_arg = ""
+ procedure_type = ""
+ contains_lines = []
+ contains_flag = nil
+ next false
+ end
+
+ # AnyMethod is created, and added to container
+ #
+ subroutine_function = nil
+ if block_searching_flag == :subroutine
+ subroutine_prefix = procedure_prefix
+ subroutine_name = procedure_name
+ subroutine_params = procedure_params
+ subroutine_trailing = procedure_trailing
+ subroutine_code = procedure_code
+
+ subroutine_comment = COMMENTS_ARE_UPPER ?
+ pre_comment.join("\n") + "\n" + subroutine_trailing :
+ subroutine_trailing + "\n" + subroutine_code.sub(/^.*$\n/i, '')
+ subroutine = AnyMethod.new("subroutine", subroutine_name)
+ parse_subprogram(subroutine, subroutine_params,
+ subroutine_comment, subroutine_code,
+ before_contains_code, nil, subroutine_prefix)
+ progress "s"
+ @stats.num_methods += 1
+ container.add_method subroutine
+ subroutine_function = subroutine
+
+ elsif block_searching_flag == :function
+ function_prefix = procedure_prefix
+ function_type = procedure_type
+ function_name = procedure_name
+ function_params_org = procedure_params
+ function_result_arg = procedure_result_arg
+ function_trailing = procedure_trailing
+ function_code_org = procedure_code
+
+ function_comment = COMMENTS_ARE_UPPER ?
+ pre_comment.join("\n") + "\n" + function_trailing :
+ function_trailing + "\n " + function_code_org.sub(/^.*$\n/i, '')
+
+ function_code = "#{function_code_org}"
+ if function_type
+ function_code << "\n" + function_type + " :: " + function_result_arg
+ end
+
+ function_params =
+ function_params_org.sub(/^\(/, "\(#{function_result_arg}, ")
+
+ function = AnyMethod.new("function", function_name)
+ parse_subprogram(function, function_params,
+ function_comment, function_code,
+ before_contains_code, true, function_prefix)
+
+ # Specific modification due to function
+ function.params.sub!(/\(\s*?#{function_result_arg}\s*?,\s*?/, "\( ")
+ function.params << " result(" + function_result_arg + ")"
+ function.start_collecting_tokens
+ function.add_token Token.new(1,1).set_text(function_code_org)
+
+ progress "f"
+ @stats.num_methods += 1
+ container.add_method function
+ subroutine_function = function
+
+ end
+
+ # The visibility of procedure is specified
+ #
+ set_visibility(container, procedure_name,
+ visibility_default, @@public_methods)
+
+ # The alias for this procedure from external modules
+ #
+ check_external_aliases(procedure_name,
+ subroutine_function.params,
+ subroutine_function.comment, subroutine_function) if external
+ check_public_methods(subroutine_function, container.name)
+
+
+ # contains_lines are parsed as private procedures
+ if contains_flag
+ parse_program_or_module(container,
+ contains_lines.join("\n"), :private)
+ end
+
+ # next loop to search next block
+ level_depth = 0
+ block_searching_flag = nil
+ block_searching_lines = []
+ pre_comment = []
+ procedure_trailing = ""
+ procedure_name = ""
+ procedure_params = ""
+ procedure_prefix = ""
+ procedure_result_arg = ""
+ contains_lines = []
+ contains_flag = nil
+ next false
+ } # End of remaining_lines.collect!{|line|
+
+ # Array remains_lines is converted to String remains_code again
+ #
+ remaining_code = remaining_lines.join("\n")
+
+ #
+ # Parse interface
+ #
+ interface_scope = false
+ generic_name = ""
+ interface_code.split("\n").each{ |line|
+ if /^\s*?
+ interface(
+ \s+\w+|
+ \s+operator\s*?\(.*?\)|
+ \s+assignment\s*?\(\s*?=\s*?\)
+ )?
+ \s*?(!.*?)?$
+ /ix =~ line
+ generic_name = $1 ? $1.strip.chomp : nil
+ interface_trailing = $2 || "!"
+ interface_scope = true
+ interface_scope = false if interface_trailing =~ /!:nodoc:/
+# if generic_name =~ /operator\s*?\((.*?)\)/i
+# operator_name = $1
+# if operator_name && !operator_name.empty?
+# generic_name = "#{operator_name}"
+# end
+# end
+# if generic_name =~ /assignment\s*?\((.*?)\)/i
+# assignment_name = $1
+# if assignment_name && !assignment_name.empty?
+# generic_name = "#{assignment_name}"
+# end
+# end
+ end
+ if /^\s*?end\s+interface/i =~ line
+ interface_scope = false
+ generic_name = nil
+ end
+ # internal alias
+ if interface_scope && /^\s*?module\s+procedure\s+(.*?)(!.*?)?$/i =~ line
+ procedures = $1.strip.chomp
+ procedures_trailing = $2 || "!"
+ next if procedures_trailing =~ /!:nodoc:/
+ procedures.split(",").each{ |proc|
+ proc.strip!
+ proc.chomp!
+ next if generic_name == proc || !generic_name
+ old_meth = container.find_symbol(proc, nil, @options.ignore_case)
+ next if !old_meth
+ nolink = old_meth.visibility == :private ? true : nil
+ nolink = nil if @options.show_all
+ new_meth =
+ initialize_external_method(generic_name, proc,
+ old_meth.params, nil,
+ old_meth.comment,
+ old_meth.clone.token_stream[0].text,
+ true, nolink)
+ new_meth.singleton = old_meth.singleton
+
+ progress "i"
+ @stats.num_methods += 1
+ container.add_method new_meth
+
+ set_visibility(container, generic_name, visibility_default, @@public_methods)
+
+ check_public_methods(new_meth, container.name)
+
+ }
+ end
+
+ # external aliases
+ if interface_scope
+ # subroutine
+ proc = nil
+ params = nil
+ procedures_trailing = nil
+ if line =~ /^\s*?
+ (recursive|pure|elemental)?\s*?
+ subroutine\s+(\w+)\s*?(\(.*?\))?\s*?(!.*?)?$
+ /ix
+ proc = $2.chomp.strip
+ generic_name = proc unless generic_name
+ params = $3 || ""
+ procedures_trailing = $4 || "!"
+
+ # function
+ elsif line =~ /^\s*?
+ (recursive|pure|elemental)?\s*?
+ (
+ character\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | type\s*?\([\w\s]+?\)\s+
+ | integer\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | real\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | double\s+precision\s+
+ | logical\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | complex\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ )?
+ function\s+(\w+)\s*?
+ (\(.*?\))?(\s+result\((.*?)\))?\s*?(!.*?)?$
+ /ix
+ proc = $8.chomp.strip
+ generic_name = proc unless generic_name
+ params = $9 || ""
+ procedures_trailing = $12 || "!"
+ else
+ next
+ end
+ next if procedures_trailing =~ /!:nodoc:/
+ indicated_method = nil
+ indicated_file = nil
+ TopLevel.all_files.each do |name, toplevel|
+ indicated_method = toplevel.find_local_symbol(proc, @options.ignore_case)
+ indicated_file = name
+ break if indicated_method
+ end
+
+ if indicated_method
+ external_method =
+ initialize_external_method(generic_name, proc,
+ indicated_method.params,
+ indicated_file,
+ indicated_method.comment)
+
+ progress "e"
+ @stats.num_methods += 1
+ container.add_method external_method
+ set_visibility(container, generic_name, visibility_default, @@public_methods)
+ if !container.include_requires?(indicated_file, @options.ignore_case)
+ container.add_require(Require.new(indicated_file, ""))
+ end
+ check_public_methods(external_method, container.name)
+
+ else
+ @@external_aliases << {
+ "new_name" => generic_name,
+ "old_name" => proc,
+ "file_or_module" => container,
+ "visibility" => find_visibility(container, generic_name, @@public_methods) || visibility_default
+ }
+ end
+ end
+
+ } if interface_code # End of interface_code.split("\n").each ...
+
+ #
+ # Already imported methods are removed from @@public_methods.
+ # Remainders are assumed to be imported from other modules.
+ #
+ @@public_methods.delete_if{ |method| method["entity_is_discovered"]}
+
+ @@public_methods.each{ |pub_meth|
+ next unless pub_meth["file_or_module"].name == container.name
+ pub_meth["used_modules"].each{ |used_mod|
+ TopLevel.all_classes_and_modules.each{ |modules|
+ if modules.name == used_mod ||
+ modules.name.upcase == used_mod.upcase &&
+ @options.ignore_case
+ modules.method_list.each{ |meth|
+ if meth.name == pub_meth["name"] ||
+ meth.name.upcase == pub_meth["name"].upcase &&
+ @options.ignore_case
+ new_meth = initialize_public_method(meth,
+ modules.name)
+ if pub_meth["local_name"]
+ new_meth.name = pub_meth["local_name"]
+ end
+ progress "e"
+ @stats.num_methods += 1
+ container.add_method new_meth
+ end
+ }
+ end
+ }
+ }
+ }
+
+ container
+ end # End of parse_program_or_module
+
+ ##
+ # Parse arguments, comment, code of subroutine and function. Return
+ # AnyMethod object.
+
+ def parse_subprogram(subprogram, params, comment, code,
+ before_contains=nil, function=nil, prefix=nil)
+ subprogram.singleton = false
+ prefix = "" if !prefix
+ arguments = params.sub(/\(/, "").sub(/\)/, "").split(",") if params
+ args_comment, params_opt =
+ find_arguments(arguments, code.sub(/^s*?contains\s*?(!.*?)?$.*/im, ""),
+ nil, nil, true)
+ params_opt = "( " + params_opt + " ) " if params_opt
+ subprogram.params = params_opt || ""
+ namelist_comment = find_namelists(code, before_contains)
+
+ block_comment = find_comments comment
+ if function
+ subprogram.comment = "<b><em> Function </em></b> :: <em>#{prefix}</em>\n"
+ else
+ subprogram.comment = "<b><em> Subroutine </em></b> :: <em>#{prefix}</em>\n"
+ end
+ subprogram.comment << args_comment if args_comment
+ subprogram.comment << block_comment if block_comment
+ subprogram.comment << namelist_comment if namelist_comment
+
+ # For output source code
+ subprogram.start_collecting_tokens
+ subprogram.add_token Token.new(1,1).set_text(code)
+
+ subprogram
+ end
+
+ ##
+ # Collect comment for file entity
+
+ def collect_first_comment(body)
+ comment = ""
+ not_comment = ""
+ comment_start = false
+ comment_end = false
+ body.split("\n").each{ |line|
+ if comment_end
+ not_comment << line
+ not_comment << "\n"
+ elsif /^\s*?!\s?(.*)$/i =~ line
+ comment_start = true
+ comment << $1
+ comment << "\n"
+ elsif /^\s*?$/i =~ line
+ comment_end = true if comment_start && COMMENTS_ARE_UPPER
+ else
+ comment_end = true
+ not_comment << line
+ not_comment << "\n"
+ end
+ }
+ return comment, not_comment
+ end
+
+
+ ##
+ # Return comments of definitions of arguments
+ #
+ # If "all" argument is true, information of all arguments are returned.
+ #
+ # If "modified_params" is true, list of arguments are decorated, for
+ # example, optional arguments are parenthetic as "[arg]".
+
+ def find_arguments(args, text, all=nil, indent=nil, modified_params=nil)
+ return unless args || all
+ indent = "" unless indent
+ args = ["all"] if all
+ params = "" if modified_params
+ comma = ""
+ return unless text
+ args_rdocforms = "\n"
+ remaining_lines = "#{text}"
+ definitions = definition_info(remaining_lines)
+ args.each{ |arg|
+ arg.strip!
+ arg.chomp!
+ definitions.each { |defitem|
+ if arg == defitem.varname.strip.chomp || all
+ args_rdocforms << <<-"EOF"
+
+#{indent}<tt><b>#{defitem.varname.chomp.strip}#{defitem.arraysuffix}</b> #{defitem.inivalue}</tt> ::
+#{indent} <tt>#{defitem.types.chomp.strip}</tt>
+EOF
+ if !defitem.comment.chomp.strip.empty?
+ comment = ""
+ defitem.comment.split("\n").each{ |line|
+ comment << " " + line + "\n"
+ }
+ args_rdocforms << <<-"EOF"
+
+#{indent} <tt></tt> ::
+#{indent} <tt></tt>
+#{indent} #{comment.chomp.strip}
+EOF
+ end
+
+ if modified_params
+ if defitem.include_attr?("optional")
+ params << "#{comma}[#{arg}]"
+ else
+ params << "#{comma}#{arg}"
+ end
+ comma = ", "
+ end
+ end
+ }
+ }
+ if modified_params
+ return args_rdocforms, params
+ else
+ return args_rdocforms
+ end
+ end
+
+ ##
+ # Return comments of definitions of namelists
+
+ def find_namelists(text, before_contains=nil)
+ return nil if !text
+ result = ""
+ lines = "#{text}"
+ before_contains = "" if !before_contains
+ while lines =~ /^\s*?namelist\s+\/\s*?(\w+)\s*?\/([\s\w\,]+)$/i
+ lines = $~.post_match
+ nml_comment = COMMENTS_ARE_UPPER ?
+ find_comments($~.pre_match) : find_comments($~.post_match)
+ nml_name = $1
+ nml_args = $2.split(",")
+ result << "\n\n=== NAMELIST <tt><b>" + nml_name + "</tt></b>\n\n"
+ result << nml_comment + "\n" if nml_comment
+ if lines.split("\n")[0] =~ /^\//i
+ lines = "namelist " + lines
+ end
+ result << find_arguments(nml_args, "#{text}" + "\n" + before_contains)
+ end
+ return result
+ end
+
+ ##
+ # Comments just after module or subprogram, or arguments are returned. If
+ # "COMMENTS_ARE_UPPER" is true, comments just before modules or subprograms
+ # are returnd
+
+ def find_comments text
+ return "" unless text
+ lines = text.split("\n")
+ lines.reverse! if COMMENTS_ARE_UPPER
+ comment_block = Array.new
+ lines.each do |line|
+ break if line =~ /^\s*?\w/ || line =~ /^\s*?$/
+ if COMMENTS_ARE_UPPER
+ comment_block.unshift line.sub(/^\s*?!\s?/,"")
+ else
+ comment_block.push line.sub(/^\s*?!\s?/,"")
+ end
+ end
+ nice_lines = comment_block.join("\n").split "\n\s*?\n"
+ nice_lines[0] ||= ""
+ nice_lines.shift
+ end
+
+ def progress(char)
+ unless @options.quiet
+ @progress.print(char)
+ @progress.flush
+ end
+ end
+
+ ##
+ # Create method for internal alias
+
+ def initialize_public_method(method, parent)
+ return if !method || !parent
+
+ new_meth = AnyMethod.new("External Alias for module", method.name)
+ new_meth.singleton = method.singleton
+ new_meth.params = method.params.clone
+ new_meth.comment = remove_trailing_alias(method.comment.clone)
+ new_meth.comment << "\n\n#{EXTERNAL_ALIAS_MES} #{parent.strip.chomp}\##{method.name}"
+
+ return new_meth
+ end
+
+ ##
+ # Create method for external alias
+ #
+ # If argument "internal" is true, file is ignored.
+
+ def initialize_external_method(new, old, params, file, comment, token=nil,
+ internal=nil, nolink=nil)
+ return nil unless new || old
+
+ if internal
+ external_alias_header = "#{INTERNAL_ALIAS_MES} "
+ external_alias_text = external_alias_header + old
+ elsif file
+ external_alias_header = "#{EXTERNAL_ALIAS_MES} "
+ external_alias_text = external_alias_header + file + "#" + old
+ else
+ return nil
+ end
+ external_meth = AnyMethod.new(external_alias_text, new)
+ external_meth.singleton = false
+ external_meth.params = params
+ external_comment = remove_trailing_alias(comment) + "\n\n" if comment
+ external_meth.comment = external_comment || ""
+ if nolink && token
+ external_meth.start_collecting_tokens
+ external_meth.add_token Token.new(1,1).set_text(token)
+ else
+ external_meth.comment << external_alias_text
+ end
+
+ return external_meth
+ end
+
+ ##
+ # Parse visibility
+
+ def parse_visibility(code, default, container)
+ result = []
+ visibility_default = default || :public
+
+ used_modules = []
+ container.includes.each{|i| used_modules << i.name} if container
+
+ remaining_code = code.gsub(/^\s*?type[\s\,]+.*?\s+end\s+type.*?$/im, "")
+ remaining_code.split("\n").each{ |line|
+ if /^\s*?private\s*?$/ =~ line
+ visibility_default = :private
+ break
+ end
+ } if remaining_code
+
+ remaining_code.split("\n").each{ |line|
+ if /^\s*?private\s*?(::)?\s+(.*)\s*?(!.*?)?/i =~ line
+ methods = $2.sub(/!.*$/, '')
+ methods.split(",").each{ |meth|
+ meth.sub!(/!.*$/, '')
+ meth.gsub!(/:/, '')
+ result << {
+ "name" => meth.chomp.strip,
+ "visibility" => :private,
+ "used_modules" => used_modules.clone,
+ "file_or_module" => container,
+ "entity_is_discovered" => nil,
+ "local_name" => nil
+ }
+ }
+ elsif /^\s*?public\s*?(::)?\s+(.*)\s*?(!.*?)?/i =~ line
+ methods = $2.sub(/!.*$/, '')
+ methods.split(",").each{ |meth|
+ meth.sub!(/!.*$/, '')
+ meth.gsub!(/:/, '')
+ result << {
+ "name" => meth.chomp.strip,
+ "visibility" => :public,
+ "used_modules" => used_modules.clone,
+ "file_or_module" => container,
+ "entity_is_discovered" => nil,
+ "local_name" => nil
+ }
+ }
+ end
+ } if remaining_code
+
+ if container
+ result.each{ |vis_info|
+ vis_info["parent"] = container.name
+ }
+ end
+
+ return visibility_default, result
+ end
+
+ ##
+ # Set visibility
+ #
+ # "subname" element of "visibility_info" is deleted.
+
+ def set_visibility(container, subname, visibility_default, visibility_info)
+ return unless container || subname || visibility_default || visibility_info
+ not_found = true
+ visibility_info.collect!{ |info|
+ if info["name"] == subname ||
+ @options.ignore_case && info["name"].upcase == subname.upcase
+ if info["file_or_module"].name == container.name
+ container.set_visibility_for([subname], info["visibility"])
+ info["entity_is_discovered"] = true
+ not_found = false
+ end
+ end
+ info
+ }
+ if not_found
+ return container.set_visibility_for([subname], visibility_default)
+ else
+ return container
+ end
+ end
+
+ ##
+ # Find visibility
+
+ def find_visibility(container, subname, visibility_info)
+ return nil if !subname || !visibility_info
+ visibility_info.each{ |info|
+ if info["name"] == subname ||
+ @options.ignore_case && info["name"].upcase == subname.upcase
+ if info["parent"] == container.name
+ return info["visibility"]
+ end
+ end
+ }
+ return nil
+ end
+
+ ##
+ # Check external aliases
+
+ def check_external_aliases(subname, params, comment, test=nil)
+ @@external_aliases.each{ |alias_item|
+ if subname == alias_item["old_name"] ||
+ subname.upcase == alias_item["old_name"].upcase &&
+ @options.ignore_case
+
+ new_meth = initialize_external_method(alias_item["new_name"],
+ subname, params, @file_name,
+ comment)
+ new_meth.visibility = alias_item["visibility"]
+
+ progress "e"
+ @stats.num_methods += 1
+ alias_item["file_or_module"].add_method(new_meth)
+
+ if !alias_item["file_or_module"].include_requires?(@file_name, @options.ignore_case)
+ alias_item["file_or_module"].add_require(Require.new(@file_name, ""))
+ end
+ end
+ }
+ end
+
+ ##
+ # Check public_methods
+
+ def check_public_methods(method, parent)
+ return if !method || !parent
+ @@public_methods.each{ |alias_item|
+ parent_is_used_module = nil
+ alias_item["used_modules"].each{ |used_module|
+ if used_module == parent ||
+ used_module.upcase == parent.upcase &&
+ @options.ignore_case
+ parent_is_used_module = true
+ end
+ }
+ next if !parent_is_used_module
+
+ if method.name == alias_item["name"] ||
+ method.name.upcase == alias_item["name"].upcase &&
+ @options.ignore_case
+
+ new_meth = initialize_public_method(method, parent)
+ if alias_item["local_name"]
+ new_meth.name = alias_item["local_name"]
+ end
+
+ progress "e"
+ @stats.num_methods += 1
+ alias_item["file_or_module"].add_method new_meth
+ end
+ }
+ end
+
+ ##
+ # Continuous lines are united.
+ #
+ # Comments in continuous lines are removed.
+
+ def united_to_one_line(f90src)
+ return "" unless f90src
+ lines = f90src.split("\n")
+ previous_continuing = false
+ now_continuing = false
+ body = ""
+ lines.each{ |line|
+ words = line.split("")
+ next if words.empty? && previous_continuing
+ commentout = false
+ brank_flag = true ; brank_char = ""
+ squote = false ; dquote = false
+ ignore = false
+ words.collect! { |char|
+ if previous_continuing && brank_flag
+ now_continuing = true
+ ignore = true
+ case char
+ when "!" ; break
+ when " " ; brank_char << char ; next ""
+ when "&"
+ brank_flag = false
+ now_continuing = false
+ next ""
+ else
+ brank_flag = false
+ now_continuing = false
+ ignore = false
+ next brank_char + char
+ end
+ end
+ ignore = false
+
+ if now_continuing
+ next ""
+ elsif !(squote) && !(dquote) && !(commentout)
+ case char
+ when "!" ; commentout = true ; next char
+ when "\""; dquote = true ; next char
+ when "\'"; squote = true ; next char
+ when "&" ; now_continuing = true ; next ""
+ else next char
+ end
+ elsif commentout
+ next char
+ elsif squote
+ case char
+ when "\'"; squote = false ; next char
+ else next char
+ end
+ elsif dquote
+ case char
+ when "\""; dquote = false ; next char
+ else next char
+ end
+ end
+ }
+ if !ignore && !previous_continuing || !brank_flag
+ if previous_continuing
+ body << words.join("")
+ else
+ body << "\n" + words.join("")
+ end
+ end
+ previous_continuing = now_continuing ? true : nil
+ now_continuing = nil
+ }
+ return body
+ end
+
+
+ ##
+ # Continuous line checker
+
+ def continuous_line?(line)
+ continuous = false
+ if /&\s*?(!.*)?$/ =~ line
+ continuous = true
+ if comment_out?($~.pre_match)
+ continuous = false
+ end
+ end
+ return continuous
+ end
+
+ ##
+ # Comment out checker
+
+ def comment_out?(line)
+ return nil unless line
+ commentout = false
+ squote = false ; dquote = false
+ line.split("").each { |char|
+ if !(squote) && !(dquote)
+ case char
+ when "!" ; commentout = true ; break
+ when "\""; dquote = true
+ when "\'"; squote = true
+ else next
+ end
+ elsif squote
+ case char
+ when "\'"; squote = false
+ else next
+ end
+ elsif dquote
+ case char
+ when "\""; dquote = false
+ else next
+ end
+ end
+ }
+ return commentout
+ end
+
+ ##
+ # Semicolons are replaced to line feed.
+
+ def semicolon_to_linefeed(text)
+ return "" unless text
+ lines = text.split("\n")
+ lines.collect!{ |line|
+ words = line.split("")
+ commentout = false
+ squote = false ; dquote = false
+ words.collect! { |char|
+ if !(squote) && !(dquote) && !(commentout)
+ case char
+ when "!" ; commentout = true ; next char
+ when "\""; dquote = true ; next char
+ when "\'"; squote = true ; next char
+ when ";" ; "\n"
+ else next char
+ end
+ elsif commentout
+ next char
+ elsif squote
+ case char
+ when "\'"; squote = false ; next char
+ else next char
+ end
+ elsif dquote
+ case char
+ when "\""; dquote = false ; next char
+ else next char
+ end
+ end
+ }
+ words.join("")
+ }
+ return lines.join("\n")
+ end
+
+ ##
+ # Which "line" is start of block (module, program, block data, subroutine,
+ # function) statement ?
+
+ def block_start?(line)
+ return nil if !line
+
+ if line =~ /^\s*?module\s+(\w+)\s*?(!.*?)?$/i ||
+ line =~ /^\s*?program\s+(\w+)\s*?(!.*?)?$/i ||
+ line =~ /^\s*?block\s+data(\s+\w+)?\s*?(!.*?)?$/i ||
+ line =~ \
+ /^\s*?
+ (recursive|pure|elemental)?\s*?
+ subroutine\s+(\w+)\s*?(\(.*?\))?\s*?(!.*?)?$
+ /ix ||
+ line =~ \
+ /^\s*?
+ (recursive|pure|elemental)?\s*?
+ (
+ character\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | type\s*?\([\w\s]+?\)\s+
+ | integer\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | real\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | double\s+precision\s+
+ | logical\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | complex\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ )?
+ function\s+(\w+)\s*?
+ (\(.*?\))?(\s+result\((.*?)\))?\s*?(!.*?)?$
+ /ix
+ return true
+ end
+
+ return nil
+ end
+
+ ##
+ # Which "line" is end of block (module, program, block data, subroutine,
+ # function) statement ?
+
+ def block_end?(line)
+ return nil if !line
+
+ if line =~ /^\s*?end\s*?(!.*?)?$/i ||
+ line =~ /^\s*?end\s+module(\s+\w+)?\s*?(!.*?)?$/i ||
+ line =~ /^\s*?end\s+program(\s+\w+)?\s*?(!.*?)?$/i ||
+ line =~ /^\s*?end\s+block\s+data(\s+\w+)?\s*?(!.*?)?$/i ||
+ line =~ /^\s*?end\s+subroutine(\s+\w+)?\s*?(!.*?)?$/i ||
+ line =~ /^\s*?end\s+function(\s+\w+)?\s*?(!.*?)?$/i
+ return true
+ end
+
+ return nil
+ end
+
+ ##
+ # Remove "Alias for" in end of comments
+
+ def remove_trailing_alias(text)
+ return "" if !text
+ lines = text.split("\n").reverse
+ comment_block = Array.new
+ checked = false
+ lines.each do |line|
+ if !checked
+ if /^\s?#{INTERNAL_ALIAS_MES}/ =~ line ||
+ /^\s?#{EXTERNAL_ALIAS_MES}/ =~ line
+ checked = true
+ next
+ end
+ end
+ comment_block.unshift line
+ end
+ nice_lines = comment_block.join("\n")
+ nice_lines ||= ""
+ return nice_lines
+ end
+
+ ##
+ # Empty lines in header are removed
+
+ def remove_empty_head_lines(text)
+ return "" unless text
+ lines = text.split("\n")
+ header = true
+ lines.delete_if{ |line|
+ header = false if /\S/ =~ line
+ header && /^\s*?$/ =~ line
+ }
+ lines.join("\n")
+ end
+
+ ##
+ # header marker "=", "==", ... are removed
+
+ def remove_header_marker(text)
+ return text.gsub(/^\s?(=+)/, '<tt></tt>\1')
+ end
+
+ def remove_private_comments(body)
+ body.gsub!(/^\s*!--\s*?$.*?^\s*!\+\+\s*?$/m, '')
+ return body
+ end
+
+ ##
+ # Information of arguments of subroutines and functions in Fortran95
+
+ class Fortran95Definition
+
+ # Name of variable
+ #
+ attr_reader :varname
+
+ # Types of variable
+ #
+ attr_reader :types
+
+ # Initial Value
+ #
+ attr_reader :inivalue
+
+ # Suffix of array
+ #
+ attr_reader :arraysuffix
+
+ # Comments
+ #
+ attr_accessor :comment
+
+ # Flag of non documentation
+ #
+ attr_accessor :nodoc
+
+ def initialize(varname, types, inivalue, arraysuffix, comment,
+ nodoc=false)
+ @varname = varname
+ @types = types
+ @inivalue = inivalue
+ @arraysuffix = arraysuffix
+ @comment = comment
+ @nodoc = nodoc
+ end
+
+ def to_s
+ return <<-EOF
+<Fortran95Definition:
+varname=#{@varname}, types=#{types},
+inivalue=#{@inivalue}, arraysuffix=#{@arraysuffix}, nodoc=#{@nodoc},
+comment=
+#{@comment}
+>
+EOF
+ end
+
+ #
+ # If attr is included, true is returned
+ #
+ def include_attr?(attr)
+ return if !attr
+ @types.split(",").each{ |type|
+ return true if type.strip.chomp.upcase == attr.strip.chomp.upcase
+ }
+ return nil
+ end
+
+ end # End of Fortran95Definition
+
+ ##
+ # Parse string argument "text", and Return Array of Fortran95Definition
+ # object
+
+ def definition_info(text)
+ return nil unless text
+ lines = "#{text}"
+ defs = Array.new
+ comment = ""
+ trailing_comment = ""
+ under_comment_valid = false
+ lines.split("\n").each{ |line|
+ if /^\s*?!\s?(.*)/ =~ line
+ if COMMENTS_ARE_UPPER
+ comment << remove_header_marker($1)
+ comment << "\n"
+ elsif defs[-1] && under_comment_valid
+ defs[-1].comment << "\n"
+ defs[-1].comment << remove_header_marker($1)
+ end
+ next
+ elsif /^\s*?$/ =~ line
+ comment = ""
+ under_comment_valid = false
+ next
+ end
+ type = ""
+ characters = ""
+ if line =~ /^\s*?
+ (
+ character\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
+ | type\s*?\([\w\s]+?\)[\s\,]*
+ | integer\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
+ | real\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
+ | double\s+precision[\s\,]*
+ | logical\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
+ | complex\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
+ )
+ (.*?::)?
+ (.+)$
+ /ix
+ characters = $8
+ type = $1
+ type << $7.gsub(/::/, '').gsub(/^\s*?\,/, '') if $7
+ else
+ under_comment_valid = false
+ next
+ end
+ squote = false ; dquote = false ; bracket = 0
+ iniflag = false; commentflag = false
+ varname = "" ; arraysuffix = "" ; inivalue = ""
+ start_pos = defs.size
+ characters.split("").each { |char|
+ if !(squote) && !(dquote) && bracket <= 0 && !(iniflag) && !(commentflag)
+ case char
+ when "!" ; commentflag = true
+ when "(" ; bracket += 1 ; arraysuffix = char
+ when "\""; dquote = true
+ when "\'"; squote = true
+ when "=" ; iniflag = true ; inivalue << char
+ when ","
+ defs << Fortran95Definition.new(varname, type, inivalue, arraysuffix, comment)
+ varname = "" ; arraysuffix = "" ; inivalue = ""
+ under_comment_valid = true
+ when " " ; next
+ else ; varname << char
+ end
+ elsif commentflag
+ comment << remove_header_marker(char)
+ trailing_comment << remove_header_marker(char)
+ elsif iniflag
+ if dquote
+ case char
+ when "\"" ; dquote = false ; inivalue << char
+ else ; inivalue << char
+ end
+ elsif squote
+ case char
+ when "\'" ; squote = false ; inivalue << char
+ else ; inivalue << char
+ end
+ elsif bracket > 0
+ case char
+ when "(" ; bracket += 1 ; inivalue << char
+ when ")" ; bracket -= 1 ; inivalue << char
+ else ; inivalue << char
+ end
+ else
+ case char
+ when ","
+ defs << Fortran95Definition.new(varname, type, inivalue, arraysuffix, comment)
+ varname = "" ; arraysuffix = "" ; inivalue = ""
+ iniflag = false
+ under_comment_valid = true
+ when "(" ; bracket += 1 ; inivalue << char
+ when "\""; dquote = true ; inivalue << char
+ when "\'"; squote = true ; inivalue << char
+ when "!" ; commentflag = true
+ else ; inivalue << char
+ end
+ end
+ elsif !(squote) && !(dquote) && bracket > 0
+ case char
+ when "(" ; bracket += 1 ; arraysuffix << char
+ when ")" ; bracket -= 1 ; arraysuffix << char
+ else ; arraysuffix << char
+ end
+ elsif squote
+ case char
+ when "\'"; squote = false ; inivalue << char
+ else ; inivalue << char
+ end
+ elsif dquote
+ case char
+ when "\""; dquote = false ; inivalue << char
+ else ; inivalue << char
+ end
+ end
+ }
+ defs << Fortran95Definition.new(varname, type, inivalue, arraysuffix, comment)
+ if trailing_comment =~ /^:nodoc:/
+ defs[start_pos..-1].collect!{ |defitem|
+ defitem.nodoc = true
+ }
+ end
+ varname = "" ; arraysuffix = "" ; inivalue = ""
+ comment = ""
+ under_comment_valid = true
+ trailing_comment = ""
+ }
+ return defs
+ end
+
+end
+
diff --git a/lib/rdoc/parsers/parse_rb.rb b/lib/rdoc/parser/ruby.rb
index a68094152c..e00d727f9e 100644
--- a/lib/rdoc/parsers/parse_rb.rb
+++ b/lib/rdoc/parser/ruby.rb
@@ -1,34 +1,28 @@
-#!/usr/local/bin/ruby
-
-# Parse a Ruby source file, building a set of objects
-# representing the modules, classes, methods,
-# requires, and includes we find (these classes
-# are defined in code_objects.rb).
-
+##
# This file contains stuff stolen outright from:
#
# rtags.rb -
# ruby-lex.rb - ruby lexcal analyzer
# ruby-token.rb - ruby tokens
-# by Keiju ISHITSUKA (Nippon Rational Inc.)
+# by Keiju ISHITSUKA (Nippon Rational Inc.)
#
-require "e2mmap"
-require "irb/slex"
+require 'e2mmap'
+require 'irb/slex'
-require "rdoc/code_objects"
-require "rdoc/tokenstream"
-
-require "rdoc/markup/preprocess"
-
-require "rdoc/parsers/parserfactory"
+require 'rdoc/code_objects'
+require 'rdoc/tokenstream'
+require 'rdoc/markup/preprocess'
+require 'rdoc/parser'
$TOKEN_DEBUG ||= nil
#$TOKEN_DEBUG = $DEBUG_RDOC
+##
# Definitions of all tokens involved in the lexical analysis
-module RubyToken
+module RDoc::RubyToken
+
EXPR_BEG = :EXPR_BEG
EXPR_MID = :EXPR_MID
EXPR_END = :EXPR_END
@@ -39,7 +33,10 @@ module RubyToken
class Token
NO_TEXT = "??".freeze
+
attr_accessor :text
+ attr_reader :line_no
+ attr_reader :char_no
def initialize(line_no, char_no)
@line_no = line_no
@@ -47,14 +44,22 @@ module RubyToken
@text = NO_TEXT
end
- # Because we're used in contexts that expect to return a token,
- # we set the text string and then return ourselves
+ def ==(other)
+ self.class == other.class and
+ other.line_no == @line_no and
+ other.char_no == @char_no and
+ other.text == @text
+ end
+
+ ##
+ # Because we're used in contexts that expect to return a token, we set the
+ # text string and then return ourselves
+
def set_text(text)
@text = text
self
end
- attr_reader :line_no, :char_no
end
class TkNode < Token
@@ -88,7 +93,7 @@ module RubyToken
class TkOPASGN < TkOp
def initialize(line_no, char_no, op)
super(line_no, char_no)
- op = TkReading2Token[op] unless op.kind_of?(Symbol)
+ op = TkReading2Token[op] unless Symbol === op
@op = op
end
attr :op
@@ -114,10 +119,8 @@ module RubyToken
tk = nil
case token
when String, Symbol
- source = token.kind_of?(String) ? TkReading2Token : TkSymbol2Token
- if (tk = source[token]).nil?
- fail TkReading2TokenNoKey, token
- end
+ source = String === token ? TkReading2Token : TkSymbol2Token
+ raise TkReading2TokenNoKey, token if (tk = source[token]).nil?
tk = Token(tk[0], value)
else
tk = if (token.ancestors & [TkId, TkVal, TkOPASGN, TkUnknownChar]).empty?
@@ -132,38 +135,38 @@ module RubyToken
TokenDefinitions = [
[:TkCLASS, TkKW, "class", EXPR_CLASS],
[:TkMODULE, TkKW, "module", EXPR_BEG],
- [:TkDEF, TkKW, "def", EXPR_FNAME],
+ [:TkDEF, TkKW, "def", EXPR_FNAME],
[:TkUNDEF, TkKW, "undef", EXPR_FNAME],
[:TkBEGIN, TkKW, "begin", EXPR_BEG],
[:TkRESCUE, TkKW, "rescue", EXPR_MID],
[:TkENSURE, TkKW, "ensure", EXPR_BEG],
- [:TkEND, TkKW, "end", EXPR_END],
+ [:TkEND, TkKW, "end", EXPR_END],
[:TkIF, TkKW, "if", EXPR_BEG, :TkIF_MOD],
[:TkUNLESS, TkKW, "unless", EXPR_BEG, :TkUNLESS_MOD],
- [:TkTHEN, TkKW, "then", EXPR_BEG],
+ [:TkTHEN, TkKW, "then", EXPR_BEG],
[:TkELSIF, TkKW, "elsif", EXPR_BEG],
- [:TkELSE, TkKW, "else", EXPR_BEG],
- [:TkCASE, TkKW, "case", EXPR_BEG],
- [:TkWHEN, TkKW, "when", EXPR_BEG],
+ [:TkELSE, TkKW, "else", EXPR_BEG],
+ [:TkCASE, TkKW, "case", EXPR_BEG],
+ [:TkWHEN, TkKW, "when", EXPR_BEG],
[:TkWHILE, TkKW, "while", EXPR_BEG, :TkWHILE_MOD],
[:TkUNTIL, TkKW, "until", EXPR_BEG, :TkUNTIL_MOD],
- [:TkFOR, TkKW, "for", EXPR_BEG],
+ [:TkFOR, TkKW, "for", EXPR_BEG],
[:TkBREAK, TkKW, "break", EXPR_END],
- [:TkNEXT, TkKW, "next", EXPR_END],
- [:TkREDO, TkKW, "redo", EXPR_END],
+ [:TkNEXT, TkKW, "next", EXPR_END],
+ [:TkREDO, TkKW, "redo", EXPR_END],
[:TkRETRY, TkKW, "retry", EXPR_END],
- [:TkIN, TkKW, "in", EXPR_BEG],
- [:TkDO, TkKW, "do", EXPR_BEG],
+ [:TkIN, TkKW, "in", EXPR_BEG],
+ [:TkDO, TkKW, "do", EXPR_BEG],
[:TkRETURN, TkKW, "return", EXPR_MID],
[:TkYIELD, TkKW, "yield", EXPR_END],
[:TkSUPER, TkKW, "super", EXPR_END],
- [:TkSELF, TkKW, "self", EXPR_END],
- [:TkNIL, TkKW, "nil", EXPR_END],
- [:TkTRUE, TkKW, "true", EXPR_END],
+ [:TkSELF, TkKW, "self", EXPR_END],
+ [:TkNIL, TkKW, "nil", EXPR_END],
+ [:TkTRUE, TkKW, "true", EXPR_END],
[:TkFALSE, TkKW, "false", EXPR_END],
- [:TkAND, TkKW, "and", EXPR_BEG],
- [:TkOR, TkKW, "or", EXPR_BEG],
- [:TkNOT, TkKW, "not", EXPR_BEG],
+ [:TkAND, TkKW, "and", EXPR_BEG],
+ [:TkOR, TkKW, "or", EXPR_BEG],
+ [:TkNOT, TkKW, "not", EXPR_BEG],
[:TkIF_MOD, TkKW],
[:TkUNLESS_MOD, TkKW],
[:TkWHILE_MOD, TkKW],
@@ -171,14 +174,14 @@ module RubyToken
[:TkALIAS, TkKW, "alias", EXPR_FNAME],
[:TkDEFINED, TkKW, "defined?", EXPR_END],
[:TklBEGIN, TkKW, "BEGIN", EXPR_END],
- [:TklEND, TkKW, "END", EXPR_END],
+ [:TklEND, TkKW, "END", EXPR_END],
[:Tk__LINE__, TkKW, "__LINE__", EXPR_END],
[:Tk__FILE__, TkKW, "__FILE__", EXPR_END],
[:TkIDENTIFIER, TkId],
- [:TkFID, TkId],
- [:TkGVAR, TkId],
- [:TkIVAR, TkId],
+ [:TkFID, TkId],
+ [:TkGVAR, TkId],
+ [:TkIVAR, TkId],
[:TkCONSTANT, TkId],
[:TkINTEGER, TkVal],
@@ -196,28 +199,28 @@ module RubyToken
[:TkUPLUS, TkOp, "+@"],
[:TkUMINUS, TkOp, "-@"],
- [:TkPOW, TkOp, "**"],
- [:TkCMP, TkOp, "<=>"],
- [:TkEQ, TkOp, "=="],
- [:TkEQQ, TkOp, "==="],
- [:TkNEQ, TkOp, "!="],
- [:TkGEQ, TkOp, ">="],
- [:TkLEQ, TkOp, "<="],
+ [:TkPOW, TkOp, "**"],
+ [:TkCMP, TkOp, "<=>"],
+ [:TkEQ, TkOp, "=="],
+ [:TkEQQ, TkOp, "==="],
+ [:TkNEQ, TkOp, "!="],
+ [:TkGEQ, TkOp, ">="],
+ [:TkLEQ, TkOp, "<="],
[:TkANDOP, TkOp, "&&"],
- [:TkOROP, TkOp, "||"],
+ [:TkOROP, TkOp, "||"],
[:TkMATCH, TkOp, "=~"],
[:TkNMATCH, TkOp, "!~"],
- [:TkDOT2, TkOp, ".."],
- [:TkDOT3, TkOp, "..."],
- [:TkAREF, TkOp, "[]"],
- [:TkASET, TkOp, "[]="],
+ [:TkDOT2, TkOp, ".."],
+ [:TkDOT3, TkOp, "..."],
+ [:TkAREF, TkOp, "[]"],
+ [:TkASET, TkOp, "[]="],
[:TkLSHFT, TkOp, "<<"],
[:TkRSHFT, TkOp, ">>"],
[:TkCOLON2, TkOp],
[:TkCOLON3, TkOp],
-# [:OPASGN, TkOp], # +=, -= etc. #
+# [:OPASGN, TkOp], # +=, -= etc. #
[:TkASSOC, TkOp, "=>"],
- [:TkQUESTION, TkOp, "?"], #?
+ [:TkQUESTION, TkOp, "?"], #?
[:TkCOLON, TkOp, ":"], #:
[:TkfLPAREN], # func( #
@@ -227,13 +230,13 @@ module RubyToken
[:TkAMPER], # &arg #
[:TkSYMBOL, TkId], # :SYMBOL
[:TkSYMBEG, TkId],
- [:TkGT, TkOp, ">"],
- [:TkLT, TkOp, "<"],
- [:TkPLUS, TkOp, "+"],
+ [:TkGT, TkOp, ">"],
+ [:TkLT, TkOp, "<"],
+ [:TkPLUS, TkOp, "+"],
[:TkMINUS, TkOp, "-"],
- [:TkMULT, TkOp, "*"],
- [:TkDIV, TkOp, "/"],
- [:TkMOD, TkOp, "%"],
+ [:TkMULT, TkOp, "*"],
+ [:TkDIV, TkOp, "/"],
+ [:TkMOD, TkOp, "%"],
[:TkBITOR, TkOp, "|"],
[:TkBITXOR, TkOp, "^"],
[:TkBITAND, TkOp, "&"],
@@ -243,7 +246,7 @@ module RubyToken
[:TkBACKQUOTE, TkOp, "`"],
[:TkASSIGN, Token, "="],
- [:TkDOT, Token, "."],
+ [:TkDOT, Token, "."],
[:TkLPAREN, Token, "("], #(exp)
[:TkLBRACK, Token, "["], #[arry]
[:TkLBRACE, Token, "{"], #{hash}
@@ -259,7 +262,7 @@ module RubyToken
[:TkEND_OF_SCRIPT],
[:TkBACKSLASH, TkUnknownChar, "\\"],
- [:TkAT, TkUnknownChar, "@"],
+ [:TkAT, TkUnknownChar, "@"],
[:TkDOLLAR, TkUnknownChar, "\$"], #"
]
@@ -268,14 +271,13 @@ module RubyToken
TkReading2Token = {}
TkSymbol2Token = {}
- def RubyToken.def_token(token_n, super_token = Token, reading = nil, *opts)
- token_n = token_n.id2name unless token_n.kind_of?(String)
- if RubyToken.const_defined?(token_n)
- fail AlreadyDefinedToken, token_n
- end
+ def self.def_token(token_n, super_token = Token, reading = nil, *opts)
+ token_n = token_n.id2name unless String === token_n
+
+ fail AlreadyDefinedToken, token_n if const_defined?(token_n)
token_c = Class.new super_token
- RubyToken.const_set token_n, token_c
+ const_set token_n, token_c
# token_c.inspect
if reading
@@ -283,9 +285,9 @@ module RubyToken
fail TkReading2TokenDuplicateError, token_n, reading
end
if opts.empty?
- TkReading2Token[reading] = [token_c]
+ TkReading2Token[reading] = [token_c]
else
- TkReading2Token[reading] = [token_c].concat(opts)
+ TkReading2Token[reading] = [token_c].concat(opts)
end
end
TkSymbol2Token[token_n.intern] = token_c
@@ -306,12 +308,12 @@ module RubyToken
end
+##
# Lexical analyzer for Ruby source
-class RubyLex
+class RDoc::RubyLex
- ######################################################################
- #
+ ##
# Read an input stream character by character. We allow for unlimited
# ungetting of characters just read.
#
@@ -432,16 +434,16 @@ class RubyLex
def_exception(:TkReading2TokenNoKey, "key nothing(key='%s')")
def_exception(:TkSymbol2TokenNoKey, "key nothing(key='%s')")
def_exception(:TkReading2TokenDuplicateError,
- "key duplicate(token_n='%s', key='%s')")
+ "key duplicate(token_n='%s', key='%s')")
def_exception(:SyntaxError, "%s")
- include RubyToken
+ include RDoc::RubyToken
include IRB
attr_reader :continue
attr_reader :lex_state
- def RubyLex.debug?
+ def self.debug?
false
end
@@ -519,13 +521,13 @@ class RubyLex
end
def lex
- until (((tk = token).kind_of?(TkNL) || tk.kind_of?(TkEND_OF_SCRIPT)) &&
- !@continue or
- tk.nil?)
+ until (TkNL === (tk = token) or TkEND_OF_SCRIPT === tk) and
+ not @continue or tk.nil?
end
+
line = get_read
- if line == "" and tk.kind_of?(TkEND_OF_SCRIPT) || tk.nil?
+ if line == "" and TkEND_OF_SCRIPT === tk or tk.nil? then
nil
else
line
@@ -536,13 +538,13 @@ class RubyLex
set_token_position(line_no, char_no)
begin
begin
- tk = @OP.match(self)
- @space_seen = tk.kind_of?(TkSPACE)
+ tk = @OP.match(self)
+ @space_seen = TkSPACE === tk
rescue SyntaxError
- abort if @exception_on_syntax_error
- tk = TkError.new(line_no, char_no)
+ abort if @exception_on_syntax_error
+ tk = TkError.new(line_no, char_no)
end
- end while @skip_space and tk.kind_of?(TkSPACE)
+ end while @skip_space and TkSPACE === tk
if @read_auto_clean_up
get_read
end
@@ -636,23 +638,23 @@ class RubyLex
end
@OP.def_rule("\n") do
- print "\\n\n" if RubyLex.debug?
+ print "\\n\n" if RDoc::RubyLex.debug?
case @lex_state
when EXPR_BEG, EXPR_FNAME, EXPR_DOT
- @continue = TRUE
+ @continue = TRUE
else
- @continue = FALSE
- @lex_state = EXPR_BEG
+ @continue = FALSE
+ @lex_state = EXPR_BEG
end
Token(TkNL).set_text("\n")
end
@OP.def_rules("*", "**",
- "!", "!=", "!~",
- "=", "==", "===",
- "=~", "<=>",
- "<", "<=",
- ">", ">=", ">>") do
+ "!", "!=", "!~",
+ "=", "==", "===",
+ "=~", "<=>",
+ "<", "<=",
+ ">", ">=", ">>") do
|op, io|
@lex_state = EXPR_BEG
Token(op).set_text(op)
@@ -662,11 +664,11 @@ class RubyLex
|op, io|
tk = nil
if @lex_state != EXPR_END && @lex_state != EXPR_CLASS &&
- (@lex_state != EXPR_ARG || @space_seen)
- c = peek(0)
- if /[-\w_\"\'\`]/ =~ c
- tk = identify_here_document
- end
+ (@lex_state != EXPR_ARG || @space_seen)
+ c = peek(0)
+ if /[-\w_\"\'\`]/ =~ c
+ tk = identify_here_document
+ end
end
if !tk
@lex_state = EXPR_BEG
@@ -683,32 +685,32 @@ class RubyLex
@OP.def_rules("`") do
|op, io|
if @lex_state == EXPR_FNAME
- Token(op).set_text(op)
+ Token(op).set_text(op)
else
- identify_string(op)
+ identify_string(op)
end
end
@OP.def_rules('?') do
|op, io|
if @lex_state == EXPR_END
- @lex_state = EXPR_BEG
- Token(TkQUESTION).set_text(op)
+ @lex_state = EXPR_BEG
+ Token(TkQUESTION).set_text(op)
else
- ch = getc
- if @lex_state == EXPR_ARG && ch !~ /\s/
- ungetc
- @lex_state = EXPR_BEG;
- Token(TkQUESTION).set_text(op)
- else
+ ch = getc
+ if @lex_state == EXPR_ARG && ch !~ /\s/
+ ungetc
+ @lex_state = EXPR_BEG
+ Token(TkQUESTION).set_text(op)
+ else
str = op
str << ch
- if (ch == '\\') #'
- str << read_escape
- end
- @lex_state = EXPR_END
- Token(TkINTEGER).set_text(str)
- end
+ if (ch == '\\') #'
+ str << read_escape
+ end
+ @lex_state = EXPR_END
+ Token(TkINTEGER).set_text(str)
+ end
end
end
@@ -719,7 +721,7 @@ class RubyLex
end
@OP.def_rules("+=", "-=", "*=", "**=",
- "&=", "|=", "^=", "<<=", ">>=", "||=", "&&=") do
+ "&=", "|=", "^=", "<<=", ">>=", "||=", "&&=") do
|op, io|
@lex_state = EXPR_BEG
op =~ /^(.*)=$/
@@ -737,30 +739,30 @@ class RubyLex
@OP.def_rules("+", "-") do
|op, io|
catch(:RET) do
- if @lex_state == EXPR_ARG
- if @space_seen and peek(0) =~ /[0-9]/
- throw :RET, identify_number(op)
- else
- @lex_state = EXPR_BEG
- end
- elsif @lex_state != EXPR_END and peek(0) =~ /[0-9]/
- throw :RET, identify_number(op)
- else
- @lex_state = EXPR_BEG
- end
- Token(op).set_text(op)
+ if @lex_state == EXPR_ARG
+ if @space_seen and peek(0) =~ /[0-9]/
+ throw :RET, identify_number(op)
+ else
+ @lex_state = EXPR_BEG
+ end
+ elsif @lex_state != EXPR_END and peek(0) =~ /[0-9]/
+ throw :RET, identify_number(op)
+ else
+ @lex_state = EXPR_BEG
+ end
+ Token(op).set_text(op)
end
end
@OP.def_rule(".") do
@lex_state = EXPR_BEG
if peek(0) =~ /[0-9]/
- ungetc
- identify_number("")
+ ungetc
+ identify_number("")
else
- # for obj.if
- @lex_state = EXPR_DOT
- Token(TkDOT).set_text(".")
+ # for obj.if
+ @lex_state = EXPR_DOT
+ Token(TkDOT).set_text(".")
end
end
@@ -783,23 +785,22 @@ class RubyLex
@OP.def_rule(":") do
if @lex_state == EXPR_END || peek(0) =~ /\s/
- @lex_state = EXPR_BEG
- tk = Token(TkCOLON)
+ @lex_state = EXPR_BEG
+ tk = Token(TkCOLON)
else
- @lex_state = EXPR_FNAME;
- tk = Token(TkSYMBEG)
+ @lex_state = EXPR_FNAME
+ tk = Token(TkSYMBEG)
end
tk.set_text(":")
end
@OP.def_rule("::") do
-# p @lex_state.id2name, @space_seen
if @lex_state == EXPR_BEG or @lex_state == EXPR_ARG && @space_seen
- @lex_state = EXPR_BEG
- tk = Token(TkCOLON3)
+ @lex_state = EXPR_BEG
+ tk = Token(TkCOLON3)
else
- @lex_state = EXPR_DOT
- tk = Token(TkCOLON2)
+ @lex_state = EXPR_DOT
+ tk = Token(TkCOLON2)
end
tk.set_text("::")
end
@@ -807,15 +808,15 @@ class RubyLex
@OP.def_rule("/") do
|op, io|
if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
- identify_string(op)
+ identify_string(op)
elsif peek(0) == '='
- getc
- @lex_state = EXPR_BEG
- Token(TkOPASGN, :/).set_text("/=") #")
+ getc
+ @lex_state = EXPR_BEG
+ Token(TkOPASGN, :/).set_text("/=") #")
elsif @lex_state == EXPR_ARG and @space_seen and peek(0) !~ /\s/
- identify_string(op)
+ identify_string(op)
else
- @lex_state = EXPR_BEG
+ @lex_state = EXPR_BEG
Token("/").set_text(op)
end
end
@@ -825,11 +826,6 @@ class RubyLex
Token("^").set_text("^")
end
- # @OP.def_rules("^=") do
- # @lex_state = EXPR_BEG
- # Token(TkOPASGN, :^)
- # end
-
@OP.def_rules(",", ";") do
|op, io|
@lex_state = EXPR_BEG
@@ -849,11 +845,11 @@ class RubyLex
@OP.def_rule("(") do
@indent += 1
if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
- @lex_state = EXPR_BEG
- tk = Token(TkfLPAREN)
+ @lex_state = EXPR_BEG
+ tk = Token(TkfLPAREN)
else
- @lex_state = EXPR_BEG
- tk = Token(TkLPAREN)
+ @lex_state = EXPR_BEG
+ tk = Token(TkLPAREN)
end
tk.set_text("(")
end
@@ -869,16 +865,16 @@ class RubyLex
@OP.def_rule("[") do
@indent += 1
if @lex_state == EXPR_FNAME
- t = Token(TkfLBRACK)
+ t = Token(TkfLBRACK)
else
- if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
- t = Token(TkLBRACK)
- elsif @lex_state == EXPR_ARG && @space_seen
- t = Token(TkLBRACK)
- else
- t = Token(TkfLBRACK)
- end
- @lex_state = EXPR_BEG
+ if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
+ t = Token(TkLBRACK)
+ elsif @lex_state == EXPR_ARG && @space_seen
+ t = Token(TkLBRACK)
+ else
+ t = Token(TkfLBRACK)
+ end
+ @lex_state = EXPR_BEG
end
t.set_text("[")
end
@@ -886,9 +882,9 @@ class RubyLex
@OP.def_rule("{") do
@indent += 1
if @lex_state != EXPR_END && @lex_state != EXPR_ARG
- t = Token(TkLBRACE)
+ t = Token(TkLBRACE)
else
- t = Token(TkfLBRACE)
+ t = Token(TkfLBRACE)
end
@lex_state = EXPR_BEG
t.set_text("{")
@@ -896,27 +892,27 @@ class RubyLex
@OP.def_rule('\\') do #'
if getc == "\n"
- @space_seen = true
- @continue = true
- Token(TkSPACE).set_text("\\\n")
+ @space_seen = true
+ @continue = true
+ Token(TkSPACE).set_text("\\\n")
else
- ungetc
- Token("\\").set_text("\\") #"
+ ungetc
+ Token("\\").set_text("\\") #"
end
end
@OP.def_rule('%') do
|op, io|
if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
- identify_quotation('%')
+ identify_quotation('%')
elsif peek(0) == '='
- getc
- Token(TkOPASGN, "%").set_text("%=")
+ getc
+ Token(TkOPASGN, "%").set_text("%=")
elsif @lex_state == EXPR_ARG and @space_seen and peek(0) !~ /\s/
- identify_quotation('%')
+ identify_quotation('%')
else
- @lex_state = EXPR_BEG
- Token("%").set_text("%")
+ @lex_state = EXPR_BEG
+ Token("%").set_text("%")
end
end
@@ -926,40 +922,28 @@ class RubyLex
@OP.def_rule('@') do
if peek(0) =~ /[@\w_]/
- ungetc
- identify_identifier
+ ungetc
+ identify_identifier
else
- Token("@").set_text("@")
+ Token("@").set_text("@")
end
end
- # @OP.def_rule("def", proc{|op, io| /\s/ =~ io.peek(0)}) do
- # |op, io|
- # @indent += 1
- # @lex_state = EXPR_FNAME
- # # @lex_state = EXPR_END
- # # until @rests[0] == "\n" or @rests[0] == ";"
- # # rests.shift
- # # end
- # end
-
@OP.def_rule("__END__", proc{@prev_char_no == 0 && peek(0) =~ /[\r\n]/}) do
throw :eof
end
@OP.def_rule("") do
|op, io|
- printf "MATCH: start %s: %s\n", op, io.inspect if RubyLex.debug?
+ printf "MATCH: start %s: %s\n", op, io.inspect if RDoc::RubyLex.debug?
if peek(0) =~ /[0-9]/
- t = identify_number("")
+ t = identify_number("")
elsif peek(0) =~ /[\w_]/
- t = identify_identifier
+ t = identify_identifier
end
- printf "MATCH: end %s: %s\n", op, io.inspect if RubyLex.debug?
+ printf "MATCH: end %s: %s\n", op, io.inspect if RDoc::RubyLex.debug?
t
end
-
- p @OP if RubyLex.debug?
end
def identify_gvar
@@ -1003,7 +987,7 @@ class RubyLex
token.concat getc if peek(0) == "@"
while (ch = getc) =~ /\w|_/
- print ":", ch, ":" if RubyLex.debug?
+ print ":", ch, ":" if RDoc::RubyLex.debug?
token.concat ch
end
ungetc
@@ -1024,38 +1008,38 @@ class RubyLex
end
if @lex_state != EXPR_DOT
- print token, "\n" if RubyLex.debug?
+ print token, "\n" if RDoc::RubyLex.debug?
token_c, *trans = TkReading2Token[token]
if token_c
- # reserved word?
-
- if (@lex_state != EXPR_BEG &&
- @lex_state != EXPR_FNAME &&
- trans[1])
- # modifiers
- token_c = TkSymbol2Token[trans[1]]
- @lex_state = trans[0]
- else
- if @lex_state != EXPR_FNAME
- if ENINDENT_CLAUSE.include?(token)
- @indent += 1
- elsif DEINDENT_CLAUSE.include?(token)
- @indent -= 1
- end
- @lex_state = trans[0]
- else
- @lex_state = EXPR_END
- end
- end
- return Token(token_c, token).set_text(token)
+ # reserved word?
+
+ if (@lex_state != EXPR_BEG &&
+ @lex_state != EXPR_FNAME &&
+ trans[1])
+ # modifiers
+ token_c = TkSymbol2Token[trans[1]]
+ @lex_state = trans[0]
+ else
+ if @lex_state != EXPR_FNAME
+ if ENINDENT_CLAUSE.include?(token)
+ @indent += 1
+ elsif DEINDENT_CLAUSE.include?(token)
+ @indent -= 1
+ end
+ @lex_state = trans[0]
+ else
+ @lex_state = EXPR_END
+ end
+ end
+ return Token(token_c, token).set_text(token)
end
end
if @lex_state == EXPR_FNAME
@lex_state = EXPR_END
if peek(0) == '='
- token.concat getc
+ token.concat getc
end
elsif @lex_state == EXPR_BEG || @lex_state == EXPR_DOT
@lex_state = EXPR_ARG
@@ -1082,13 +1066,13 @@ class RubyLex
lt = ch
quoted = ""
while (c = getc) && c != lt
- quoted.concat c
+ quoted.concat c
end
else
lt = '"'
quoted = ch.dup
while (c = getc) && c =~ /\w/
- quoted.concat c
+ quoted.concat c
end
ungetc
end
@@ -1100,9 +1084,9 @@ class RubyLex
reserve << ch
if ch == "\\" #"
ch = getc
- reserve << ch
+ reserve << ch
elsif ch == "\n"
- break
+ break
end
end
@@ -1178,25 +1162,25 @@ class RubyLex
str << ch
when allow_point && "."
- type = TkFLOAT
- if peek(0) !~ /[0-9]/
- ungetc
- break
- end
+ type = TkFLOAT
+ if peek(0) !~ /[0-9]/
+ ungetc
+ break
+ end
str << ch
- allow_point = false
+ allow_point = false
when allow_e && "e", allow_e && "E"
str << ch
- type = TkFLOAT
- if peek(0) =~ /[+-]/
- str << getc
- end
- allow_e = false
- allow_point = false
+ type = TkFLOAT
+ if peek(0) =~ /[+-]/
+ str << getc
+ end
+ allow_e = false
+ allow_point = false
else
- ungetc
- break
+ ungetc
+ break
end
end
Token(type).set_text(str)
@@ -1214,8 +1198,8 @@ class RubyLex
nest = 0
begin
while ch = getc
- str << ch
- if @quoted == ch
+ str << ch
+ if @quoted == ch
if nest == 0
break
else
@@ -1223,7 +1207,7 @@ class RubyLex
end
elsif opener == ch
nest += 1
- elsif @ltype != "'" && @ltype != "]" and ch == "#"
+ elsif @ltype != "'" && @ltype != "]" and ch == "#"
ch = getc
if ch == "{"
subtype = true
@@ -1231,19 +1215,19 @@ class RubyLex
else
ungetc(ch)
end
- elsif ch == '\\' #'
- str << read_escape
- end
+ elsif ch == '\\' #'
+ str << read_escape
+ end
end
if @ltype == "/"
- if peek(0) =~ /i|o|n|e|s/
- str << getc
- end
+ if peek(0) =~ /i|o|n|e|s/
+ str << getc
+ end
end
if subtype
- Token(DLtype2Token[ltype], str)
+ Token(DLtype2Token[ltype], str)
else
- Token(Ltype2Token[ltype], str)
+ Token(Ltype2Token[ltype], str)
end.set_text(str)
ensure
@ltype = nil
@@ -1296,49 +1280,49 @@ class RubyLex
when /[0-7]/
ungetc ch
3.times do
- case ch = getc
- when /[0-7]/
- when nil
- break
- else
- ungetc
- break
- end
+ case ch = getc
+ when /[0-7]/
+ when nil
+ break
+ else
+ ungetc
+ break
+ end
res << ch
end
when "x"
res << ch
2.times do
- case ch = getc
- when /[0-9a-fA-F]/
- when nil
- break
- else
- ungetc
- break
- end
+ case ch = getc
+ when /[0-9a-fA-F]/
+ when nil
+ break
+ else
+ ungetc
+ break
+ end
res << ch
end
when "M"
res << ch
if (ch = getc) != '-'
- ungetc
+ ungetc
else
res << ch
- if (ch = getc) == "\\" #"
+ if (ch = getc) == "\\" #"
res << ch
- res << read_escape
+ res << read_escape
else
res << ch
- end
+ end
end
when "C", "c" #, "^"
res << ch
if ch == "C" and (ch = getc) != "-"
- ungetc
+ ungetc
else
res << ch
if (ch = getc) == "\\" #"
@@ -1356,74 +1340,179 @@ class RubyLex
end
##
-# Extract code elements from a source file, returning a TopLevel object
+# Extracts code elements from a source file returning a TopLevel object
# containing the constituent file elements.
#
# This file is based on rtags
+#
+# RubyParser understands how to document:
+# * classes
+# * modules
+# * methods
+# * constants
+# * aliases
+# * private, public, protected
+# * private_class_function, public_class_function
+# * module_function
+# * attr, attr_reader, attr_writer, attr_accessor
+# * extra accessors given on the command line
+# * metaprogrammed methods
+# * require
+# * include
+#
+# == Method Arguments
+#
+#--
+# NOTE: I don't think this works, needs tests, remove the paragraph following
+# this block when known to work
+#
+# The parser extracts the arguments from the method definition. You can
+# override this with a custom argument definition using the :args: directive:
+#
+# ##
+# # This method tries over and over until it is tired
+#
+# def go_go_go(thing_to_try, tries = 10) # :args: thing_to_try
+# puts thing_to_try
+# go_go_go thing_to_try, tries - 1
+# end
+#
+# If you have a more-complex set of overrides you can use the :call-seq:
+# directive:
+#++
+#
+# The parser extracts the arguments from the method definition. You can
+# override this with a custom argument definition using the :call-seq:
+# directive:
+#
+# ##
+# # This method can be called with a range or an offset and length
+# #
+# # :call-seq:
+# # my_method(Range)
+# # my_method(offset, length)
+#
+# def my_method(*args)
+# end
+#
+# The parser extracts +yield+ expressions from method bodies to gather the
+# yielded argument names. If your method manually calls a block instead of
+# yielding or you want to override the discovered argument names use
+# the :yields: directive:
+#
+# ##
+# # My method is awesome
+#
+# def my_method(&block) # :yields: happy, times
+# block.call 1, 2
+# end
+#
+# == Metaprogrammed Methods
+#
+# To pick up a metaprogrammed method, the parser looks for a comment starting
+# with '##' before an identifier:
+#
+# ##
+# # This is a meta-programmed method!
+#
+# add_my_method :meta_method, :arg1, :arg2
+#
+# The parser looks at the token after the identifier to determine the name, in
+# this example, :meta_method. If a name cannot be found, a warning is printed
+# and 'unknown is used.
+#
+# You can force the name of a method using the :method: directive:
+#
+# ##
+# # :method: woo_hoo!
+#
+# By default, meta-methods are instance methods. To indicate that a method is
+# a singleton method instead use the :singleton-method: directive:
+#
+# ##
+# # :singleton-method:
+#
+# You can also use the :singleton-method: directive with a name:
+#
+# ##
+# # :singleton-method: woo_hoo!
+#
+# == Hidden methods
+#
+# You can provide documentation for methods that don't appear using
+# the :method: and :singleton-method: directives:
+#
+# ##
+# # :method: ghost_method
+# # There is a method here, but you can't see it!
+#
+# ##
+# # this is a comment for a regular method
+#
+# def regular_method() end
+#
+# Note that by default, the :method: directive will be ignored if there is a
+# standard rdocable item following it.
-class RDoc::RubyParser
+class RDoc::Parser::Ruby < RDoc::Parser
- include RubyToken
- include RDoc::TokenStream
+ parse_files_matching(/\.rbw?$/)
- extend RDoc::ParserFactory
+ include RDoc::RubyToken
+ include RDoc::TokenStream
- parse_files_matching(/\.rbw?$/)
+ NORMAL = "::"
+ SINGLE = "<<"
def initialize(top_level, file_name, content, options, stats)
- @options = options
- @stats = stats
+ super
+
@size = 0
@token_listeners = nil
- @input_file_name = file_name
- @scanner = RubyLex.new content, @options
+ @scanner = RDoc::RubyLex.new content, @options
@scanner.exception_on_syntax_error = false
- @top_level = top_level
- @progress = $stderr unless options.quiet
+
+ reset
end
- def scan
- @tokens = []
- @unget_read = []
- @read = []
- catch(:eof) do
- catch(:enddoc) do
- begin
- parse_toplevel_statements(@top_level)
- rescue Exception => e
- $stderr.puts "\n\n"
- $stderr.puts "RDoc failure in #@input_file_name at or around " +
- "line #{@scanner.line_no} column #{@scanner.char_no}"
- $stderr.puts
- $stderr.puts "Before reporting this, could you check that the file"
- $stderr.puts "you're documenting compiles cleanly--RDoc is not a"
- $stderr.puts "full Ruby parser, and gets confused easily if fed"
- $stderr.puts "invalid programs."
- $stderr.puts
- $stderr.puts "The internal error was:\n\n"
+ def add_token_listener(obj)
+ @token_listeners ||= []
+ @token_listeners << obj
+ end
- e.set_backtrace(e.backtrace[0,4])
- raise
+ ##
+ # Look for the first comment in a file that isn't a shebang line.
+
+ def collect_first_comment
+ skip_tkspace
+ res = ''
+ first_line = true
+
+ tk = get_tk
+
+ while TkCOMMENT === tk
+ if first_line and tk.text =~ /\A#!/ then
+ skip_tkspace
+ tk = get_tk
+ elsif first_line and tk.text =~ /\A#\s*-\*-/ then
+ first_line = false
+ skip_tkspace
+ tk = get_tk
+ else
+ first_line = false
+ res << tk.text << "\n"
+ tk = get_tk
+
+ if TkNL === tk then
+ skip_tkspace false
+ tk = get_tk
end
end
end
- @top_level
- end
- private
+ unget_tk tk
- def make_message(msg)
- prefix = "\n" + @input_file_name + ":"
- if @scanner
- prefix << "#{@scanner.line_no}:#{@scanner.char_no}: "
- end
- return prefix + msg
- end
-
- def warn(msg)
- return if @options.quiet
- msg = make_message msg
- $stderr.puts msg
+ res
end
def error(msg)
@@ -1432,20 +1521,149 @@ class RDoc::RubyParser
exit(1)
end
- def progress(char)
- unless @options.quiet
- @progress.print(char)
- @progress.flush
+ ##
+ # Look for a 'call-seq' in the comment, and override the normal parameter
+ # stuff
+
+ def extract_call_seq(comment, meth)
+ if comment.sub!(/:?call-seq:(.*?)^\s*\#?\s*$/m, '') then
+ seq = $1
+ seq.gsub!(/^\s*\#\s*/, '')
+ meth.call_seq = seq
end
+
+ meth
end
- def add_token_listener(obj)
- @token_listeners ||= []
- @token_listeners << obj
+ def get_bool
+ skip_tkspace
+ tk = get_tk
+ case tk
+ when TkTRUE
+ true
+ when TkFALSE, TkNIL
+ false
+ else
+ unget_tk tk
+ true
+ end
end
- def remove_token_listener(obj)
- @token_listeners.delete(obj)
+ ##
+ # Look for the name of a class of module (optionally with a leading :: or
+ # with :: separated named) and return the ultimate name and container
+
+ def get_class_or_module(container)
+ skip_tkspace
+ name_t = get_tk
+
+ # class ::A -> A is in the top level
+ if TkCOLON2 === name_t then
+ name_t = get_tk
+ container = @top_level
+ end
+
+ skip_tkspace(false)
+
+ while TkCOLON2 === peek_tk do
+ prev_container = container
+ container = container.find_module_named(name_t.name)
+ if !container
+# warn("Couldn't find module #{name_t.name}")
+ container = prev_container.add_module RDoc::NormalModule, name_t.name
+ end
+ get_tk
+ name_t = get_tk
+ end
+ skip_tkspace(false)
+ return [container, name_t]
+ end
+
+ ##
+ # Return a superclass, which can be either a constant of an expression
+
+ def get_class_specification
+ tk = get_tk
+ return "self" if TkSELF === tk
+
+ res = ""
+ while TkCOLON2 === tk or TkCOLON3 === tk or TkCONSTANT === tk do
+ res += tk.text
+ tk = get_tk
+ end
+
+ unget_tk(tk)
+ skip_tkspace(false)
+
+ get_tkread # empty out read buffer
+
+ tk = get_tk
+
+ case tk
+ when TkNL, TkCOMMENT, TkSEMICOLON then
+ unget_tk(tk)
+ return res
+ end
+
+ res += parse_call_parameters(tk)
+ res
+ end
+
+ ##
+ # Parse a constant, which might be qualified by one or more class or module
+ # names
+
+ def get_constant
+ res = ""
+ skip_tkspace(false)
+ tk = get_tk
+
+ while TkCOLON2 === tk or TkCOLON3 === tk or TkCONSTANT === tk do
+ res += tk.text
+ tk = get_tk
+ end
+
+# if res.empty?
+# warn("Unexpected token #{tk} in constant")
+# end
+ unget_tk(tk)
+ res
+ end
+
+ ##
+ # Get a constant that may be surrounded by parens
+
+ def get_constant_with_optional_parens
+ skip_tkspace(false)
+ nest = 0
+ while TkLPAREN === (tk = peek_tk) or TkfLPAREN === tk do
+ get_tk
+ skip_tkspace(true)
+ nest += 1
+ end
+
+ name = get_constant
+
+ while nest > 0
+ skip_tkspace(true)
+ tk = get_tk
+ nest -= 1 if TkRPAREN === tk
+ end
+ name
+ end
+
+ def get_symbol_or_name
+ tk = get_tk
+ case tk
+ when TkSYMBOL
+ tk.text.sub(/^:/, '')
+ when TkId, TkOp
+ tk.name
+ when TkSTRING
+ tk.text
+ else
+ raise "Name or symbol expected (got #{tk})"
+ end
end
def get_tk
@@ -1460,10 +1678,10 @@ class RDoc::RubyParser
puts "get_tk2 => #{tk.inspect}" if $TOKEN_DEBUG
end
- if tk.kind_of?(TkSYMBEG)
+ if TkSYMBEG === tk then
set_token_position(tk.line_no, tk.char_no)
tk1 = get_tk
- if tk1.kind_of?(TkId) || tk1.kind_of?(TkOp) || tk1.kind_of?(TkSTRING)
+ if TkId === tk1 or TkOp === tk1 or TkSTRING === tk1 then
if tk1.respond_to?(:name)
tk = Token(TkSYMBOL).set_text(":" + tk1.name)
else
@@ -1488,233 +1706,168 @@ class RDoc::RubyParser
tk
end
- def peek_tk
- unget_tk(tk = get_tk)
- tk
- end
-
- def unget_tk(tk)
- @tokens.unshift tk
- @unget_read.unshift @read.pop
-
- # Remove this token from any listeners
- @token_listeners.each do |obj|
- obj.pop_token
- end if @token_listeners
- end
-
- def skip_tkspace(skip_nl = true)
- tokens = []
- while ((tk = get_tk).kind_of?(TkSPACE) ||
- (skip_nl && tk.kind_of?(TkNL)))
- tokens.push tk
- end
- unget_tk(tk)
- tokens
- end
-
def get_tkread
read = @read.join("")
@read = []
read
end
- def peek_read
- @read.join('')
- end
-
- NORMAL = "::"
- SINGLE = "<<"
-
##
- # Look for the first comment in a file that isn't a shebang line.
+ # Look for directives in a normal comment block:
+ #
+ # #-- - don't display comment from this point forward
+ #
+ # This routine modifies it's parameter
- def collect_first_comment
- skip_tkspace
- res = ''
- first_line = true
+ def look_for_directives_in(context, comment)
+ preprocess = RDoc::Markup::PreProcess.new(@file_name,
+ @options.rdoc_include)
- tk = get_tk
- while tk.kind_of?(TkCOMMENT)
- if first_line && /\A#!/ =~ tk.text
- skip_tkspace
- tk = get_tk
- elsif first_line && /\A#\s*-\*-/ =~ tk.text
- first_line = false
- skip_tkspace
- tk = get_tk
+ preprocess.handle(comment) do |directive, param|
+ case directive
+ when 'enddoc' then
+ throw :enddoc
+ when 'main' then
+ @options.main_page = param
+ ''
+ when 'method', 'singleton-method' then
+ false # ignore
+ when 'section' then
+ context.set_current_section(param, comment)
+ comment.replace ''
+ break
+ when 'startdoc' then
+ context.start_doc
+ context.force_documentation = true
+ ''
+ when 'stopdoc' then
+ context.stop_doc
+ ''
+ when 'title' then
+ @options.title = param
+ ''
else
- first_line = false
- res << tk.text << "\n"
- tk = get_tk
- if tk.kind_of? TkNL
- skip_tkspace(false)
- tk = get_tk
- end
+ warn "Unrecognized directive '#{directive}'"
+ false
end
end
- unget_tk(tk)
- res
- end
- def parse_toplevel_statements(container)
- comment = collect_first_comment
- look_for_directives_in(container, comment)
- container.comment = comment unless comment.empty?
- parse_statements(container, NORMAL, nil, comment)
+ remove_private_comments(comment)
end
- def parse_statements(container, single=NORMAL, current_method=nil, comment='')
- nest = 1
- save_visibility = container.visibility
-
-# if container.kind_of?(TopLevel)
-# else
-# comment = ''
-# end
-
- non_comment_seen = true
-
- while tk = get_tk
- keep_comment = false
-
- non_comment_seen = true unless tk.kind_of?(TkCOMMENT)
-
- case tk
- when TkNL
- skip_tkspace(true) # Skip blanks and newlines
- tk = get_tk
- if tk.kind_of?(TkCOMMENT)
- if non_comment_seen
- comment = ''
- non_comment_seen = false
- end
- while tk.kind_of?(TkCOMMENT)
- comment << tk.text << "\n"
- tk = get_tk # this is the newline
- skip_tkspace(false) # leading spaces
- tk = get_tk
- end
- unless comment.empty?
- look_for_directives_in(container, comment)
- if container.done_documenting
- container.ongoing_visibility = save_visibility
- # return
- end
- end
- keep_comment = true
- else
- non_comment_seen = true
- end
- unget_tk(tk)
- keep_comment = true
-
- when TkCLASS
- if container.document_children
- parse_class(container, single, tk, comment)
- else
- nest += 1
- end
+ def make_message(msg)
+ prefix = "\n" + @file_name + ":"
+ if @scanner
+ prefix << "#{@scanner.line_no}:#{@scanner.char_no}: "
+ end
+ return prefix + msg
+ end
- when TkMODULE
- if container.document_children
- parse_module(container, single, tk, comment)
- else
- nest += 1
- end
+ def parse_attr(context, single, tk, comment)
+ args = parse_symbol_arg(1)
+ if args.size > 0
+ name = args[0]
+ rw = "R"
+ skip_tkspace(false)
+ tk = get_tk
+ if TkCOMMA === tk then
+ rw = "RW" if get_bool
+ else
+ unget_tk tk
+ end
+ att = RDoc::Attr.new get_tkread, name, rw, comment
+ read_documentation_modifiers att, RDoc::ATTR_MODIFIERS
+ if att.document_self
+ context.add_attribute(att)
+ end
+ else
+ warn("'attr' ignored - looks like a variable")
+ end
+ end
- when TkDEF
- if container.document_self
- parse_method(container, single, tk, comment)
- else
- nest += 1
- end
+ def parse_attr_accessor(context, single, tk, comment)
+ args = parse_symbol_arg
+ read = get_tkread
+ rw = "?"
- when TkCONSTANT
- if container.document_self
- parse_constant(container, single, tk, comment)
- end
+ # If nodoc is given, don't document any of them
- when TkALIAS
- if container.document_self
- parse_alias(container, single, tk, comment)
- end
+ tmp = RDoc::CodeObject.new
+ read_documentation_modifiers tmp, RDoc::ATTR_MODIFIERS
+ return unless tmp.document_self
- when TkYIELD
- if current_method.nil?
- warn("Warning: yield outside of method") if container.document_self
- else
- parse_yield(container, single, tk, current_method)
- end
+ case tk.name
+ when "attr_reader" then rw = "R"
+ when "attr_writer" then rw = "W"
+ when "attr_accessor" then rw = "RW"
+ else
+ rw = @options.extra_accessor_flags[tk.name]
+ rw = '?' if rw.nil?
+ end
- # Until and While can have a 'do', which shouldn't increas
- # the nesting. We can't solve the general case, but we can
- # handle most occurrences by ignoring a do at the end of a line
+ for name in args
+ att = RDoc::Attr.new get_tkread, name, rw, comment
+ context.add_attribute att
+ end
+ end
- when TkUNTIL, TkWHILE
- nest += 1
- puts "Found #{tk.class} in #{container.name}, nest = #{nest}, " +
- "line #{tk.line_no}" if $DEBUG_RDOC
- skip_optional_do_after_expression
+ def parse_alias(context, single, tk, comment)
+ skip_tkspace
+ if TkLPAREN === peek_tk then
+ get_tk
+ skip_tkspace
+ end
+ new_name = get_symbol_or_name
+ @scanner.instance_eval{@lex_state = EXPR_FNAME}
+ skip_tkspace
+ if TkCOMMA === peek_tk then
+ get_tk
+ skip_tkspace
+ end
+ old_name = get_symbol_or_name
- # 'for' is trickier
- when TkFOR
- nest += 1
- puts "Found #{tk.class} in #{container.name}, nest = #{nest}, " +
- "line #{tk.line_no}" if $DEBUG_RDOC
- skip_for_variable
- skip_optional_do_after_expression
+ al = RDoc::Alias.new get_tkread, old_name, new_name, comment
+ read_documentation_modifiers al, RDoc::ATTR_MODIFIERS
+ if al.document_self
+ context.add_alias(al)
+ end
+ end
- when TkCASE, TkDO, TkIF, TkUNLESS, TkBEGIN
- nest += 1
- puts "Found #{tk.class} in #{container.name}, nest = #{nest}, " +
- "line #{tk.line_no}" if $DEBUG_RDOC
+ def parse_call_parameters(tk)
+ end_token = case tk
+ when TkLPAREN, TkfLPAREN
+ TkRPAREN
+ when TkRPAREN
+ return ""
+ else
+ TkNL
+ end
+ nest = 0
- when TkIDENTIFIER
- if nest == 1 and current_method.nil?
- case tk.name
- when "private", "protected", "public",
- "private_class_method", "public_class_method"
- parse_visibility(container, single, tk)
- keep_comment = true
- when "attr"
- parse_attr(container, single, tk, comment)
- when /^attr_(reader|writer|accessor)$/, @options.extra_accessors
- parse_attr_accessor(container, single, tk, comment)
- when "alias_method"
- if container.document_self
- parse_alias(container, single, tk, comment)
- end
+ loop do
+ puts("Call param: #{tk}, #{@scanner.continue} " +
+ "#{@scanner.lex_state} #{nest}") if $DEBUG_RDOC
+ case tk
+ when TkSEMICOLON
+ break
+ when TkLPAREN, TkfLPAREN
+ nest += 1
+ when end_token
+ if end_token == TkRPAREN
+ nest -= 1
+ break if @scanner.lex_state == EXPR_END and nest <= 0
+ else
+ break unless @scanner.continue
end
+ when TkCOMMENT
+ unget_tk(tk)
+ break
end
-
- case tk.name
- when "require"
- parse_require(container, comment)
- when "include"
- parse_include(container, comment)
- end
-
-
- when TkEND
- nest -= 1
- puts "Found 'end' in #{container.name}, nest = #{nest}, line #{tk.line_no}" if $DEBUG_RDOC
- puts "Method = #{current_method.name}" if $DEBUG_RDOC and current_method
- if nest == 0
- read_documentation_modifiers container, RDoc::CLASS_MODIFIERS
- container.ongoing_visibility = save_visibility
- return
- end
-
- end
-
- comment = '' unless keep_comment
-
- begin
- get_tkread
- skip_tkspace(false)
- end while peek_tk == TkNL
+ tk = get_tk
end
+ res = get_tkread.tr("\n", " ").strip
+ res = "" if res == ";"
+ res
end
def parse_class(container, single, tk, comment, &block)
@@ -1729,7 +1882,7 @@ class RDoc::RubyParser
name = name_t.name
superclass = "Object"
- if peek_tk.kind_of?(TkLT)
+ if TkLT === peek_tk then
get_tk
skip_tkspace(true)
superclass = get_class_specification
@@ -1769,54 +1922,12 @@ class RDoc::RubyParser
end
end
- def parse_module(container, single, tk, comment)
- progress("m")
- @stats.num_modules += 1
- container, name_t = get_class_or_module(container)
-# skip_tkspace
- name = name_t.name
- mod = container.add_module RDoc::NormalModule, name
- mod.record_location @top_level
- read_documentation_modifiers mod, RDoc::CLASS_MODIFIERS
- parse_statements(mod)
- mod.comment = comment
- end
-
- # Look for the name of a class of module (optionally with a leading :: or
- # with :: separated named) and return the ultimate name and container
-
- def get_class_or_module(container)
- skip_tkspace
- name_t = get_tk
-
- # class ::A -> A is in the top level
- if name_t.kind_of?(TkCOLON2)
- name_t = get_tk
- container = @top_level
- end
-
- skip_tkspace(false)
-
- while peek_tk.kind_of?(TkCOLON2)
- prev_container = container
- container = container.find_module_named(name_t.name)
- if !container
-# warn("Couldn't find module #{name_t.name}")
- container = prev_container.add_module RDoc::NormalModule, name_t.name
- end
- get_tk
- name_t = get_tk
- end
- skip_tkspace(false)
- return [container, name_t]
- end
-
def parse_constant(container, single, tk, comment)
name = tk.name
skip_tkspace(false)
eq_tk = get_tk
- unless eq_tk.kind_of?(TkASSIGN)
+ unless TkASSIGN === eq_tk then
unget_tk(eq_tk)
return
end
@@ -1826,7 +1937,7 @@ class RDoc::RubyParser
get_tkread
tk = get_tk
- if tk.kind_of? TkGT
+ if TkGT === tk then
unget_tk(tk)
unget_tk(eq_tk)
return
@@ -1868,6 +1979,129 @@ class RDoc::RubyParser
end
end
+ def parse_comment(container, tk, comment)
+ progress(".")
+ @stats.num_methods += 1
+ line_no = tk.line_no
+ column = tk.char_no
+
+ singleton = !!comment.sub!(/(^# +:?)(singleton-)(method:)/, '\1\3')
+
+ if comment.sub!(/^# +:?method: *(\S*).*?\n/i, '') then
+ name = $1 unless $1.empty?
+ else
+ return nil
+ end
+
+ meth = RDoc::GhostMethod.new get_tkread, name
+ meth.singleton = singleton
+
+ meth.start_collecting_tokens
+ indent = TkSPACE.new 1, 1
+ indent.set_text " " * column
+
+ position_comment = TkCOMMENT.new(line_no, 1, "# File #{@top_level.file_absolute_name}, line #{line_no}")
+ meth.add_tokens [position_comment, NEWLINE_TOKEN, indent]
+
+ meth.params = ''
+
+ extract_call_seq comment, meth
+
+ container.add_method meth if meth.document_self
+
+ meth.comment = comment
+ end
+
+ def parse_include(context, comment)
+ loop do
+ skip_tkspace_comment
+
+ name = get_constant_with_optional_parens
+ context.add_include RDoc::Include.new(name, comment) unless name.empty?
+
+ return unless TkCOMMA === peek_tk
+ get_tk
+ end
+ end
+
+ ##
+ # Parses a meta-programmed method
+
+ def parse_meta_method(container, single, tk, comment)
+ progress(".")
+ @stats.num_methods += 1
+ line_no = tk.line_no
+ column = tk.char_no
+
+ start_collecting_tokens
+ add_token tk
+ add_token_listener self
+
+ skip_tkspace false
+
+ singleton = !!comment.sub!(/(^# +:?)(singleton-)(method:)/, '\1\3')
+
+ if comment.sub!(/^# +:?method: *(\S*).*?\n/i, '') then
+ name = $1 unless $1.empty?
+ end
+
+ if name.nil? then
+ name_t = get_tk
+ case name_t
+ when TkSYMBOL then
+ name = name_t.text[1..-1]
+ when TkSTRING then
+ name = name_t.text[1..-2]
+ else
+ warn "#{container.top_level.file_relative_name}:#{name_t.line_no} unknown name token #{name_t.inspect} for meta-method"
+ name = 'unknown'
+ end
+ end
+
+ meth = RDoc::MetaMethod.new get_tkread, name
+ meth.singleton = singleton
+
+ remove_token_listener self
+
+ meth.start_collecting_tokens
+ indent = TkSPACE.new 1, 1
+ indent.set_text " " * column
+
+ position_comment = TkCOMMENT.new(line_no, 1, "# File #{@top_level.file_absolute_name}, line #{line_no}")
+ meth.add_tokens [position_comment, NEWLINE_TOKEN, indent]
+ meth.add_tokens @token_stream
+
+ add_token_listener meth
+
+ meth.params = ''
+
+ extract_call_seq comment, meth
+
+ container.add_method meth if meth.document_self
+
+ last_tk = tk
+
+ while tk = get_tk do
+ case tk
+ when TkSEMICOLON then
+ break
+ when TkNL then
+ break unless last_tk and TkCOMMA === last_tk
+ when TkSPACE then
+ # expression continues
+ else
+ last_tk = tk
+ end
+ end
+
+ remove_token_listener meth
+
+ meth.comment = comment
+ end
+
+ ##
+ # Parses a method
+
def parse_method(container, single, tk, comment)
progress(".")
@stats.num_methods += 1
@@ -1878,7 +2112,8 @@ class RDoc::RubyParser
add_token(tk)
add_token_listener(self)
- @scanner.instance_eval{@lex_state = EXPR_FNAME}
+ @scanner.instance_eval do @lex_state = EXPR_FNAME end
+
skip_tkspace(false)
name_t = get_tk
back_tk = skip_tkspace
@@ -1886,25 +2121,27 @@ class RDoc::RubyParser
added_container = false
dot = get_tk
- if dot.kind_of?(TkDOT) or dot.kind_of?(TkCOLON2)
- @scanner.instance_eval{@lex_state = EXPR_FNAME}
+ if TkDOT === dot or TkCOLON2 === dot then
+ @scanner.instance_eval do @lex_state = EXPR_FNAME end
skip_tkspace
name_t2 = get_tk
+
case name_t
- when TkSELF
+ when TkSELF then
name = name_t2.name
- when TkCONSTANT
+ when TkCONSTANT then
name = name_t2.name
prev_container = container
container = container.find_module_named(name_t.name)
- if !container
+ unless container then
added_container = true
obj = name_t.name.split("::").inject(Object) do |state, item|
state.const_get(item)
end rescue nil
type = obj.class == Class ? RDoc::NormalClass : RDoc::NormalModule
- if not [Class, Module].include?(obj.class)
+
+ unless [Class, Module].include?(obj.class) then
warn("Couldn't find #{name_t.name}. Assuming it's a module")
end
@@ -1913,6 +2150,8 @@ class RDoc::RubyParser
else
container = prev_container.add_module(type, name_t.name)
end
+
+ container.record_location @top_level
end
else
# warn("Unexpected token '#{name_t2.inspect}'")
@@ -1920,6 +2159,7 @@ class RDoc::RubyParser
skip_method(container)
return
end
+
meth = RDoc::AnyMethod.new(get_tkread, name)
meth.singleton = true
else
@@ -1933,36 +2173,32 @@ class RDoc::RubyParser
meth.singleton = (single == SINGLE)
end
- remove_token_listener(self)
+ remove_token_listener self
meth.start_collecting_tokens
- indent = TkSPACE.new(1,1)
- indent.set_text(" " * column)
+ indent = TkSPACE.new 1, 1
+ indent.set_text " " * column
- meth.add_tokens([TkCOMMENT.new(line_no,
- 1,
- "# File #{@top_level.file_absolute_name}, line #{line_no}"),
- NEWLINE_TOKEN,
- indent])
+ token = TkCOMMENT.new(line_no, 1, "# File #{@top_level.file_absolute_name}, line #{line_no}")
+ meth.add_tokens [token, NEWLINE_TOKEN, indent]
+ meth.add_tokens @token_stream
- meth.add_tokens(@token_stream)
+ add_token_listener meth
- add_token_listener(meth)
+ @scanner.instance_eval do @continue = false end
+ parse_method_parameters meth
- @scanner.instance_eval{@continue = false}
- parse_method_parameters(meth)
-
- if meth.document_self
- container.add_method(meth)
- elsif added_container
+ if meth.document_self then
+ container.add_method meth
+ elsif added_container then
container.document_self = false
end
# Having now read the method parameters and documentation modifiers, we
# now know whether we have to rename #initialize to ::new
- if name == "initialize" && !meth.singleton
- if meth.dont_rename_initialize
+ if name == "initialize" && !meth.singleton then
+ if meth.dont_rename_initialize then
meth.visibility = :protected
else
meth.singleton = true
@@ -1975,41 +2211,11 @@ class RDoc::RubyParser
remove_token_listener(meth)
- # Look for a 'call-seq' in the comment, and override the
- # normal parameter stuff
-
- if comment.sub!(/:?call-seq:(.*?)^\s*\#?\s*$/m, '')
- seq = $1
- seq.gsub!(/^\s*\#\s*/, '')
- meth.call_seq = seq
- end
+ extract_call_seq comment, meth
meth.comment = comment
end
- def skip_method(container)
- meth = RDoc::AnyMethod.new "", "anon"
- parse_method_parameters(meth)
- parse_statements(container, false, meth)
- end
-
- # Capture the method's parameters. Along the way, look for a comment
- # containing.
- #
- # # yields: ....
- #
- # and add this as the block_params for the method
-
- def parse_method_parameters(method)
- res = parse_method_or_yield_parameters(method)
- res = "(" + res + ")" unless res[0] == ?(
- method.params = res unless method.params
- if method.block_params.nil?
- skip_tkspace(false)
- read_documentation_modifiers method, RDoc::METHOD_MODIFIERS
- end
- end
-
def parse_method_or_yield_parameters(method = nil,
modifiers = RDoc::METHOD_MODIFIERS)
skip_tkspace(false)
@@ -2063,174 +2269,388 @@ class RDoc::RubyParser
res
end
- # skip the var [in] part of a 'for' statement
- def skip_for_variable
- skip_tkspace(false)
- tk = get_tk
- skip_tkspace(false)
- tk = get_tk
- unget_tk(tk) unless tk.kind_of?(TkIN)
+ ##
+ # Capture the method's parameters. Along the way, look for a comment
+ # containing:
+ #
+ # # yields: ....
+ #
+ # and add this as the block_params for the method
+
+ def parse_method_parameters(method)
+ res = parse_method_or_yield_parameters(method)
+ res = "(" + res + ")" unless res[0] == ?(
+ method.params = res unless method.params
+ if method.block_params.nil?
+ skip_tkspace(false)
+ read_documentation_modifiers method, RDoc::METHOD_MODIFIERS
+ end
end
- # while, until, and for have an optional
- def skip_optional_do_after_expression
- skip_tkspace(false)
+ def parse_module(container, single, tk, comment)
+ progress("m")
+ @stats.num_modules += 1
+ container, name_t = get_class_or_module(container)
+# skip_tkspace
+ name = name_t.name
+
+ mod = container.add_module RDoc::NormalModule, name
+ mod.record_location @top_level
+ read_documentation_modifiers mod, RDoc::CLASS_MODIFIERS
+ parse_statements(mod)
+ mod.comment = comment
+ end
+
+ def parse_require(context, comment)
+ skip_tkspace_comment
tk = get_tk
+ if TkLPAREN === tk then
+ skip_tkspace_comment
+ tk = get_tk
+ end
+
+ name = nil
case tk
- when TkLPAREN, TkfLPAREN
- end_token = TkRPAREN
+ when TkSTRING
+ name = tk.text
+ # when TkCONSTANT, TkIDENTIFIER, TkIVAR, TkGVAR
+ # name = tk.name
+ when TkDSTRING
+ warn "Skipping require of dynamic string: #{tk.text}"
+ # else
+ # warn "'require' used as variable"
+ end
+ if name
+ context.add_require RDoc::Require.new(name, comment)
else
- end_token = TkNL
+ unget_tk(tk)
end
+ end
- nest = 0
- @scanner.instance_eval{@continue = false}
+ def parse_statements(container, single = NORMAL, current_method = nil,
+ comment = '')
+ nest = 1
+ save_visibility = container.visibility
+
+ non_comment_seen = true
+
+ while tk = get_tk do
+ keep_comment = false
+
+ non_comment_seen = true unless TkCOMMENT === tk
- loop do
- puts("\nWhile: #{tk.text.inspect}, #{@scanner.continue} " \
- "#{@scanner.lex_state} #{nest}") if $DEBUG_RDOC
case tk
- when TkSEMICOLON
- break
- when TkLPAREN, TkfLPAREN
- nest += 1
- when TkDO
- break if nest.zero?
- when end_token
- if end_token == TkRPAREN
- nest -= 1
- break if @scanner.lex_state == EXPR_END and nest.zero?
+ when TkNL then
+ skip_tkspace true # Skip blanks and newlines
+ tk = get_tk
+
+ if TkCOMMENT === tk then
+ if non_comment_seen then
+ # Look for RDoc in a comment about to be thrown away
+ parse_comment container, tk, comment unless comment.empty?
+
+ comment = ''
+ non_comment_seen = false
+ end
+
+ while TkCOMMENT === tk do
+ comment << tk.text << "\n"
+ tk = get_tk # this is the newline
+ skip_tkspace(false) # leading spaces
+ tk = get_tk
+ end
+
+ unless comment.empty? then
+ look_for_directives_in container, comment
+
+ if container.done_documenting then
+ container.ongoing_visibility = save_visibility
+ end
+ end
+
+ keep_comment = true
else
- break unless @scanner.continue
+ non_comment_seen = true
end
- end
- tk = get_tk
- end
- skip_tkspace(false)
- if peek_tk.kind_of? TkDO
- get_tk
- end
- end
- # Return a superclass, which can be either a constant
- # of an expression
+ unget_tk tk
+ keep_comment = true
- def get_class_specification
- tk = get_tk
- return "self" if tk.kind_of?(TkSELF)
+ when TkCLASS then
+ if container.document_children then
+ parse_class container, single, tk, comment
+ else
+ nest += 1
+ end
- res = ""
- while tk.kind_of?(TkCOLON2) ||
- tk.kind_of?(TkCOLON3) ||
- tk.kind_of?(TkCONSTANT)
+ when TkMODULE then
+ if container.document_children then
+ parse_module container, single, tk, comment
+ else
+ nest += 1
+ end
- res += tk.text
- tk = get_tk
- end
+ when TkDEF then
+ if container.document_self then
+ parse_method container, single, tk, comment
+ else
+ nest += 1
+ end
- unget_tk(tk)
- skip_tkspace(false)
+ when TkCONSTANT then
+ if container.document_self then
+ parse_constant container, single, tk, comment
+ end
- get_tkread # empty out read buffer
+ when TkALIAS then
+ if container.document_self then
+ parse_alias container, single, tk, comment
+ end
- tk = get_tk
+ when TkYIELD then
+ if current_method.nil? then
+ warn "Warning: yield outside of method" if container.document_self
+ else
+ parse_yield container, single, tk, current_method
+ end
- case tk
- when TkNL, TkCOMMENT, TkSEMICOLON
- unget_tk(tk)
- return res
- end
+ # Until and While can have a 'do', which shouldn't increase the nesting.
+ # We can't solve the general case, but we can handle most occurrences by
+ # ignoring a do at the end of a line.
- res += parse_call_parameters(tk)
- res
- end
+ when TkUNTIL, TkWHILE then
+ nest += 1
+ puts "Found #{tk.class} in #{container.name}, nest = #{nest}, " +
+ "line #{tk.line_no}" if $DEBUG_RDOC
+ skip_optional_do_after_expression
- def parse_call_parameters(tk)
+ # 'for' is trickier
+ when TkFOR then
+ nest += 1
+ puts "Found #{tk.class} in #{container.name}, nest = #{nest}, " +
+ "line #{tk.line_no}" if $DEBUG_RDOC
+ skip_for_variable
+ skip_optional_do_after_expression
- end_token = case tk
- when TkLPAREN, TkfLPAREN
- TkRPAREN
- when TkRPAREN
- return ""
- else
- TkNL
- end
- nest = 0
+ when TkCASE, TkDO, TkIF, TkUNLESS, TkBEGIN then
+ nest += 1
+ puts "Found #{tk.class} in #{container.name}, nest = #{nest}, " +
+ "line #{tk.line_no}" if $DEBUG_RDOC
- loop do
- puts("Call param: #{tk}, #{@scanner.continue} " +
- "#{@scanner.lex_state} #{nest}") if $DEBUG_RDOC
- case tk
- when TkSEMICOLON
- break
- when TkLPAREN, TkfLPAREN
- nest += 1
- when end_token
- if end_token == TkRPAREN
- nest -= 1
- break if @scanner.lex_state == EXPR_END and nest <= 0
+ when TkIDENTIFIER then
+ if nest == 1 and current_method.nil? then
+ case tk.name
+ when 'private', 'protected', 'public', 'private_class_method',
+ 'public_class_method', 'module_function' then
+ parse_visibility container, single, tk
+ keep_comment = true
+ when 'attr' then
+ parse_attr container, single, tk, comment
+ when /^attr_(reader|writer|accessor)$/, @options.extra_accessors then
+ parse_attr_accessor container, single, tk, comment
+ when 'alias_method' then
+ if container.document_self then
+ parse_alias container, single, tk, comment
+ end
else
- break unless @scanner.continue
+ if container.document_self and comment =~ /\A#\#$/ then
+ parse_meta_method container, single, tk, comment
+ end
end
- when TkCOMMENT
- unget_tk(tk)
- break
end
- tk = get_tk
+
+ case tk.name
+ when "require" then
+ parse_require container, comment
+ when "include" then
+ parse_include container, comment
+ end
+
+ when TkEND then
+ nest -= 1
+ puts "Found 'end' in #{container.name}, nest = #{nest}, line #{tk.line_no}" if $DEBUG_RDOC
+ puts "Method = #{current_method.name}" if $DEBUG_RDOC and current_method
+ if nest == 0 then
+ read_documentation_modifiers container, RDoc::CLASS_MODIFIERS
+ container.ongoing_visibility = save_visibility
+ return
+ end
+
+ end
+
+ comment = '' unless keep_comment
+
+ begin
+ get_tkread
+ skip_tkspace(false)
+ end while peek_tk == TkNL
end
- res = get_tkread.tr("\n", " ").strip
- res = "" if res == ";"
- res
end
- # Parse a constant, which might be qualified by
- # one or more class or module names
+ def parse_symbol_arg(no = nil)
+ args = []
+ skip_tkspace_comment
+ case tk = get_tk
+ when TkLPAREN
+ loop do
+ skip_tkspace_comment
+ if tk1 = parse_symbol_in_arg
+ args.push tk1
+ break if no and args.size >= no
+ end
- def get_constant
- res = ""
- skip_tkspace(false)
- tk = get_tk
+ skip_tkspace_comment
+ case tk2 = get_tk
+ when TkRPAREN
+ break
+ when TkCOMMA
+ else
+ warn("unexpected token: '#{tk2.inspect}'") if $DEBUG_RDOC
+ break
+ end
+ end
+ else
+ unget_tk tk
+ if tk = parse_symbol_in_arg
+ args.push tk
+ return args if no and args.size >= no
+ end
- while tk.kind_of?(TkCOLON2) ||
- tk.kind_of?(TkCOLON3) ||
- tk.kind_of?(TkCONSTANT)
+ loop do
+ skip_tkspace(false)
- res += tk.text
- tk = get_tk
+ tk1 = get_tk
+ unless TkCOMMA === tk1 then
+ unget_tk tk1
+ break
+ end
+
+ skip_tkspace_comment
+ if tk = parse_symbol_in_arg
+ args.push tk
+ break if no and args.size >= no
+ end
+ end
end
+ args
+ end
-# if res.empty?
-# warn("Unexpected token #{tk} in constant")
-# end
- unget_tk(tk)
- res
+ def parse_symbol_in_arg
+ case tk = get_tk
+ when TkSYMBOL
+ tk.text.sub(/^:/, '')
+ when TkSTRING
+ eval @read[-1]
+ else
+ warn("Expected symbol or string, got #{tk.inspect}") if $DEBUG_RDOC
+ nil
+ end
end
- # Get a constant that may be surrounded by parens
+ def parse_toplevel_statements(container)
+ comment = collect_first_comment
+ look_for_directives_in(container, comment)
+ container.comment = comment unless comment.empty?
+ parse_statements container, NORMAL, nil, comment
+ end
- def get_constant_with_optional_parens
- skip_tkspace(false)
- nest = 0
- while (tk = peek_tk).kind_of?(TkLPAREN) || tk.kind_of?(TkfLPAREN)
- get_tk
- skip_tkspace(true)
- nest += 1
+ def parse_visibility(container, single, tk)
+ singleton = (single == SINGLE)
+
+ vis_type = tk.name
+
+ vis = case vis_type
+ when 'private' then :private
+ when 'protected' then :protected
+ when 'public' then :public
+ when 'private_class_method' then
+ singleton = true
+ :private
+ when 'public_class_method' then
+ singleton = true
+ :public
+ when 'module_function' then
+ singleton = true
+ :public
+ else
+ raise "Invalid visibility: #{tk.name}"
+ end
+
+ skip_tkspace_comment false
+
+ case peek_tk
+ # Ryan Davis suggested the extension to ignore modifiers, because he
+ # often writes
+ #
+ # protected unless $TESTING
+ #
+ when TkNL, TkUNLESS_MOD, TkIF_MOD, TkSEMICOLON then
+ container.ongoing_visibility = vis
+ else
+ if vis_type == 'module_function' then
+ args = parse_symbol_arg
+ container.set_visibility_for args, :private, false
+
+ module_functions = []
+
+ container.methods_matching args do |m|
+ s_m = m.dup
+ s_m.singleton = true if RDoc::AnyMethod === s_m
+ s_m.visibility = :public
+ module_functions << s_m
+ end
+
+ module_functions.each do |s_m|
+ case s_m
+ when RDoc::AnyMethod then
+ container.add_method s_m
+ when RDoc::Attr then
+ container.add_attribute s_m
+ end
+ end
+ else
+ args = parse_symbol_arg
+ container.set_visibility_for args, vis, singleton
+ end
+ end
+ end
+
+ def parse_yield_parameters
+ parse_method_or_yield_parameters
+ end
+
+ def parse_yield(context, single, tk, method)
+ if method.block_params.nil?
+ get_tkread
+ @scanner.instance_eval{@continue = false}
+ method.block_params = parse_yield_parameters
end
+ end
- name = get_constant
+ def peek_read
+ @read.join('')
+ end
- while nest > 0
- skip_tkspace(true)
- tk = get_tk
- nest -= 1 if tk.kind_of?(TkRPAREN)
+ ##
+ # Peek at the next token, but don't remove it from the stream
+
+ def peek_tk
+ unget_tk(tk = get_tk)
+ tk
+ end
+
+ def progress(char)
+ unless @options.quiet
+ @progress.print(char)
+ @progress.flush
end
- name
end
- # Directives are modifier comments that can appear after class, module,
- # or method names. For example:
+ ##
+ # Directives are modifier comments that can appear after class, module, or
+ # method names. For example:
#
- # def fred # :yields: a, b
+ # def fred # :yields: a, b
#
# or:
#
@@ -2242,7 +2662,7 @@ class RDoc::RubyParser
tk = get_tk
puts "directive: #{tk.text.inspect}" if $DEBUG_RDOC
result = nil
- if tk.kind_of?(TkCOMMENT)
+ if TkCOMMENT === tk
if tk.text =~ /\s*:?(\w+):\s*(.*)/
directive = $1.downcase
if allowed.include?(directive)
@@ -2259,329 +2679,174 @@ class RDoc::RubyParser
dir = read_directive(allow)
case dir[0]
-
- when "notnew", "not_new", "not-new"
+ when "notnew", "not_new", "not-new" then
context.dont_rename_initialize = true
- when "nodoc"
+ when "nodoc" then
context.document_self = false
if dir[1].downcase == "all"
context.document_children = false
end
- when "doc"
+ when "doc" then
context.document_self = true
context.force_documentation = true
- when "yield", "yields"
+ when "yield", "yields" then
unless context.params.nil?
context.params.sub!(/(,|)\s*&\w+/,'') # remove parameter &proc
end
- context.block_params = dir[1]
- when "arg", "args"
+ context.block_params = dir[1]
+
+ when "arg", "args" then
context.params = dir[1]
end if dir
end
- ##
- # Look for directives in a normal comment block:
- #
- # #-- - don't display comment from this point forward
- #
- # This routine modifies it's parameter
-
- def look_for_directives_in(context, comment)
- preprocess = RDoc::Markup::PreProcess.new(@input_file_name,
- @options.rdoc_include)
-
- preprocess.handle(comment) do |directive, param|
- case directive
- when "stopdoc"
- context.stop_doc
- ""
- when "startdoc"
- context.start_doc
- context.force_documentation = true
- ""
-
- when "enddoc"
- #context.done_documenting = true
- #""
- throw :enddoc
-
- when "main"
- @options.main_page = param
- ""
-
- when "title"
- @options.title = param
- ""
-
- when "section"
- context.set_current_section(param, comment)
- comment.replace ''
- break
-
- else
- warn "Unrecognized directive '#{directive}'"
- break
- end
- end
-
- remove_private_comments(comment)
- end
-
def remove_private_comments(comment)
comment.gsub!(/^#--.*?^#\+\+/m, '')
comment.sub!(/^#--.*/m, '')
end
- def get_symbol_or_name
- tk = get_tk
- case tk
- when TkSYMBOL
- tk.text.sub(/^:/, '')
- when TkId, TkOp
- tk.name
- when TkSTRING
- tk.text
- else
- raise "Name or symbol expected (got #{tk})"
- end
+ def remove_token_listener(obj)
+ @token_listeners.delete(obj)
end
- def parse_alias(context, single, tk, comment)
- skip_tkspace
- if (peek_tk.kind_of? TkLPAREN)
- get_tk
- skip_tkspace
- end
- new_name = get_symbol_or_name
- @scanner.instance_eval{@lex_state = EXPR_FNAME}
- skip_tkspace
- if (peek_tk.kind_of? TkCOMMA)
- get_tk
- skip_tkspace
- end
- old_name = get_symbol_or_name
-
- al = RDoc::Alias.new get_tkread, old_name, new_name, comment
- read_documentation_modifiers al, RDoc::ATTR_MODIFIERS
- if al.document_self
- context.add_alias(al)
- end
+ def reset
+ @tokens = []
+ @unget_read = []
+ @read = []
end
- def parse_yield_parameters
- parse_method_or_yield_parameters
- end
+ def scan
+ reset
- def parse_yield(context, single, tk, method)
- if method.block_params.nil?
- get_tkread
- @scanner.instance_eval{@continue = false}
- method.block_params = parse_yield_parameters
- end
- end
+ catch(:eof) do
+ catch(:enddoc) do
+ begin
+ parse_toplevel_statements(@top_level)
+ rescue Exception => e
+ $stderr.puts <<-EOF
- def parse_require(context, comment)
- skip_tkspace_comment
- tk = get_tk
- if tk.kind_of? TkLPAREN
- skip_tkspace_comment
- tk = get_tk
- end
- name = nil
- case tk
- when TkSTRING
- name = tk.text
- # when TkCONSTANT, TkIDENTIFIER, TkIVAR, TkGVAR
- # name = tk.name
- when TkDSTRING
- warn "Skipping require of dynamic string: #{tk.text}"
- # else
- # warn "'require' used as variable"
- end
- if name
- context.add_require(RDoc::Require.new(name, comment))
- else
- unget_tk(tk)
- end
- end
+RDoc failure in #{@file_name} at or around line #{@scanner.line_no} column
+#{@scanner.char_no}
- def parse_include(context, comment)
- loop do
- skip_tkspace_comment
- name = get_constant_with_optional_parens
- unless name.empty?
- context.add_include RDoc::Include.new(name, comment)
+Before reporting this, could you check that the file you're documenting
+compiles cleanly--RDoc is not a full Ruby parser, and gets confused easily if
+fed invalid programs.
+
+The internal error was:
+
+ EOF
+
+ e.set_backtrace(e.backtrace[0,4])
+ raise
+ end
end
- return unless peek_tk.kind_of?(TkCOMMA)
- get_tk
end
+
+ @top_level
end
- def get_bool
- skip_tkspace
+ ##
+ # while, until, and for have an optional do
+
+ def skip_optional_do_after_expression
+ skip_tkspace(false)
tk = get_tk
case tk
- when TkTRUE
- true
- when TkFALSE, TkNIL
- false
+ when TkLPAREN, TkfLPAREN
+ end_token = TkRPAREN
else
- unget_tk tk
- true
+ end_token = TkNL
end
- end
- def parse_attr(context, single, tk, comment)
- args = parse_symbol_arg(1)
- if args.size > 0
- name = args[0]
- rw = "R"
- skip_tkspace(false)
- tk = get_tk
- if tk.kind_of? TkCOMMA
- rw = "RW" if get_bool
- else
- unget_tk tk
- end
- att = RDoc::Attr.new get_tkread, name, rw, comment
- read_documentation_modifiers att, RDoc::ATTR_MODIFIERS
- if att.document_self
- context.add_attribute(att)
+ nest = 0
+ @scanner.instance_eval{@continue = false}
+
+ loop do
+ puts("\nWhile: #{tk.text.inspect}, #{@scanner.continue} " \
+ "#{@scanner.lex_state} #{nest}") if $DEBUG_RDOC
+ case tk
+ when TkSEMICOLON
+ break
+ when TkLPAREN, TkfLPAREN
+ nest += 1
+ when TkDO
+ break if nest.zero?
+ when end_token
+ if end_token == TkRPAREN
+ nest -= 1
+ break if @scanner.lex_state == EXPR_END and nest.zero?
+ else
+ break unless @scanner.continue
+ end
end
- else
- warn("'attr' ignored - looks like a variable")
+ tk = get_tk
end
+ skip_tkspace(false)
+
+ get_tk if TkDO === peek_tk
end
- def parse_visibility(container, single, tk)
- singleton = (single == SINGLE)
- vis = case tk.name
- when "private" then :private
- when "protected" then :protected
- when "public" then :public
- when "private_class_method"
- singleton = true
- :private
- when "public_class_method"
- singleton = true
- :public
- else raise "Invalid visibility: #{tk.name}"
- end
+ ##
+ # skip the var [in] part of a 'for' statement
- skip_tkspace_comment(false)
- case peek_tk
- # Ryan Davis suggested the extension to ignore modifiers, because he
- # often writes
- #
- # protected unless $TESTING
- #
- when TkNL, TkUNLESS_MOD, TkIF_MOD
- # error("Missing argument") if singleton
- container.ongoing_visibility = vis
- else
- args = parse_symbol_arg
- container.set_visibility_for(args, vis, singleton)
- end
+ def skip_for_variable
+ skip_tkspace(false)
+ tk = get_tk
+ skip_tkspace(false)
+ tk = get_tk
+ unget_tk(tk) unless TkIN === tk
end
- def parse_attr_accessor(context, single, tk, comment)
- args = parse_symbol_arg
- read = get_tkread
- rw = "?"
+ def skip_method(container)
+ meth = RDoc::AnyMethod.new "", "anon"
+ parse_method_parameters(meth)
+ parse_statements(container, false, meth)
+ end
- # If nodoc is given, don't document any of them
+ ##
+ # Skip spaces
- tmp = RDoc::CodeObject.new
- read_documentation_modifiers tmp, RDoc::ATTR_MODIFIERS
- return unless tmp.document_self
+ def skip_tkspace(skip_nl = true)
+ tokens = []
- case tk.name
- when "attr_reader" then rw = "R"
- when "attr_writer" then rw = "W"
- when "attr_accessor" then rw = "RW"
- else
- rw = @options.extra_accessor_flags[tk.name]
+ while TkSPACE === (tk = get_tk) or (skip_nl and TkNL === tk) do
+ tokens.push tk
end
- for name in args
- att = RDoc::Attr.new get_tkread, name, rw, comment
- context.add_attribute att
- end
+ unget_tk(tk)
+ tokens
end
+ ##
+ # Skip spaces until a comment is found
+
def skip_tkspace_comment(skip_nl = true)
loop do
skip_tkspace(skip_nl)
- return unless peek_tk.kind_of? TkCOMMENT
+ return unless TkCOMMENT === peek_tk
get_tk
end
end
- def parse_symbol_arg(no = nil)
- args = []
- skip_tkspace_comment
- case tk = get_tk
- when TkLPAREN
- loop do
- skip_tkspace_comment
- if tk1 = parse_symbol_in_arg
- args.push tk1
- break if no and args.size >= no
- end
-
- skip_tkspace_comment
- case tk2 = get_tk
- when TkRPAREN
- break
- when TkCOMMA
- else
- warn("unexpected token: '#{tk2.inspect}'") if $DEBUG_RDOC
- break
- end
- end
- else
- unget_tk tk
- if tk = parse_symbol_in_arg
- args.push tk
- return args if no and args.size >= no
- end
-
- loop do
- # skip_tkspace_comment(false)
- skip_tkspace(false)
-
- tk1 = get_tk
- unless tk1.kind_of?(TkCOMMA)
- unget_tk tk1
- break
- end
+ def unget_tk(tk)
+ @tokens.unshift tk
+ @unget_read.unshift @read.pop
- skip_tkspace_comment
- if tk = parse_symbol_in_arg
- args.push tk
- break if no and args.size >= no
- end
- end
- end
- args
+ # Remove this token from any listeners
+ @token_listeners.each do |obj|
+ obj.pop_token
+ end if @token_listeners
end
- def parse_symbol_in_arg
- case tk = get_tk
- when TkSYMBOL
- tk.text.sub(/^:/, '')
- when TkSTRING
- eval @read[-1]
- else
- warn("Expected symbol or string, got #{tk.inspect}") if $DEBUG_RDOC
- nil
- end
+ def warn(msg)
+ return if @options.quiet
+ msg = make_message msg
+ $stderr.puts msg
end
end
diff --git a/lib/rdoc/parser/simple.rb b/lib/rdoc/parser/simple.rb
new file mode 100644
index 0000000000..6e123a4655
--- /dev/null
+++ b/lib/rdoc/parser/simple.rb
@@ -0,0 +1,38 @@
+require 'rdoc/parser'
+
+##
+# Parse a non-source file. We basically take the whole thing as one big
+# comment. If the first character in the file is '#', we strip leading pound
+# signs.
+
+class RDoc::Parser::Simple < RDoc::Parser
+
+ parse_files_matching(//)
+
+ ##
+ # Prepare to parse a plain file
+
+ def initialize(top_level, file_name, content, options, stats)
+ super
+
+ preprocess = RDoc::Markup::PreProcess.new @file_name, @options.rdoc_include
+
+ preprocess.handle @content do |directive, param|
+ warn "Unrecognized directive '#{directive}' in #{@file_name}"
+ end
+ end
+
+ ##
+ # Extract the file contents and attach them to the toplevel as a comment
+
+ def scan
+ @top_level.comment = remove_private_comments(@content)
+ @top_level
+ end
+
+ def remove_private_comments(comment)
+ comment.gsub(/^--[^-].*?^\+\+/m, '').sub(/^--.*/m, '')
+ end
+
+end
+
diff --git a/lib/rdoc/parsers/parse_c.rb b/lib/rdoc/parsers/parse_c.rb
deleted file mode 100644
index d398b1aa71..0000000000
--- a/lib/rdoc/parsers/parse_c.rb
+++ /dev/null
@@ -1,775 +0,0 @@
-# Classes and modules built in to the interpreter. We need
-# these to define superclasses of user objects
-
-require "rdoc/code_objects"
-require "rdoc/parsers/parserfactory"
-require "rdoc/rdoc"
-
-module RDoc
-
- ##
- # Ruby's built-in classes.
-
- KNOWN_CLASSES = {
- "rb_cObject" => "Object",
- "rb_cArray" => "Array",
- "rb_cBignum" => "Bignum",
- "rb_cClass" => "Class",
- "rb_cDir" => "Dir",
- "rb_cData" => "Data",
- "rb_cFalseClass" => "FalseClass",
- "rb_cFile" => "File",
- "rb_cFixnum" => "Fixnum",
- "rb_cFloat" => "Float",
- "rb_cHash" => "Hash",
- "rb_cInteger" => "Integer",
- "rb_cIO" => "IO",
- "rb_cModule" => "Module",
- "rb_cNilClass" => "NilClass",
- "rb_cNumeric" => "Numeric",
- "rb_cProc" => "Proc",
- "rb_cRange" => "Range",
- "rb_cRegexp" => "Regexp",
- "rb_cString" => "String",
- "rb_cSymbol" => "Symbol",
- "rb_cThread" => "Thread",
- "rb_cTime" => "Time",
- "rb_cTrueClass" => "TrueClass",
- "rb_cStruct" => "Struct",
- "rb_cRubyVM" => "RubyVM",
- "rb_eException" => "Exception",
- "rb_eStandardError" => "StandardError",
- "rb_eSystemExit" => "SystemExit",
- "rb_eInterrupt" => "Interrupt",
- "rb_eSignal" => "Signal",
- "rb_eFatal" => "Fatal",
- "rb_eArgError" => "ArgError",
- "rb_eEOFError" => "EOFError",
- "rb_eIndexError" => "IndexError",
- "rb_eRangeError" => "RangeError",
- "rb_eIOError" => "IOError",
- "rb_eRuntimeError" => "RuntimeError",
- "rb_eSecurityError" => "SecurityError",
- "rb_eSystemCallError" => "SystemCallError",
- "rb_eTypeError" => "TypeError",
- "rb_eZeroDivError" => "ZeroDivError",
- "rb_eNotImpError" => "NotImpError",
- "rb_eNoMemError" => "NoMemError",
- "rb_eFloatDomainError" => "FloatDomainError",
- "rb_eScriptError" => "ScriptError",
- "rb_eNameError" => "NameError",
- "rb_eSyntaxError" => "SyntaxError",
- "rb_eLoadError" => "LoadError",
-
- "rb_mKernel" => "Kernel",
- "rb_mComparable" => "Comparable",
- "rb_mEnumerable" => "Enumerable",
- "rb_mPrecision" => "Precision",
- "rb_mErrno" => "Errno",
- "rb_mFileTest" => "FileTest",
- "rb_mGC" => "GC",
- "rb_mMath" => "Math",
- "rb_mProcess" => "Process"
- }
-
- ##
- # We attempt to parse C extension files. Basically we look for
- # the standard patterns that you find in extensions: <tt>rb_define_class,
- # rb_define_method</tt> and so on. We also try to find the corresponding
- # C source for the methods and extract comments, but if we fail
- # we don't worry too much.
- #
- # The comments associated with a Ruby method are extracted from the C
- # comment block associated with the routine that _implements_ that
- # method, that is to say the method whose name is given in the
- # <tt>rb_define_method</tt> call. For example, you might write:
- #
- # /*
- # * Returns a new array that is a one-dimensional flattening of this
- # * array (recursively). That is, for every element that is an array,
- # * extract its elements into the new array.
- # *
- # * s = [ 1, 2, 3 ] #=> [1, 2, 3]
- # * t = [ 4, 5, 6, [7, 8] ] #=> [4, 5, 6, [7, 8]]
- # * a = [ s, t, 9, 10 ] #=> [[1, 2, 3], [4, 5, 6, [7, 8]], 9, 10]
- # * a.flatten #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
- # */
- # static VALUE
- # rb_ary_flatten(ary)
- # VALUE ary;
- # {
- # ary = rb_obj_dup(ary);
- # rb_ary_flatten_bang(ary);
- # return ary;
- # }
- #
- # ...
- #
- # void
- # Init_Array()
- # {
- # ...
- # rb_define_method(rb_cArray, "flatten", rb_ary_flatten, 0);
- #
- # Here RDoc will determine from the rb_define_method line that there's a
- # method called "flatten" in class Array, and will look for the implementation
- # in the method rb_ary_flatten. It will then use the comment from that
- # method in the HTML output. This method must be in the same source file
- # as the rb_define_method.
- #
- # C classes can be diagrammed (see /tc/dl/ruby/ruby/error.c), and RDoc
- # integrates C and Ruby source into one tree
- #
- # The comment blocks may include special directives:
- #
- # [Document-class: <i>name</i>]
- # This comment block is documentation for the given class. Use this
- # when the <tt>Init_xxx</tt> method is not named after the class.
- #
- # [Document-method: <i>name</i>]
- # This comment documents the named method. Use when RDoc cannot
- # automatically find the method from it's declaration
- #
- # [call-seq: <i>text up to an empty line</i>]
- # Because C source doesn't give descriptive names to Ruby-level parameters,
- # you need to document the calling sequence explicitly
- #
- # In addition, RDoc assumes by default that the C method implementing a
- # Ruby function is in the same source file as the rb_define_method call.
- # If this isn't the case, add the comment
- #
- # rb_define_method(....); // in: filename
- #
- # As an example, we might have an extension that defines multiple classes
- # in its Init_xxx method. We could document them using
- #
- #
- # /*
- # * Document-class: MyClass
- # *
- # * Encapsulate the writing and reading of the configuration
- # * file. ...
- # */
- #
- # /*
- # * Document-method: read_value
- # *
- # * call-seq:
- # * cfg.read_value(key) -> value
- # * cfg.read_value(key} { |key| } -> value
- # *
- # * Return the value corresponding to +key+ from the configuration.
- # * In the second form, if the key isn't found, invoke the
- # * block and return its value.
- # */
- #
-
- class C_Parser
-
- attr_writer :progress
-
- extend ParserFactory
- parse_files_matching(/\.(?:([CcHh])\1?|c([+xp])\2|y)\z/)
-
- @@enclosure_classes = {}
- @@known_bodies = {}
-
- # prepare to parse a C file
- def initialize(top_level, file_name, body, options, stats)
- @known_classes = KNOWN_CLASSES.dup
- @options = options
- @body = handle_tab_width(handle_ifdefs_in(body))
- @stats = stats
- @top_level = top_level
- @classes = Hash.new
- @file_dir = File.dirname(file_name)
- @progress = $stderr unless @options.quiet
- end
-
- # Extract the classes/modules and methods from a C file
- # and return the corresponding top-level object
- def scan
- remove_commented_out_lines
- do_classes
- do_constants
- do_methods
- do_includes
- do_aliases
- @top_level
- end
-
- #######
- private
- #######
-
- def progress(char)
- unless @options.quiet
- @progress.print(char)
- @progress.flush
- end
- end
-
- def warn(msg)
- $stderr.puts
- $stderr.puts msg
- $stderr.flush
- end
-
- def remove_private_comments(comment)
- comment.gsub!(/\/?\*--(.*?)\/?\*\+\+/m, '')
- comment.sub!(/\/?\*--.*/m, '')
- end
-
- ##
- # removes lines that are commented out that might otherwise get picked up
- # when scanning for classes and methods
-
- def remove_commented_out_lines
- @body.gsub!(%r{//.*rb_define_}, '//')
- end
-
- def handle_class_module(var_name, class_mod, class_name, parent, in_module)
- progress(class_mod[0, 1])
-
- parent_name = @known_classes[parent] || parent
-
- if in_module
- enclosure = @classes[in_module] || @@enclosure_classes[in_module]
- unless enclosure
- if enclosure = @known_classes[in_module]
- handle_class_module(in_module, (/^rb_m/ =~ in_module ? "module" : "class"),
- enclosure, nil, nil)
- enclosure = @classes[in_module]
- end
- end
- unless enclosure
- warn("Enclosing class/module '#{in_module}' for " +
- "#{class_mod} #{class_name} not known")
- return
- end
- else
- enclosure = @top_level
- end
-
- if class_mod == "class"
- cm = enclosure.add_class(NormalClass, class_name, parent_name)
- @stats.num_classes += 1
- else
- cm = enclosure.add_module(NormalModule, class_name)
- @stats.num_modules += 1
- end
- cm.record_location(enclosure.toplevel)
-
- find_class_comment(cm.full_name, cm)
- @classes[var_name] = cm
- @@enclosure_classes[var_name] = cm
- @known_classes[var_name] = cm.full_name
- end
-
- ##
- # Look for class or module documentation above Init_+class_name+(void),
- # in a Document-class +class_name+ (or module) comment or above an
- # rb_define_class (or module). If a comment is supplied above a matching
- # Init_ and a rb_define_class the Init_ comment is used.
- #
- # /*
- # * This is a comment for Foo
- # */
- # Init_Foo(void) {
- # VALUE cFoo = rb_define_class("Foo", rb_cObject);
- # }
- #
- # /*
- # * Document-class: Foo
- # * This is a comment for Foo
- # */
- # Init_foo(void) {
- # VALUE cFoo = rb_define_class("Foo", rb_cObject);
- # }
- #
- # /*
- # * This is a comment for Foo
- # */
- # VALUE cFoo = rb_define_class("Foo", rb_cObject);
-
- def find_class_comment(class_name, class_meth)
- comment = nil
- if @body =~ %r{((?>/\*.*?\*/\s+))
- (static\s+)?void\s+Init_#{class_name}\s*(?:_\(\s*)?\(\s*(?:void\s*)\)}xmi
- comment = $1
- elsif @body =~ %r{Document-(class|module):\s#{class_name}\s*?\n((?>.*?\*/))}m
- comment = $2
- else
- if @body =~ /rb_define_(class|module)/m then
- class_name = class_name.split("::").last
- comments = []
- @body.split(/(\/\*.*?\*\/)\s*?\n/m).each_with_index do |chunk, index|
- comments[index] = chunk
- if chunk =~ /rb_define_(class|module).*?"(#{class_name})"/m then
- comment = comments[index-1]
- break
- end
- end
- end
- end
- class_meth.comment = mangle_comment(comment) if comment
- end
-
- ############################################################
-
- def do_classes
- @body.scan(/(\w+)\s* = \s*rb_define_module\s*\(\s*"(\w+)"\s*\)/mx) do
- |var_name, class_name|
- handle_class_module(var_name, "module", class_name, nil, nil)
- end
-
- # The '.' lets us handle SWIG-generated files
- @body.scan(/([\w\.]+)\s* = \s*rb_define_class\s*
- \(
- \s*"(\w+)",
- \s*(\w+)\s*
- \)/mx) do
-
- |var_name, class_name, parent|
- handle_class_module(var_name, "class", class_name, parent, nil)
- end
-
- @body.scan(/(\w+)\s*=\s*boot_defclass\s*\(\s*"(\w+?)",\s*(\w+?)\s*\)/) do
- |var_name, class_name, parent|
- parent = nil if parent == "0"
- handle_class_module(var_name, "class", class_name, parent, nil)
- end
-
- @body.scan(/(\w+)\s* = \s*rb_define_module_under\s*
- \(
- \s*(\w+),
- \s*"(\w+)"
- \s*\)/mx) do
-
- |var_name, in_module, class_name|
- handle_class_module(var_name, "module", class_name, nil, in_module)
- end
-
- @body.scan(/([\w\.]+)\s* = \s*rb_define_class_under\s*
- \(
- \s*(\w+),
- \s*"(\w+)",
- \s*(\w+)\s*
- \s*\)/mx) do
-
- |var_name, in_module, class_name, parent|
- handle_class_module(var_name, "class", class_name, parent, in_module)
- end
-
- end
-
- ###########################################################
-
- def do_constants
- @body.scan(%r{\Wrb_define_
- (
- variable |
- readonly_variable |
- const |
- global_const |
- )
- \s*\(
- (?:\s*(\w+),)?
- \s*"(\w+)",
- \s*(.*?)\s*\)\s*;
- }xm) do
-
- |type, var_name, const_name, definition|
- var_name = "rb_cObject" if !var_name or var_name == "rb_mKernel"
- handle_constants(type, var_name, const_name, definition)
- end
- end
-
- ############################################################
-
- def do_methods
-
- @body.scan(%r{rb_define_
- (
- singleton_method |
- method |
- module_function |
- private_method
- )
- \s*\(\s*([\w\.]+),
- \s*"([^"]+)",
- \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
- \s*(-?\w+)\s*\)
- (?:;\s*/[*/]\s+in\s+(\w+?\.[cy]))?
- }xm) do
- |type, var_name, meth_name, meth_body, param_count, source_file|
- #"
-
- # Ignore top-object and weird struct.c dynamic stuff
- next if var_name == "ruby_top_self"
- next if var_name == "nstr"
- next if var_name == "envtbl"
- next if var_name == "argf" # it'd be nice to handle this one
-
- var_name = "rb_cObject" if var_name == "rb_mKernel"
- handle_method(type, var_name, meth_name,
- meth_body, param_count, source_file)
- end
-
- @body.scan(%r{rb_define_attr\(
- \s*([\w\.]+),
- \s*"([^"]+)",
- \s*(\d+),
- \s*(\d+)\s*\);
- }xm) do #"
- |var_name, attr_name, attr_reader, attr_writer|
-
- #var_name = "rb_cObject" if var_name == "rb_mKernel"
- handle_attr(var_name, attr_name,
- attr_reader.to_i != 0,
- attr_writer.to_i != 0)
- end
-
- @body.scan(%r{rb_define_global_function\s*\(
- \s*"([^"]+)",
- \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
- \s*(-?\w+)\s*\)
- (?:;\s*/[*/]\s+in\s+(\w+?\.[cy]))?
- }xm) do #"
- |meth_name, meth_body, param_count, source_file|
- handle_method("method", "rb_mKernel", meth_name,
- meth_body, param_count, source_file)
- end
-
- @body.scan(/define_filetest_function\s*\(
- \s*"([^"]+)",
- \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
- \s*(-?\w+)\s*\)/xm) do #"
- |meth_name, meth_body, param_count|
-
- handle_method("method", "rb_mFileTest", meth_name, meth_body, param_count)
- handle_method("singleton_method", "rb_cFile", meth_name, meth_body, param_count)
- end
- end
-
- ############################################################
-
- def do_aliases
- @body.scan(%r{rb_define_alias\s*\(\s*(\w+),\s*"([^"]+)",\s*"([^"]+)"\s*\)}m) do
- |var_name, new_name, old_name|
- @stats.num_methods += 1
- class_name = @known_classes[var_name] || var_name
- class_obj = find_class(var_name, class_name)
-
- class_obj.add_alias(Alias.new("", old_name, new_name, ""))
- end
- end
-
- ##
- # Adds constant comments. By providing some_value: at the start ofthe
- # comment you can override the C value of the comment to give a friendly
- # definition.
- #
- # /* 300: The perfect score in bowling */
- # rb_define_const(cFoo, "PERFECT", INT2FIX(300);
- #
- # Will override +INT2FIX(300)+ with the value +300+ in the output RDoc.
- # Values may include quotes and escaped colons (\:).
-
- def handle_constants(type, var_name, const_name, definition)
- #@stats.num_constants += 1
- class_name = @known_classes[var_name]
-
- return unless class_name
-
- class_obj = find_class(var_name, class_name)
-
- unless class_obj
- warn("Enclosing class/module '#{const_name}' for not known")
- return
- end
-
- comment = find_const_comment(type, const_name)
-
- # In the case of rb_define_const, the definition and comment are in
- # "/* definition: comment */" form. The literal ':' and '\' characters
- # can be escaped with a backslash.
- if type.downcase == 'const' then
- elements = mangle_comment(comment).split(':')
- if elements.nil? or elements.empty? then
- con = Constant.new(const_name, definition, mangle_comment(comment))
- else
- new_definition = elements[0..-2].join(':')
- if new_definition.empty? then # Default to literal C definition
- new_definition = definition
- else
- new_definition.gsub!("\:", ":")
- new_definition.gsub!("\\", '\\')
- end
- new_definition.sub!(/\A(\s+)/, '')
- new_comment = $1.nil? ? elements.last : "#{$1}#{elements.last.lstrip}"
- con = Constant.new(const_name, new_definition,
- mangle_comment(new_comment))
- end
- else
- con = Constant.new(const_name, definition, mangle_comment(comment))
- end
-
- class_obj.add_constant(con)
- end
-
- ##
- # Finds a comment matching +type+ and +const_name+ either above the
- # comment or in the matching Document- section.
-
- def find_const_comment(type, const_name)
- if @body =~ %r{((?>^\s*/\*.*?\*/\s+))
- rb_define_#{type}\((?:\s*(\w+),)?\s*"#{const_name}"\s*,.*?\)\s*;}xmi
- $1
- elsif @body =~ %r{Document-(?:const|global|variable):\s#{const_name}\s*?\n((?>.*?\*/))}m
- $1
- else
- ''
- end
- end
-
- ###########################################################
-
- def handle_attr(var_name, attr_name, reader, writer)
- rw = ''
- if reader
- #@stats.num_methods += 1
- rw << 'R'
- end
- if writer
- #@stats.num_methods += 1
- rw << 'W'
- end
-
- class_name = @known_classes[var_name]
-
- return unless class_name
-
- class_obj = find_class(var_name, class_name)
-
- if class_obj
- comment = find_attr_comment(attr_name)
- unless comment.empty?
- comment = mangle_comment(comment)
- end
- att = Attr.new('', attr_name, rw, comment)
- class_obj.add_attribute(att)
- end
-
- end
-
- ###########################################################
-
- def find_attr_comment(attr_name)
- if @body =~ %r{((?>/\*.*?\*/\s+))
- rb_define_attr\((?:\s*(\w+),)?\s*"#{attr_name}"\s*,.*?\)\s*;}xmi
- $1
- elsif @body =~ %r{Document-attr:\s#{attr_name}\s*?\n((?>.*?\*/))}m
- $1
- else
- ''
- end
- end
-
- ###########################################################
-
- def handle_method(type, var_name, meth_name,
- meth_body, param_count, source_file = nil)
- progress(".")
-
- @stats.num_methods += 1
- class_name = @known_classes[var_name]
-
- return unless class_name
-
- class_obj = find_class(var_name, class_name)
-
- if class_obj
- if meth_name == "initialize"
- meth_name = "new"
- type = "singleton_method"
- end
- meth_obj = AnyMethod.new("", meth_name)
- meth_obj.singleton =
- %w{singleton_method module_function}.include?(type)
-
- p_count = (Integer(param_count) rescue -1)
-
- if p_count < 0
- meth_obj.params = "(...)"
- elsif p_count == 0
- meth_obj.params = "()"
- else
- meth_obj.params = "(" +
- (1..p_count).map{|i| "p#{i}"}.join(", ") +
- ")"
- end
-
- if source_file
- file_name = File.join(@file_dir, source_file)
- body = (@@known_bodies[source_file] ||= File.read(file_name))
- else
- body = @body
- end
- if find_body(meth_body, meth_obj, body) and meth_obj.document_self
- class_obj.add_method(meth_obj)
- end
- end
- end
-
- ############################################################
-
- # Find the C code corresponding to a Ruby method
- def find_body(meth_name, meth_obj, body, quiet = false)
- case body
- when %r"((?>/\*.*?\*/\s*))(?:static\s+)?VALUE\s+#{meth_name}
- \s*(\([^)]*\))\s*\{.*?^\}"xm
- comment, params = $1, $2
- body_text = $&
-
- remove_private_comments(comment) if comment
-
- # see if we can find the whole body
-
- re = Regexp.escape(body_text) + '[^(]*^\{.*?^\}'
- if Regexp.new(re, Regexp::MULTILINE).match(body)
- body_text = $&
- end
-
- # The comment block may have been overridden with a
- # 'Document-method' block. This happens in the interpreter
- # when multiple methods are vectored through to the same
- # C method but those methods are logically distinct (for
- # example Kernel.hash and Kernel.object_id share the same
- # implementation
-
- override_comment = find_override_comment(meth_obj.name)
- comment = override_comment if override_comment
-
- find_modifiers(comment, meth_obj) if comment
-
-# meth_obj.params = params
- meth_obj.start_collecting_tokens
- meth_obj.add_token(RubyToken::Token.new(1,1).set_text(body_text))
- meth_obj.comment = mangle_comment(comment)
- when %r{((?>/\*.*?\*/\s*))^\s*\#\s*define\s+#{meth_name}\s+(\w+)}m
- comment = $1
- find_body($2, meth_obj, body, true)
- find_modifiers(comment, meth_obj)
- meth_obj.comment = mangle_comment(comment) + meth_obj.comment
- when %r{^\s*\#\s*define\s+#{meth_name}\s+(\w+)}m
- unless find_body($1, meth_obj, body, true)
- warn "No definition for #{meth_name}" unless quiet
- return false
- end
- else
-
- # No body, but might still have an override comment
- comment = find_override_comment(meth_obj.name)
-
- if comment
- find_modifiers(comment, meth_obj)
- meth_obj.comment = mangle_comment(comment)
- else
- warn "No definition for #{meth_name}" unless quiet
- return false
- end
- end
- true
- end
-
-
- ##
- # If the comment block contains a section that looks like:
- #
- # call-seq:
- # Array.new
- # Array.new(10)
- #
- # use it for the parameters.
-
- def find_modifiers(comment, meth_obj)
- if comment.sub!(/:nodoc:\s*^\s*\*?\s*$/m, '') or
- comment.sub!(/\A\/\*\s*:nodoc:\s*\*\/\Z/, '')
- meth_obj.document_self = false
- end
- if comment.sub!(/call-seq:(.*?)^\s*\*?\s*$/m, '') or
- comment.sub!(/\A\/\*\s*call-seq:(.*?)\*\/\Z/, '')
- seq = $1
- seq.gsub!(/^\s*\*\s*/, '')
- meth_obj.call_seq = seq
- end
- end
-
- ############################################################
-
- def find_override_comment(meth_name)
- name = Regexp.escape(meth_name)
- if @body =~ %r{Document-method:\s#{name}\s*?\n((?>.*?\*/))}m
- $1
- end
- end
-
- ##
- # Look for includes of the form:
- #
- # rb_include_module(rb_cArray, rb_mEnumerable);
-
- def do_includes
- @body.scan(/rb_include_module\s*\(\s*(\w+?),\s*(\w+?)\s*\)/) do |c,m|
- if cls = @classes[c]
- m = @known_classes[m] || m
- cls.add_include(Include.new(m, ""))
- end
- end
- end
-
- ##
- # Remove the /*'s and leading asterisks from C comments
-
- def mangle_comment(comment)
- comment.sub!(%r{/\*+}) { " " * $&.length }
- comment.sub!(%r{\*+/}) { " " * $&.length }
- comment.gsub!(/^[ \t]*\*/m) { " " * $&.length }
- comment
- end
-
- def find_class(raw_name, name)
- unless @classes[raw_name]
- if raw_name =~ /^rb_m/
- @classes[raw_name] = @top_level.add_module(NormalModule, name)
- else
- @classes[raw_name] = @top_level.add_class(NormalClass, name, nil)
- end
- end
- @classes[raw_name]
- end
-
- def handle_tab_width(body)
- if /\t/ =~ body
- tab_width = @options.tab_width
- body.split(/\n/).map do |line|
- 1 while line.gsub!(/\t+/) { ' ' * (tab_width*$&.length - $`.length % tab_width)} && $~ #`
- line
- end .join("\n")
- else
- body
- end
- end
-
- ##
- # Removes #ifdefs that would otherwise confuse us
-
- def handle_ifdefs_in(body)
- body.gsub(/^#ifdef HAVE_PROTOTYPES.*?#else.*?\n(.*?)#endif.*?\n/m, '\1')
- end
-
- end
-
-end
-
diff --git a/lib/rdoc/parsers/parse_f95.rb b/lib/rdoc/parsers/parse_f95.rb
deleted file mode 100644
index f1a1ae4d4b..0000000000
--- a/lib/rdoc/parsers/parse_f95.rb
+++ /dev/null
@@ -1,1841 +0,0 @@
-#= parse_f95.rb - Fortran95 Parser
-#
-#== Overview
-#
-#"parse_f95.rb" parses Fortran95 files with suffixes "f90", "F90", "f95"
-#and "F95". Fortran95 files are expected to be conformed to Fortran95
-#standards.
-#
-#== Rules
-#
-#Fundamental rules are same as that of the Ruby parser.
-#But comment markers are '!' not '#'.
-#
-#=== Correspondence between RDoc documentation and Fortran95 programs
-#
-#"parse_f95.rb" parses main programs, modules, subroutines, functions,
-#derived-types, public variables, public constants,
-#defined operators and defined assignments.
-#These components are described in items of RDoc documentation, as follows.
-#
-#Files :: Files (same as Ruby)
-#Classes :: Modules
-#Methods :: Subroutines, functions, variables, constants, derived-types, defined operators, defined assignments
-#Required files :: Files in which imported modules, external subroutines and external functions are defined.
-#Included Modules :: List of imported modules
-#Attributes :: List of derived-types, List of imported modules all of whose components are published again
-#
-#Components listed in 'Methods' (subroutines, functions, ...)
-#defined in modules are described in the item of 'Classes'.
-#On the other hand, components defined in main programs or
-#as external procedures are described in the item of 'Files'.
-#
-#=== Components parsed by default
-#
-#By default, documentation on public components (subroutines, functions,
-#variables, constants, derived-types, defined operators,
-#defined assignments) are generated.
-#With "--all" option, documentation on all components
-#are generated (almost same as the Ruby parser).
-#
-#=== Information parsed automatically
-#
-#The following information is automatically parsed.
-#
-#* Types of arguments
-#* Types of variables and constants
-#* Types of variables in the derived types, and initial values
-#* NAMELISTs and types of variables in them, and initial values
-#
-#Aliases by interface statement are described in the item of 'Methods'.
-#
-#Components which are imported from other modules and published again
-#are described in the item of 'Methods'.
-#
-#=== Format of comment blocks
-#
-#Comment blocks should be written as follows.
-#Comment blocks are considered to be ended when the line without '!'
-#appears.
-#The indentation is not necessary.
-#
-# ! (Top of file)
-# !
-# ! Comment blocks for the files.
-# !
-# !--
-# ! The comment described in the part enclosed by
-# ! "!--" and "!++" is ignored.
-# !++
-# !
-# module hogehoge
-# !
-# ! Comment blocks for the modules (or the programs).
-# !
-#
-# private
-#
-# logical :: a ! a private variable
-# real, public :: b ! a public variable
-# integer, parameter :: c = 0 ! a public constant
-#
-# public :: c
-# public :: MULTI_ARRAY
-# public :: hoge, foo
-#
-# type MULTI_ARRAY
-# !
-# ! Comment blocks for the derived-types.
-# !
-# real, pointer :: var(:) =>null() ! Comments block for the variables.
-# integer :: num = 0
-# end type MULTI_ARRAY
-#
-# contains
-#
-# subroutine hoge( in, & ! Comment blocks between continuation lines are ignored.
-# & out )
-# !
-# ! Comment blocks for the subroutines or functions
-# !
-# character(*),intent(in):: in ! Comment blocks for the arguments.
-# character(*),intent(out),allocatable,target :: in
-# ! Comment blocks can be
-# ! written under Fortran statements.
-#
-# character(32) :: file ! This comment parsed as a variable in below NAMELIST.
-# integer :: id
-#
-# namelist /varinfo_nml/ file, id
-# !
-# ! Comment blocks for the NAMELISTs.
-# ! Information about variables are described above.
-# !
-#
-# ....
-#
-# end subroutine hoge
-#
-# integer function foo( in )
-# !
-# ! This part is considered as comment block.
-#
-# ! Comment blocks under blank lines are ignored.
-# !
-# integer, intent(in):: inA ! This part is considered as comment block.
-#
-# ! This part is ignored.
-#
-# end function foo
-#
-# subroutine hide( in, &
-# & out ) !:nodoc:
-# !
-# ! If "!:nodoc:" is described at end-of-line in subroutine
-# ! statement as above, the subroutine is ignored.
-# ! This assignment can be used to modules, subroutines,
-# ! functions, variables, constants, derived-types,
-# ! defined operators, defined assignments,
-# ! list of imported modules ("use" statement).
-# !
-#
-# ....
-#
-# end subroutine hide
-#
-# end module hogehoge
-#
-
-
-require "rdoc/code_objects"
-
-module RDoc
-
- class Token
-
- NO_TEXT = "??".freeze
-
- def initialize(line_no, char_no)
- @line_no = line_no
- @char_no = char_no
- @text = NO_TEXT
- end
- # Because we're used in contexts that expect to return a token,
- # we set the text string and then return ourselves
- def set_text(text)
- @text = text
- self
- end
-
- attr_reader :line_no, :char_no, :text
-
- end
-
- # See rdoc/parsers/parse_f95.rb
-
- class Fortran95parser
-
- extend ParserFactory
- parse_files_matching(/\.((f|F)9(0|5)|F)$/)
-
- @@external_aliases = []
- @@public_methods = []
-
- # "false":: Comments are below source code
- # "true" :: Comments are upper source code
- COMMENTS_ARE_UPPER = false
-
- # Internal alias message
- INTERNAL_ALIAS_MES = "Alias for"
-
- # External alias message
- EXTERNAL_ALIAS_MES = "The entity is"
-
- # prepare to parse a Fortran 95 file
- def initialize(top_level, file_name, body, options, stats)
- @body = body
- @stats = stats
- @file_name = file_name
- @options = options
- @top_level = top_level
- @progress = $stderr unless options.quiet
- end
-
- # define code constructs
- def scan
-
- # remove private comment
- remaining_code = remove_private_comments(@body)
-
- # continuation lines are united to one line
- remaining_code = united_to_one_line(remaining_code)
-
- # semicolons are replaced to line feed
- remaining_code = semicolon_to_linefeed(remaining_code)
-
- # collect comment for file entity
- whole_comment, remaining_code = collect_first_comment(remaining_code)
- @top_level.comment = whole_comment
-
- # String "remaining_code" is converted to Array "remaining_lines"
- remaining_lines = remaining_code.split("\n")
-
- # "module" or "program" parts are parsed (new)
- #
- level_depth = 0
- block_searching_flag = nil
- block_searching_lines = []
- pre_comment = []
- module_program_trailing = ""
- module_program_name = ""
- other_block_level_depth = 0
- other_block_searching_flag = nil
- remaining_lines.collect!{|line|
- if !block_searching_flag && !other_block_searching_flag
- if line =~ /^\s*?module\s+(\w+)\s*?(!.*?)?$/i
- block_searching_flag = :module
- block_searching_lines << line
- module_program_name = $1
- module_program_trailing = find_comments($2)
- next false
- elsif line =~ /^\s*?program\s+(\w+)\s*?(!.*?)?$/i ||
- line =~ /^\s*?\w/ && !block_start?(line)
- block_searching_flag = :program
- block_searching_lines << line
- module_program_name = $1 || ""
- module_program_trailing = find_comments($2)
- next false
-
- elsif block_start?(line)
- other_block_searching_flag = true
- next line
-
- elsif line =~ /^\s*?!\s?(.*)/
- pre_comment << line
- next line
- else
- pre_comment = []
- next line
- end
- elsif other_block_searching_flag
- other_block_level_depth += 1 if block_start?(line)
- other_block_level_depth -= 1 if block_end?(line)
- if other_block_level_depth < 0
- other_block_level_depth = 0
- other_block_searching_flag = nil
- end
- next line
- end
-
- block_searching_lines << line
- level_depth += 1 if block_start?(line)
- level_depth -= 1 if block_end?(line)
- if level_depth >= 0
- next false
- end
-
- # "module_program_code" is formatted.
- # ":nodoc:" flag is checked.
- #
- module_program_code = block_searching_lines.join("\n")
- module_program_code = remove_empty_head_lines(module_program_code)
- if module_program_trailing =~ /^:nodoc:/
- # next loop to search next block
- level_depth = 0
- block_searching_flag = false
- block_searching_lines = []
- pre_comment = []
- next false
- end
-
- # NormalClass is created, and added to @top_level
- #
- if block_searching_flag == :module
- module_name = module_program_name
- module_code = module_program_code
- module_trailing = module_program_trailing
- progress "m"
- @stats.num_modules += 1
- f9x_module = @top_level.add_module NormalClass, module_name
- f9x_module.record_location @top_level
-
- f9x_comment = COMMENTS_ARE_UPPER ?
- find_comments(pre_comment.join("\n")) + "\n" + module_trailing :
- module_trailing + "\n" + find_comments(module_code.sub(/^.*$\n/i, ''))
- f9x_module.comment = f9x_comment
- parse_program_or_module(f9x_module, module_code)
-
- TopLevel.all_files.each do |name, toplevel|
- if toplevel.include_includes?(module_name, @options.ignore_case)
- if !toplevel.include_requires?(@file_name, @options.ignore_case)
- toplevel.add_require(Require.new(@file_name, ""))
- end
- end
- toplevel.each_classmodule{|m|
- if m.include_includes?(module_name, @options.ignore_case)
- if !m.include_requires?(@file_name, @options.ignore_case)
- m.add_require(Require.new(@file_name, ""))
- end
- end
- }
- end
- elsif block_searching_flag == :program
- program_name = module_program_name
- program_code = module_program_code
- program_trailing = module_program_trailing
- progress "p"
- program_comment = COMMENTS_ARE_UPPER ?
- find_comments(pre_comment.join("\n")) + "\n" + program_trailing :
- program_trailing + "\n" + find_comments(program_code.sub(/^.*$\n/i, ''))
- program_comment = "\n\n= <i>Program</i> <tt>#{program_name}</tt>\n\n" \
- + program_comment
- @top_level.comment << program_comment
- parse_program_or_module(@top_level, program_code, :private)
- end
-
- # next loop to search next block
- level_depth = 0
- block_searching_flag = false
- block_searching_lines = []
- pre_comment = []
- next false
- }
-
- remaining_lines.delete_if{ |line|
- line == false
- }
-
- # External subprograms and functions are parsed
- #
- parse_program_or_module(@top_level, remaining_lines.join("\n"),
- :public, true)
-
- @top_level
- end # End of scan
-
- private
-
- def parse_program_or_module(container, code,
- visibility=:public, external=nil)
- return unless container
- return unless code
- remaining_lines = code.split("\n")
- remaining_code = "#{code}"
-
- #
- # Parse variables before "contains" in module
- #
- level_depth = 0
- before_contains_lines = []
- before_contains_code = nil
- before_contains_flag = nil
- remaining_lines.each{ |line|
- if !before_contains_flag
- if line =~ /^\s*?module\s+\w+\s*?(!.*?)?$/i
- before_contains_flag = true
- end
- else
- break if line =~ /^\s*?contains\s*?(!.*?)?$/i
- level_depth += 1 if block_start?(line)
- level_depth -= 1 if block_end?(line)
- break if level_depth < 0
- before_contains_lines << line
- end
- }
- before_contains_code = before_contains_lines.join("\n")
- if before_contains_code
- before_contains_code.gsub!(/^\s*?interface\s+.*?\s+end\s+interface.*?$/im, "")
- before_contains_code.gsub!(/^\s*?type[\s\,]+.*?\s+end\s+type.*?$/im, "")
- end
-
- #
- # Parse global "use"
- #
- use_check_code = "#{before_contains_code}"
- cascaded_modules_list = []
- while use_check_code =~ /^\s*?use\s+(\w+)(.*?)(!.*?)?$/i
- use_check_code = $~.pre_match
- use_check_code << $~.post_match
- used_mod_name = $1.strip.chomp
- used_list = $2 || ""
- used_trailing = $3 || ""
- next if used_trailing =~ /!:nodoc:/
- if !container.include_includes?(used_mod_name, @options.ignore_case)
- progress "."
- container.add_include Include.new(used_mod_name, "")
- end
- if ! (used_list =~ /\,\s*?only\s*?:/i )
- cascaded_modules_list << "\#" + used_mod_name
- end
- end
-
- #
- # Parse public and private, and store information.
- # This information is used when "add_method" and
- # "set_visibility_for" are called.
- #
- visibility_default, visibility_info =
- parse_visibility(remaining_lines.join("\n"), visibility, container)
- @@public_methods.concat visibility_info
- if visibility_default == :public
- if !cascaded_modules_list.empty?
- cascaded_modules =
- Attr.new("Cascaded Modules",
- "Imported modules all of whose components are published again",
- "",
- cascaded_modules_list.join(", "))
- container.add_attribute(cascaded_modules)
- end
- end
-
- #
- # Check rename elements
- #
- use_check_code = "#{before_contains_code}"
- while use_check_code =~ /^\s*?use\s+(\w+)\s*?\,(.+)$/i
- use_check_code = $~.pre_match
- use_check_code << $~.post_match
- used_mod_name = $1.strip.chomp
- used_elements = $2.sub(/\s*?only\s*?:\s*?/i, '')
- used_elements.split(",").each{ |used|
- if /\s*?(\w+)\s*?=>\s*?(\w+)\s*?/ =~ used
- local = $1
- org = $2
- @@public_methods.collect!{ |pub_meth|
- if local == pub_meth["name"] ||
- local.upcase == pub_meth["name"].upcase &&
- @options.ignore_case
- pub_meth["name"] = org
- pub_meth["local_name"] = local
- end
- pub_meth
- }
- end
- }
- end
-
- #
- # Parse private "use"
- #
- use_check_code = remaining_lines.join("\n")
- while use_check_code =~ /^\s*?use\s+(\w+)(.*?)(!.*?)?$/i
- use_check_code = $~.pre_match
- use_check_code << $~.post_match
- used_mod_name = $1.strip.chomp
- used_trailing = $3 || ""
- next if used_trailing =~ /!:nodoc:/
- if !container.include_includes?(used_mod_name, @options.ignore_case)
- progress "."
- container.add_include Include.new(used_mod_name, "")
- end
- end
-
- container.each_includes{ |inc|
- TopLevel.all_files.each do |name, toplevel|
- indicated_mod = toplevel.find_symbol(inc.name,
- nil, @options.ignore_case)
- if indicated_mod
- indicated_name = indicated_mod.parent.file_relative_name
- if !container.include_requires?(indicated_name, @options.ignore_case)
- container.add_require(Require.new(indicated_name, ""))
- end
- break
- end
- end
- }
-
- #
- # Parse derived-types definitions
- #
- derived_types_comment = ""
- remaining_code = remaining_lines.join("\n")
- while remaining_code =~ /^\s*?
- type[\s\,]+(public|private)?\s*?(::)?\s*?
- (\w+)\s*?(!.*?)?$
- (.*?)
- ^\s*?end\s+type.*?$
- /imx
- remaining_code = $~.pre_match
- remaining_code << $~.post_match
- typename = $3.chomp.strip
- type_elements = $5 || ""
- type_code = remove_empty_head_lines($&)
- type_trailing = find_comments($4)
- next if type_trailing =~ /^:nodoc:/
- type_visibility = $1
- type_comment = COMMENTS_ARE_UPPER ?
- find_comments($~.pre_match) + "\n" + type_trailing :
- type_trailing + "\n" + find_comments(type_code.sub(/^.*$\n/i, ''))
- type_element_visibility_public = true
- type_code.split("\n").each{ |line|
- if /^\s*?private\s*?$/ =~ line
- type_element_visibility_public = nil
- break
- end
- } if type_code
-
- args_comment = ""
- type_args_info = nil
-
- if @options.show_all
- args_comment = find_arguments(nil, type_code, true)
- else
- type_public_args_list = []
- type_args_info = definition_info(type_code)
- type_args_info.each{ |arg|
- arg_is_public = type_element_visibility_public
- arg_is_public = true if arg.include_attr?("public")
- arg_is_public = nil if arg.include_attr?("private")
- type_public_args_list << arg.varname if arg_is_public
- }
- args_comment = find_arguments(type_public_args_list, type_code)
- end
-
- type = AnyMethod.new("type #{typename}", typename)
- type.singleton = false
- type.params = ""
- type.comment = "<b><em> Derived Type </em></b> :: <tt></tt>\n"
- type.comment << args_comment if args_comment
- type.comment << type_comment if type_comment
- progress "t"
- @stats.num_methods += 1
- container.add_method type
-
- set_visibility(container, typename, visibility_default, @@public_methods)
-
- if type_visibility
- type_visibility.gsub!(/\s/,'')
- type_visibility.gsub!(/\,/,'')
- type_visibility.gsub!(/:/,'')
- type_visibility.downcase!
- if type_visibility == "public"
- container.set_visibility_for([typename], :public)
- elsif type_visibility == "private"
- container.set_visibility_for([typename], :private)
- end
- end
-
- check_public_methods(type, container.name)
-
- if @options.show_all
- derived_types_comment << ", " unless derived_types_comment.empty?
- derived_types_comment << typename
- else
- if type.visibility == :public
- derived_types_comment << ", " unless derived_types_comment.empty?
- derived_types_comment << typename
- end
- end
-
- end
-
- if !derived_types_comment.empty?
- derived_types_table =
- Attr.new("Derived Types", "Derived_Types", "",
- derived_types_comment)
- container.add_attribute(derived_types_table)
- end
-
- #
- # move interface scope
- #
- interface_code = ""
- while remaining_code =~ /^\s*?
- interface(
- \s+\w+ |
- \s+operator\s*?\(.*?\) |
- \s+assignment\s*?\(\s*?=\s*?\)
- )?\s*?$
- (.*?)
- ^\s*?end\s+interface.*?$
- /imx
- interface_code << remove_empty_head_lines($&) + "\n"
- remaining_code = $~.pre_match
- remaining_code << $~.post_match
- end
-
- #
- # Parse global constants or variables in modules
- #
- const_var_defs = definition_info(before_contains_code)
- const_var_defs.each{|defitem|
- next if defitem.nodoc
- const_or_var_type = "Variable"
- const_or_var_progress = "v"
- if defitem.include_attr?("parameter")
- const_or_var_type = "Constant"
- const_or_var_progress = "c"
- end
- const_or_var = AnyMethod.new(const_or_var_type, defitem.varname)
- const_or_var.singleton = false
- const_or_var.params = ""
- self_comment = find_arguments([defitem.varname], before_contains_code)
- const_or_var.comment = "<b><em>" + const_or_var_type + "</em></b> :: <tt></tt>\n"
- const_or_var.comment << self_comment if self_comment
- progress const_or_var_progress
- @stats.num_methods += 1
- container.add_method const_or_var
-
- set_visibility(container, defitem.varname, visibility_default, @@public_methods)
-
- if defitem.include_attr?("public")
- container.set_visibility_for([defitem.varname], :public)
- elsif defitem.include_attr?("private")
- container.set_visibility_for([defitem.varname], :private)
- end
-
- check_public_methods(const_or_var, container.name)
-
- } if const_var_defs
-
- remaining_lines = remaining_code.split("\n")
-
- # "subroutine" or "function" parts are parsed (new)
- #
- level_depth = 0
- block_searching_flag = nil
- block_searching_lines = []
- pre_comment = []
- procedure_trailing = ""
- procedure_name = ""
- procedure_params = ""
- procedure_prefix = ""
- procedure_result_arg = ""
- procedure_type = ""
- contains_lines = []
- contains_flag = nil
- remaining_lines.collect!{|line|
- if !block_searching_flag
- # subroutine
- if line =~ /^\s*?
- (recursive|pure|elemental)?\s*?
- subroutine\s+(\w+)\s*?(\(.*?\))?\s*?(!.*?)?$
- /ix
- block_searching_flag = :subroutine
- block_searching_lines << line
-
- procedure_name = $2.chomp.strip
- procedure_params = $3 || ""
- procedure_prefix = $1 || ""
- procedure_trailing = $4 || "!"
- next false
-
- # function
- elsif line =~ /^\s*?
- (recursive|pure|elemental)?\s*?
- (
- character\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | type\s*?\([\w\s]+?\)\s+
- | integer\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | real\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | double\s+precision\s+
- | logical\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | complex\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- )?
- function\s+(\w+)\s*?
- (\(.*?\))?(\s+result\((.*?)\))?\s*?(!.*?)?$
- /ix
- block_searching_flag = :function
- block_searching_lines << line
-
- procedure_prefix = $1 || ""
- procedure_type = $2 ? $2.chomp.strip : nil
- procedure_name = $8.chomp.strip
- procedure_params = $9 || ""
- procedure_result_arg = $11 ? $11.chomp.strip : procedure_name
- procedure_trailing = $12 || "!"
- next false
- elsif line =~ /^\s*?!\s?(.*)/
- pre_comment << line
- next line
- else
- pre_comment = []
- next line
- end
- end
- contains_flag = true if line =~ /^\s*?contains\s*?(!.*?)?$/
- block_searching_lines << line
- contains_lines << line if contains_flag
-
- level_depth += 1 if block_start?(line)
- level_depth -= 1 if block_end?(line)
- if level_depth >= 0
- next false
- end
-
- # "procedure_code" is formatted.
- # ":nodoc:" flag is checked.
- #
- procedure_code = block_searching_lines.join("\n")
- procedure_code = remove_empty_head_lines(procedure_code)
- if procedure_trailing =~ /^!:nodoc:/
- # next loop to search next block
- level_depth = 0
- block_searching_flag = nil
- block_searching_lines = []
- pre_comment = []
- procedure_trailing = ""
- procedure_name = ""
- procedure_params = ""
- procedure_prefix = ""
- procedure_result_arg = ""
- procedure_type = ""
- contains_lines = []
- contains_flag = nil
- next false
- end
-
- # AnyMethod is created, and added to container
- #
- subroutine_function = nil
- if block_searching_flag == :subroutine
- subroutine_prefix = procedure_prefix
- subroutine_name = procedure_name
- subroutine_params = procedure_params
- subroutine_trailing = procedure_trailing
- subroutine_code = procedure_code
-
- subroutine_comment = COMMENTS_ARE_UPPER ?
- pre_comment.join("\n") + "\n" + subroutine_trailing :
- subroutine_trailing + "\n" + subroutine_code.sub(/^.*$\n/i, '')
- subroutine = AnyMethod.new("subroutine", subroutine_name)
- parse_subprogram(subroutine, subroutine_params,
- subroutine_comment, subroutine_code,
- before_contains_code, nil, subroutine_prefix)
- progress "s"
- @stats.num_methods += 1
- container.add_method subroutine
- subroutine_function = subroutine
-
- elsif block_searching_flag == :function
- function_prefix = procedure_prefix
- function_type = procedure_type
- function_name = procedure_name
- function_params_org = procedure_params
- function_result_arg = procedure_result_arg
- function_trailing = procedure_trailing
- function_code_org = procedure_code
-
- function_comment = COMMENTS_ARE_UPPER ?
- pre_comment.join("\n") + "\n" + function_trailing :
- function_trailing + "\n " + function_code_org.sub(/^.*$\n/i, '')
-
- function_code = "#{function_code_org}"
- if function_type
- function_code << "\n" + function_type + " :: " + function_result_arg
- end
-
- function_params =
- function_params_org.sub(/^\(/, "\(#{function_result_arg}, ")
-
- function = AnyMethod.new("function", function_name)
- parse_subprogram(function, function_params,
- function_comment, function_code,
- before_contains_code, true, function_prefix)
-
- # Specific modification due to function
- function.params.sub!(/\(\s*?#{function_result_arg}\s*?,\s*?/, "\( ")
- function.params << " result(" + function_result_arg + ")"
- function.start_collecting_tokens
- function.add_token Token.new(1,1).set_text(function_code_org)
-
- progress "f"
- @stats.num_methods += 1
- container.add_method function
- subroutine_function = function
-
- end
-
- # The visibility of procedure is specified
- #
- set_visibility(container, procedure_name,
- visibility_default, @@public_methods)
-
- # The alias for this procedure from external modules
- #
- check_external_aliases(procedure_name,
- subroutine_function.params,
- subroutine_function.comment, subroutine_function) if external
- check_public_methods(subroutine_function, container.name)
-
-
- # contains_lines are parsed as private procedures
- if contains_flag
- parse_program_or_module(container,
- contains_lines.join("\n"), :private)
- end
-
- # next loop to search next block
- level_depth = 0
- block_searching_flag = nil
- block_searching_lines = []
- pre_comment = []
- procedure_trailing = ""
- procedure_name = ""
- procedure_params = ""
- procedure_prefix = ""
- procedure_result_arg = ""
- contains_lines = []
- contains_flag = nil
- next false
- } # End of remaining_lines.collect!{|line|
-
- # Array remains_lines is converted to String remains_code again
- #
- remaining_code = remaining_lines.join("\n")
-
- #
- # Parse interface
- #
- interface_scope = false
- generic_name = ""
- interface_code.split("\n").each{ |line|
- if /^\s*?
- interface(
- \s+\w+|
- \s+operator\s*?\(.*?\)|
- \s+assignment\s*?\(\s*?=\s*?\)
- )?
- \s*?(!.*?)?$
- /ix =~ line
- generic_name = $1 ? $1.strip.chomp : nil
- interface_trailing = $2 || "!"
- interface_scope = true
- interface_scope = false if interface_trailing =~ /!:nodoc:/
-# if generic_name =~ /operator\s*?\((.*?)\)/i
-# operator_name = $1
-# if operator_name && !operator_name.empty?
-# generic_name = "#{operator_name}"
-# end
-# end
-# if generic_name =~ /assignment\s*?\((.*?)\)/i
-# assignment_name = $1
-# if assignment_name && !assignment_name.empty?
-# generic_name = "#{assignment_name}"
-# end
-# end
- end
- if /^\s*?end\s+interface/i =~ line
- interface_scope = false
- generic_name = nil
- end
- # internal alias
- if interface_scope && /^\s*?module\s+procedure\s+(.*?)(!.*?)?$/i =~ line
- procedures = $1.strip.chomp
- procedures_trailing = $2 || "!"
- next if procedures_trailing =~ /!:nodoc:/
- procedures.split(",").each{ |proc|
- proc.strip!
- proc.chomp!
- next if generic_name == proc || !generic_name
- old_meth = container.find_symbol(proc, nil, @options.ignore_case)
- next if !old_meth
- nolink = old_meth.visibility == :private ? true : nil
- nolink = nil if @options.show_all
- new_meth =
- initialize_external_method(generic_name, proc,
- old_meth.params, nil,
- old_meth.comment,
- old_meth.clone.token_stream[0].text,
- true, nolink)
- new_meth.singleton = old_meth.singleton
-
- progress "i"
- @stats.num_methods += 1
- container.add_method new_meth
-
- set_visibility(container, generic_name, visibility_default, @@public_methods)
-
- check_public_methods(new_meth, container.name)
-
- }
- end
-
- # external aliases
- if interface_scope
- # subroutine
- proc = nil
- params = nil
- procedures_trailing = nil
- if line =~ /^\s*?
- (recursive|pure|elemental)?\s*?
- subroutine\s+(\w+)\s*?(\(.*?\))?\s*?(!.*?)?$
- /ix
- proc = $2.chomp.strip
- generic_name = proc unless generic_name
- params = $3 || ""
- procedures_trailing = $4 || "!"
-
- # function
- elsif line =~ /^\s*?
- (recursive|pure|elemental)?\s*?
- (
- character\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | type\s*?\([\w\s]+?\)\s+
- | integer\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | real\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | double\s+precision\s+
- | logical\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | complex\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- )?
- function\s+(\w+)\s*?
- (\(.*?\))?(\s+result\((.*?)\))?\s*?(!.*?)?$
- /ix
- proc = $8.chomp.strip
- generic_name = proc unless generic_name
- params = $9 || ""
- procedures_trailing = $12 || "!"
- else
- next
- end
- next if procedures_trailing =~ /!:nodoc:/
- indicated_method = nil
- indicated_file = nil
- TopLevel.all_files.each do |name, toplevel|
- indicated_method = toplevel.find_local_symbol(proc, @options.ignore_case)
- indicated_file = name
- break if indicated_method
- end
-
- if indicated_method
- external_method =
- initialize_external_method(generic_name, proc,
- indicated_method.params,
- indicated_file,
- indicated_method.comment)
-
- progress "e"
- @stats.num_methods += 1
- container.add_method external_method
- set_visibility(container, generic_name, visibility_default, @@public_methods)
- if !container.include_requires?(indicated_file, @options.ignore_case)
- container.add_require(Require.new(indicated_file, ""))
- end
- check_public_methods(external_method, container.name)
-
- else
- @@external_aliases << {
- "new_name" => generic_name,
- "old_name" => proc,
- "file_or_module" => container,
- "visibility" => find_visibility(container, generic_name, @@public_methods) || visibility_default
- }
- end
- end
-
- } if interface_code # End of interface_code.split("\n").each ...
-
- #
- # Already imported methods are removed from @@public_methods.
- # Remainders are assumed to be imported from other modules.
- #
- @@public_methods.delete_if{ |method| method["entity_is_discovered"]}
-
- @@public_methods.each{ |pub_meth|
- next unless pub_meth["file_or_module"].name == container.name
- pub_meth["used_modules"].each{ |used_mod|
- TopLevel.all_classes_and_modules.each{ |modules|
- if modules.name == used_mod ||
- modules.name.upcase == used_mod.upcase &&
- @options.ignore_case
- modules.method_list.each{ |meth|
- if meth.name == pub_meth["name"] ||
- meth.name.upcase == pub_meth["name"].upcase &&
- @options.ignore_case
- new_meth = initialize_public_method(meth,
- modules.name)
- if pub_meth["local_name"]
- new_meth.name = pub_meth["local_name"]
- end
- progress "e"
- @stats.num_methods += 1
- container.add_method new_meth
- end
- }
- end
- }
- }
- }
-
- container
- end # End of parse_program_or_module
-
- #
- # Parse arguments, comment, code of subroutine and function.
- # Return AnyMethod object.
- #
- def parse_subprogram(subprogram, params, comment, code,
- before_contains=nil, function=nil, prefix=nil)
- subprogram.singleton = false
- prefix = "" if !prefix
- arguments = params.sub(/\(/, "").sub(/\)/, "").split(",") if params
- args_comment, params_opt =
- find_arguments(arguments, code.sub(/^s*?contains\s*?(!.*?)?$.*/im, ""),
- nil, nil, true)
- params_opt = "( " + params_opt + " ) " if params_opt
- subprogram.params = params_opt || ""
- namelist_comment = find_namelists(code, before_contains)
-
- block_comment = find_comments comment
- if function
- subprogram.comment = "<b><em> Function </em></b> :: <em>#{prefix}</em>\n"
- else
- subprogram.comment = "<b><em> Subroutine </em></b> :: <em>#{prefix}</em>\n"
- end
- subprogram.comment << args_comment if args_comment
- subprogram.comment << block_comment if block_comment
- subprogram.comment << namelist_comment if namelist_comment
-
- # For output source code
- subprogram.start_collecting_tokens
- subprogram.add_token Token.new(1,1).set_text(code)
-
- subprogram
- end
-
- #
- # Collect comment for file entity
- #
- def collect_first_comment(body)
- comment = ""
- not_comment = ""
- comment_start = false
- comment_end = false
- body.split("\n").each{ |line|
- if comment_end
- not_comment << line
- not_comment << "\n"
- elsif /^\s*?!\s?(.*)$/i =~ line
- comment_start = true
- comment << $1
- comment << "\n"
- elsif /^\s*?$/i =~ line
- comment_end = true if comment_start && COMMENTS_ARE_UPPER
- else
- comment_end = true
- not_comment << line
- not_comment << "\n"
- end
- }
- return comment, not_comment
- end
-
-
- # Return comments of definitions of arguments
- #
- # If "all" argument is true, information of all arguments are returned.
- # If "modified_params" is true, list of arguments are decorated,
- # for example, optional arguments are parenthetic as "[arg]".
- #
- def find_arguments(args, text, all=nil, indent=nil, modified_params=nil)
- return unless args || all
- indent = "" unless indent
- args = ["all"] if all
- params = "" if modified_params
- comma = ""
- return unless text
- args_rdocforms = "\n"
- remaining_lines = "#{text}"
- definitions = definition_info(remaining_lines)
- args.each{ |arg|
- arg.strip!
- arg.chomp!
- definitions.each { |defitem|
- if arg == defitem.varname.strip.chomp || all
- args_rdocforms << <<-"EOF"
-
-#{indent}<tt><b>#{defitem.varname.chomp.strip}#{defitem.arraysuffix}</b> #{defitem.inivalue}</tt> ::
-#{indent} <tt>#{defitem.types.chomp.strip}</tt>
-EOF
- if !defitem.comment.chomp.strip.empty?
- comment = ""
- defitem.comment.split("\n").each{ |line|
- comment << " " + line + "\n"
- }
- args_rdocforms << <<-"EOF"
-
-#{indent} <tt></tt> ::
-#{indent} <tt></tt>
-#{indent} #{comment.chomp.strip}
-EOF
- end
-
- if modified_params
- if defitem.include_attr?("optional")
- params << "#{comma}[#{arg}]"
- else
- params << "#{comma}#{arg}"
- end
- comma = ", "
- end
- end
- }
- }
- if modified_params
- return args_rdocforms, params
- else
- return args_rdocforms
- end
- end
-
- # Return comments of definitions of namelists
- #
- def find_namelists(text, before_contains=nil)
- return nil if !text
- result = ""
- lines = "#{text}"
- before_contains = "" if !before_contains
- while lines =~ /^\s*?namelist\s+\/\s*?(\w+)\s*?\/([\s\w\,]+)$/i
- lines = $~.post_match
- nml_comment = COMMENTS_ARE_UPPER ?
- find_comments($~.pre_match) : find_comments($~.post_match)
- nml_name = $1
- nml_args = $2.split(",")
- result << "\n\n=== NAMELIST <tt><b>" + nml_name + "</tt></b>\n\n"
- result << nml_comment + "\n" if nml_comment
- if lines.split("\n")[0] =~ /^\//i
- lines = "namelist " + lines
- end
- result << find_arguments(nml_args, "#{text}" + "\n" + before_contains)
- end
- return result
- end
-
- #
- # Comments just after module or subprogram, or arguments are
- # returned. If "COMMENTS_ARE_UPPER" is true, comments just before
- # modules or subprograms are returned
- #
- def find_comments text
- return "" unless text
- lines = text.split("\n")
- lines.reverse! if COMMENTS_ARE_UPPER
- comment_block = Array.new
- lines.each do |line|
- break if line =~ /^\s*?\w/ || line =~ /^\s*?$/
- if COMMENTS_ARE_UPPER
- comment_block.unshift line.sub(/^\s*?!\s?/,"")
- else
- comment_block.push line.sub(/^\s*?!\s?/,"")
- end
- end
- nice_lines = comment_block.join("\n").split "\n\s*?\n"
- nice_lines[0] ||= ""
- nice_lines.shift
- end
-
- def progress(char)
- unless @options.quiet
- @progress.print(char)
- @progress.flush
- end
- end
-
- #
- # Create method for internal alias
- #
- def initialize_public_method(method, parent)
- return if !method || !parent
-
- new_meth = AnyMethod.new("External Alias for module", method.name)
- new_meth.singleton = method.singleton
- new_meth.params = method.params.clone
- new_meth.comment = remove_trailing_alias(method.comment.clone)
- new_meth.comment << "\n\n#{EXTERNAL_ALIAS_MES} #{parent.strip.chomp}\##{method.name}"
-
- return new_meth
- end
-
- #
- # Create method for external alias
- #
- # If argument "internal" is true, file is ignored.
- #
- def initialize_external_method(new, old, params, file, comment, token=nil,
- internal=nil, nolink=nil)
- return nil unless new || old
-
- if internal
- external_alias_header = "#{INTERNAL_ALIAS_MES} "
- external_alias_text = external_alias_header + old
- elsif file
- external_alias_header = "#{EXTERNAL_ALIAS_MES} "
- external_alias_text = external_alias_header + file + "#" + old
- else
- return nil
- end
- external_meth = AnyMethod.new(external_alias_text, new)
- external_meth.singleton = false
- external_meth.params = params
- external_comment = remove_trailing_alias(comment) + "\n\n" if comment
- external_meth.comment = external_comment || ""
- if nolink && token
- external_meth.start_collecting_tokens
- external_meth.add_token Token.new(1,1).set_text(token)
- else
- external_meth.comment << external_alias_text
- end
-
- return external_meth
- end
-
-
-
- #
- # Parse visibility
- #
- def parse_visibility(code, default, container)
- result = []
- visibility_default = default || :public
-
- used_modules = []
- container.includes.each{|i| used_modules << i.name} if container
-
- remaining_code = code.gsub(/^\s*?type[\s\,]+.*?\s+end\s+type.*?$/im, "")
- remaining_code.split("\n").each{ |line|
- if /^\s*?private\s*?$/ =~ line
- visibility_default = :private
- break
- end
- } if remaining_code
-
- remaining_code.split("\n").each{ |line|
- if /^\s*?private\s*?(::)?\s+(.*)\s*?(!.*?)?/i =~ line
- methods = $2.sub(/!.*$/, '')
- methods.split(",").each{ |meth|
- meth.sub!(/!.*$/, '')
- meth.gsub!(/:/, '')
- result << {
- "name" => meth.chomp.strip,
- "visibility" => :private,
- "used_modules" => used_modules.clone,
- "file_or_module" => container,
- "entity_is_discovered" => nil,
- "local_name" => nil
- }
- }
- elsif /^\s*?public\s*?(::)?\s+(.*)\s*?(!.*?)?/i =~ line
- methods = $2.sub(/!.*$/, '')
- methods.split(",").each{ |meth|
- meth.sub!(/!.*$/, '')
- meth.gsub!(/:/, '')
- result << {
- "name" => meth.chomp.strip,
- "visibility" => :public,
- "used_modules" => used_modules.clone,
- "file_or_module" => container,
- "entity_is_discovered" => nil,
- "local_name" => nil
- }
- }
- end
- } if remaining_code
-
- if container
- result.each{ |vis_info|
- vis_info["parent"] = container.name
- }
- end
-
- return visibility_default, result
- end
-
- #
- # Set visibility
- #
- # "subname" element of "visibility_info" is deleted.
- #
- def set_visibility(container, subname, visibility_default, visibility_info)
- return unless container || subname || visibility_default || visibility_info
- not_found = true
- visibility_info.collect!{ |info|
- if info["name"] == subname ||
- @options.ignore_case && info["name"].upcase == subname.upcase
- if info["file_or_module"].name == container.name
- container.set_visibility_for([subname], info["visibility"])
- info["entity_is_discovered"] = true
- not_found = false
- end
- end
- info
- }
- if not_found
- return container.set_visibility_for([subname], visibility_default)
- else
- return container
- end
- end
-
- #
- # Find visibility
- #
- def find_visibility(container, subname, visibility_info)
- return nil if !subname || !visibility_info
- visibility_info.each{ |info|
- if info["name"] == subname ||
- @options.ignore_case && info["name"].upcase == subname.upcase
- if info["parent"] == container.name
- return info["visibility"]
- end
- end
- }
- return nil
- end
-
- #
- # Check external aliases
- #
- def check_external_aliases(subname, params, comment, test=nil)
- @@external_aliases.each{ |alias_item|
- if subname == alias_item["old_name"] ||
- subname.upcase == alias_item["old_name"].upcase &&
- @options.ignore_case
-
- new_meth = initialize_external_method(alias_item["new_name"],
- subname, params, @file_name,
- comment)
- new_meth.visibility = alias_item["visibility"]
-
- progress "e"
- @stats.num_methods += 1
- alias_item["file_or_module"].add_method(new_meth)
-
- if !alias_item["file_or_module"].include_requires?(@file_name, @options.ignore_case)
- alias_item["file_or_module"].add_require(Require.new(@file_name, ""))
- end
- end
- }
- end
-
- #
- # Check public_methods
- #
- def check_public_methods(method, parent)
- return if !method || !parent
- @@public_methods.each{ |alias_item|
- parent_is_used_module = nil
- alias_item["used_modules"].each{ |used_module|
- if used_module == parent ||
- used_module.upcase == parent.upcase &&
- @options.ignore_case
- parent_is_used_module = true
- end
- }
- next if !parent_is_used_module
-
- if method.name == alias_item["name"] ||
- method.name.upcase == alias_item["name"].upcase &&
- @options.ignore_case
-
- new_meth = initialize_public_method(method, parent)
- if alias_item["local_name"]
- new_meth.name = alias_item["local_name"]
- end
-
- progress "e"
- @stats.num_methods += 1
- alias_item["file_or_module"].add_method new_meth
- end
- }
- end
-
- #
- # Continuous lines are united.
- #
- # Comments in continuous lines are removed.
- #
- def united_to_one_line(f90src)
- return "" unless f90src
- lines = f90src.split("\n")
- previous_continuing = false
- now_continuing = false
- body = ""
- lines.each{ |line|
- words = line.split("")
- next if words.empty? && previous_continuing
- commentout = false
- brank_flag = true ; brank_char = ""
- squote = false ; dquote = false
- ignore = false
- words.collect! { |char|
- if previous_continuing && brank_flag
- now_continuing = true
- ignore = true
- case char
- when "!" ; break
- when " " ; brank_char << char ; next ""
- when "&"
- brank_flag = false
- now_continuing = false
- next ""
- else
- brank_flag = false
- now_continuing = false
- ignore = false
- next brank_char + char
- end
- end
- ignore = false
-
- if now_continuing
- next ""
- elsif !(squote) && !(dquote) && !(commentout)
- case char
- when "!" ; commentout = true ; next char
- when "\""; dquote = true ; next char
- when "\'"; squote = true ; next char
- when "&" ; now_continuing = true ; next ""
- else next char
- end
- elsif commentout
- next char
- elsif squote
- case char
- when "\'"; squote = false ; next char
- else next char
- end
- elsif dquote
- case char
- when "\""; dquote = false ; next char
- else next char
- end
- end
- }
- if !ignore && !previous_continuing || !brank_flag
- if previous_continuing
- body << words.join("")
- else
- body << "\n" + words.join("")
- end
- end
- previous_continuing = now_continuing ? true : nil
- now_continuing = nil
- }
- return body
- end
-
-
- #
- # Continuous line checker
- #
- def continuous_line?(line)
- continuous = false
- if /&\s*?(!.*)?$/ =~ line
- continuous = true
- if comment_out?($~.pre_match)
- continuous = false
- end
- end
- return continuous
- end
-
- #
- # Comment out checker
- #
- def comment_out?(line)
- return nil unless line
- commentout = false
- squote = false ; dquote = false
- line.split("").each { |char|
- if !(squote) && !(dquote)
- case char
- when "!" ; commentout = true ; break
- when "\""; dquote = true
- when "\'"; squote = true
- else next
- end
- elsif squote
- case char
- when "\'"; squote = false
- else next
- end
- elsif dquote
- case char
- when "\""; dquote = false
- else next
- end
- end
- }
- return commentout
- end
-
- #
- # Semicolons are replaced to line feed.
- #
- def semicolon_to_linefeed(text)
- return "" unless text
- lines = text.split("\n")
- lines.collect!{ |line|
- words = line.split("")
- commentout = false
- squote = false ; dquote = false
- words.collect! { |char|
- if !(squote) && !(dquote) && !(commentout)
- case char
- when "!" ; commentout = true ; next char
- when "\""; dquote = true ; next char
- when "\'"; squote = true ; next char
- when ";" ; "\n"
- else next char
- end
- elsif commentout
- next char
- elsif squote
- case char
- when "\'"; squote = false ; next char
- else next char
- end
- elsif dquote
- case char
- when "\""; dquote = false ; next char
- else next char
- end
- end
- }
- words.join("")
- }
- return lines.join("\n")
- end
-
- #
- # Which "line" is start of block (module, program, block data,
- # subroutine, function) statement ?
- #
- def block_start?(line)
- return nil if !line
-
- if line =~ /^\s*?module\s+(\w+)\s*?(!.*?)?$/i ||
- line =~ /^\s*?program\s+(\w+)\s*?(!.*?)?$/i ||
- line =~ /^\s*?block\s+data(\s+\w+)?\s*?(!.*?)?$/i ||
- line =~ \
- /^\s*?
- (recursive|pure|elemental)?\s*?
- subroutine\s+(\w+)\s*?(\(.*?\))?\s*?(!.*?)?$
- /ix ||
- line =~ \
- /^\s*?
- (recursive|pure|elemental)?\s*?
- (
- character\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | type\s*?\([\w\s]+?\)\s+
- | integer\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | real\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | double\s+precision\s+
- | logical\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | complex\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- )?
- function\s+(\w+)\s*?
- (\(.*?\))?(\s+result\((.*?)\))?\s*?(!.*?)?$
- /ix
- return true
- end
-
- return nil
- end
-
- #
- # Which "line" is end of block (module, program, block data,
- # subroutine, function) statement ?
- #
- def block_end?(line)
- return nil if !line
-
- if line =~ /^\s*?end\s*?(!.*?)?$/i ||
- line =~ /^\s*?end\s+module(\s+\w+)?\s*?(!.*?)?$/i ||
- line =~ /^\s*?end\s+program(\s+\w+)?\s*?(!.*?)?$/i ||
- line =~ /^\s*?end\s+block\s+data(\s+\w+)?\s*?(!.*?)?$/i ||
- line =~ /^\s*?end\s+subroutine(\s+\w+)?\s*?(!.*?)?$/i ||
- line =~ /^\s*?end\s+function(\s+\w+)?\s*?(!.*?)?$/i
- return true
- end
-
- return nil
- end
-
- #
- # Remove "Alias for" in end of comments
- #
- def remove_trailing_alias(text)
- return "" if !text
- lines = text.split("\n").reverse
- comment_block = Array.new
- checked = false
- lines.each do |line|
- if !checked
- if /^\s?#{INTERNAL_ALIAS_MES}/ =~ line ||
- /^\s?#{EXTERNAL_ALIAS_MES}/ =~ line
- checked = true
- next
- end
- end
- comment_block.unshift line
- end
- nice_lines = comment_block.join("\n")
- nice_lines ||= ""
- return nice_lines
- end
-
- # Empty lines in header are removed
- def remove_empty_head_lines(text)
- return "" unless text
- lines = text.split("\n")
- header = true
- lines.delete_if{ |line|
- header = false if /\S/ =~ line
- header && /^\s*?$/ =~ line
- }
- lines.join("\n")
- end
-
-
- # header marker "=", "==", ... are removed
- def remove_header_marker(text)
- return text.gsub(/^\s?(=+)/, '<tt></tt>\1')
- end
-
- def remove_private_comments(body)
- body.gsub!(/^\s*!--\s*?$.*?^\s*!\+\+\s*?$/m, '')
- return body
- end
-
-
- #
- # Information of arguments of subroutines and functions in Fortran95
- #
- class Fortran95Definition
-
- # Name of variable
- #
- attr_reader :varname
-
- # Types of variable
- #
- attr_reader :types
-
- # Initial Value
- #
- attr_reader :inivalue
-
- # Suffix of array
- #
- attr_reader :arraysuffix
-
- # Comments
- #
- attr_accessor :comment
-
- # Flag of non documentation
- #
- attr_accessor :nodoc
-
- def initialize(varname, types, inivalue, arraysuffix, comment,
- nodoc=false)
- @varname = varname
- @types = types
- @inivalue = inivalue
- @arraysuffix = arraysuffix
- @comment = comment
- @nodoc = nodoc
- end
-
- def to_s
- return <<-EOF
-<Fortran95Definition:
- varname=#{@varname}, types=#{types},
- inivalue=#{@inivalue}, arraysuffix=#{@arraysuffix}, nodoc=#{@nodoc},
- comment=
-#{@comment}
->
-EOF
- end
-
- #
- # If attr is included, true is returned
- #
- def include_attr?(attr)
- return if !attr
- @types.split(",").each{ |type|
- return true if type.strip.chomp.upcase == attr.strip.chomp.upcase
- }
- return nil
- end
-
- end # End of Fortran95Definition
-
- #
- # Parse string argument "text", and Return Array of
- # Fortran95Definition object
- #
- def definition_info(text)
- return nil unless text
- lines = "#{text}"
- defs = Array.new
- comment = ""
- trailing_comment = ""
- under_comment_valid = false
- lines.split("\n").each{ |line|
- if /^\s*?!\s?(.*)/ =~ line
- if COMMENTS_ARE_UPPER
- comment << remove_header_marker($1)
- comment << "\n"
- elsif defs[-1] && under_comment_valid
- defs[-1].comment << "\n"
- defs[-1].comment << remove_header_marker($1)
- end
- next
- elsif /^\s*?$/ =~ line
- comment = ""
- under_comment_valid = false
- next
- end
- type = ""
- characters = ""
- if line =~ /^\s*?
- (
- character\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
- | type\s*?\([\w\s]+?\)[\s\,]*
- | integer\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
- | real\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
- | double\s+precision[\s\,]*
- | logical\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
- | complex\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
- )
- (.*?::)?
- (.+)$
- /ix
- characters = $8
- type = $1
- type << $7.gsub(/::/, '').gsub(/^\s*?\,/, '') if $7
- else
- under_comment_valid = false
- next
- end
- squote = false ; dquote = false ; bracket = 0
- iniflag = false; commentflag = false
- varname = "" ; arraysuffix = "" ; inivalue = ""
- start_pos = defs.size
- characters.split("").each { |char|
- if !(squote) && !(dquote) && bracket <= 0 && !(iniflag) && !(commentflag)
- case char
- when "!" ; commentflag = true
- when "(" ; bracket += 1 ; arraysuffix = char
- when "\""; dquote = true
- when "\'"; squote = true
- when "=" ; iniflag = true ; inivalue << char
- when ","
- defs << Fortran95Definition.new(varname, type, inivalue, arraysuffix, comment)
- varname = "" ; arraysuffix = "" ; inivalue = ""
- under_comment_valid = true
- when " " ; next
- else ; varname << char
- end
- elsif commentflag
- comment << remove_header_marker(char)
- trailing_comment << remove_header_marker(char)
- elsif iniflag
- if dquote
- case char
- when "\"" ; dquote = false ; inivalue << char
- else ; inivalue << char
- end
- elsif squote
- case char
- when "\'" ; squote = false ; inivalue << char
- else ; inivalue << char
- end
- elsif bracket > 0
- case char
- when "(" ; bracket += 1 ; inivalue << char
- when ")" ; bracket -= 1 ; inivalue << char
- else ; inivalue << char
- end
- else
- case char
- when ","
- defs << Fortran95Definition.new(varname, type, inivalue, arraysuffix, comment)
- varname = "" ; arraysuffix = "" ; inivalue = ""
- iniflag = false
- under_comment_valid = true
- when "(" ; bracket += 1 ; inivalue << char
- when "\""; dquote = true ; inivalue << char
- when "\'"; squote = true ; inivalue << char
- when "!" ; commentflag = true
- else ; inivalue << char
- end
- end
- elsif !(squote) && !(dquote) && bracket > 0
- case char
- when "(" ; bracket += 1 ; arraysuffix << char
- when ")" ; bracket -= 1 ; arraysuffix << char
- else ; arraysuffix << char
- end
- elsif squote
- case char
- when "\'"; squote = false ; inivalue << char
- else ; inivalue << char
- end
- elsif dquote
- case char
- when "\""; dquote = false ; inivalue << char
- else ; inivalue << char
- end
- end
- }
- defs << Fortran95Definition.new(varname, type, inivalue, arraysuffix, comment)
- if trailing_comment =~ /^:nodoc:/
- defs[start_pos..-1].collect!{ |defitem|
- defitem.nodoc = true
- }
- end
- varname = "" ; arraysuffix = "" ; inivalue = ""
- comment = ""
- under_comment_valid = true
- trailing_comment = ""
- }
- return defs
- end
-
-
- end # class Fortran95parser
-
-end # module RDoc
diff --git a/lib/rdoc/parsers/parse_simple.rb b/lib/rdoc/parsers/parse_simple.rb
deleted file mode 100644
index f4b501fe93..0000000000
--- a/lib/rdoc/parsers/parse_simple.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-require 'rdoc'
-require 'rdoc/code_objects'
-require 'rdoc/markup/preprocess'
-
-##
-# Parse a non-source file. We basically take the whole thing as one big
-# comment. If the first character in the file is '#', we strip leading pound
-# signs.
-
-class RDoc::SimpleParser
-
- ##
- # Prepare to parse a plain file
-
- def initialize(top_level, file_name, body, options, stats)
- preprocess = RDoc::Markup::PreProcess.new(file_name, options.rdoc_include)
-
- preprocess.handle(body) do |directive, param|
- warn "Unrecognized directive '#{directive}' in #{file_name}"
- end
-
- @body = body
- @options = options
- @top_level = top_level
- end
-
- ##
- # Extract the file contents and attach them to the toplevel as a comment
-
- def scan
- @top_level.comment = remove_private_comments(@body)
- @top_level
- end
-
- def remove_private_comments(comment)
- comment.gsub(/^--[^-].*?^\+\+/m, '').sub(/^--.*/m, '')
- end
-
-end
-
diff --git a/lib/rdoc/parsers/parserfactory.rb b/lib/rdoc/parsers/parserfactory.rb
deleted file mode 100644
index 00a82cf4b1..0000000000
--- a/lib/rdoc/parsers/parserfactory.rb
+++ /dev/null
@@ -1,99 +0,0 @@
-require "rdoc/parsers/parse_simple"
-
-module RDoc
-
- # A parser is simple a class that implements
- #
- # #initialize(file_name, body, options)
- #
- # and
- #
- # #scan
- #
- # The initialize method takes a file name to be used, the body of the
- # file, and an RDoc::Options object. The scan method is then called
- # to return an appropriately parsed TopLevel code object.
- #
- # The ParseFactory is used to redirect to the correct parser given a filename
- # extension. This magic works because individual parsers have to register
- # themselves with us as they are loaded in. The do this using the following
- # incantation
- #
- #
- # require "rdoc/parsers/parsefactory"
- #
- # module RDoc
- #
- # class XyzParser
- # extend ParseFactory <<<<
- # parse_files_matching /\.xyz$/ <<<<
- #
- # def initialize(file_name, body, options)
- # ...
- # end
- #
- # def scan
- # ...
- # end
- # end
- # end
- #
- # Just to make life interesting, if we suspect a plain text file, we
- # also look for a shebang line just in case it's a potential
- # shell script
-
-
-
- module ParserFactory
-
- @@parsers = []
-
- Parsers = Struct.new(:regexp, :parser)
-
- # Record the fact that a particular class parses files that
- # match a given extension
-
- def parse_files_matching(regexp)
- @@parsers.unshift Parsers.new(regexp, self)
- end
-
- # Return a parser that can handle a particular extension
-
- def ParserFactory.can_parse(file_name)
- @@parsers.find {|p| p.regexp.match(file_name) }
- end
-
- # Alias an extension to another extension. After this call,
- # files ending "new_ext" will be parsed using the same parser
- # as "old_ext"
-
- def ParserFactory.alias_extension(old_ext, new_ext)
- parser = ParserFactory.can_parse("xxx.#{old_ext}")
- return false unless parser
- @@parsers.unshift Parsers.new(Regexp.new("\\.#{new_ext}$"), parser.parser)
- true
- end
-
- # Find the correct parser for a particular file name. Return a
- # SimpleParser for ones that we don't know
-
- def ParserFactory.parser_for(top_level, file_name, body, options, stats)
- # If no extension, look for shebang
- if file_name !~ /\.\w+$/ && body =~ %r{\A#!(.+)}
- shebang = $1
- case shebang
- when %r{env\s+ruby}, %r{/ruby}
- file_name = "dummy.rb"
- end
- end
- parser_description = can_parse(file_name)
- if parser_description
- parser = parser_description.parser
- else
- parser = SimpleParser
- end
-
- parser.new(top_level, file_name, body, options, stats)
- end
- end
-end
diff --git a/lib/rdoc/rdoc.rb b/lib/rdoc/rdoc.rb
index 81cae16bfd..601863ccc9 100644
--- a/lib/rdoc/rdoc.rb
+++ b/lib/rdoc/rdoc.rb
@@ -1,9 +1,12 @@
require 'rdoc'
-require 'rdoc/parsers/parse_rb.rb'
-require 'rdoc/parsers/parse_c.rb'
-require 'rdoc/parsers/parse_f95.rb'
-require 'rdoc/parsers/parse_simple.rb'
+require 'rdoc/parser'
+
+# Simple must come first
+require 'rdoc/parser/simple'
+require 'rdoc/parser/ruby'
+require 'rdoc/parser/c'
+require 'rdoc/parser/f95'
require 'rdoc/stats'
require 'rdoc/options'
@@ -146,7 +149,10 @@ module RDoc
case type = stat.ftype
when "file"
next if @last_created and stat.mtime < @last_created
- file_list << rel_file_name.sub(/^\.\//, '') if force_doc || ParserFactory.can_parse(rel_file_name)
+
+ if force_doc or ::RDoc::Parser.can_parse(rel_file_name) then
+ file_list << rel_file_name.sub(/^\.\//, '')
+ end
when "directory"
next if rel_file_name == "CVS" || rel_file_name == ".svn"
dot_doc = File.join(rel_file_name, DOT_DOC_FILENAME)
@@ -198,14 +204,18 @@ module RDoc
File.read fn
end
- if /coding:\s*(\S+)/ =~ content[/\A(?:.*\n){0,2}/]
- if enc = Encoding.find($1)
- content.force_encoding(enc)
+ if defined? Encoding then
+ if /coding:\s*(\S+)/ =~ content[/\A(?:.*\n){0,2}/]
+ if enc = ::Encoding.find($1)
+ content.force_encoding(enc)
+ end
end
end
- top_level = TopLevel.new(fn)
- parser = ParserFactory.parser_for(top_level, fn, content, options, @stats)
+ top_level = ::RDoc::TopLevel.new fn
+
+ parser = ::RDoc::Parser.for top_level, fn, content, options, @stats
+
file_info << parser.scan
@stats.num_files += 1
end
@@ -241,17 +251,19 @@ module RDoc
file_info = parse_files @options
+ @options.title = "RDoc Documentation"
+
if file_info.empty?
$stderr.puts "\nNo newer files." unless @options.quiet
else
- gen = @options.generator
+ @gen = @options.generator
- $stderr.puts "\nGenerating #{gen.key.upcase}..." unless @options.quiet
+ $stderr.puts "\nGenerating #{@gen.key.upcase}..." unless @options.quiet
- require gen.file_name
+ require @gen.file_name
- gen_class = ::RDoc::Generator.const_get gen.class_name
- gen = gen_class.for @options
+ gen_class = ::RDoc::Generator.const_get @gen.class_name
+ @gen = gen_class.for @options
pwd = Dir.pwd
@@ -259,7 +271,7 @@ module RDoc
begin
Diagram.new(file_info, @options).draw if @options.diagram
- gen.generate(file_info)
+ @gen.generate(file_info)
update_output_dir(".", start_time)
ensure
Dir.chdir(pwd)
diff --git a/lib/rdoc/ri.rb b/lib/rdoc/ri.rb
index 08cc4a145f..a3a858e673 100644
--- a/lib/rdoc/ri.rb
+++ b/lib/rdoc/ri.rb
@@ -1,4 +1,8 @@
require 'rdoc'
-module RDoc::RI; end
+module RDoc::RI
+
+ class Error < RDoc::Error; end
+
+end
diff --git a/lib/rdoc/ri/descriptions.rb b/lib/rdoc/ri/descriptions.rb
index 0aef189025..6a30f51c9b 100644
--- a/lib/rdoc/ri/descriptions.rb
+++ b/lib/rdoc/ri/descriptions.rb
@@ -2,11 +2,10 @@ require 'yaml'
require 'rdoc/markup/fragments'
require 'rdoc/ri'
-#--
+##
# Descriptions are created by RDoc (in ri_generator) and written out in
# serialized form into the documentation tree. ri then reads these to generate
# the documentation
-#++
class RDoc::RI::NamedThing
attr_reader :name
diff --git a/lib/rdoc/ri/driver.rb b/lib/rdoc/ri/driver.rb
index 7ac2e0f15c..ecf1bf9f27 100644
--- a/lib/rdoc/ri/driver.rb
+++ b/lib/rdoc/ri/driver.rb
@@ -11,6 +11,64 @@ require 'rdoc/markup/to_flow'
class RDoc::RI::Driver
+ class Hash < ::Hash
+ def self.convert(hash)
+ hash = new.update hash
+
+ hash.each do |key, value|
+ hash[key] = case value
+ when ::Hash then
+ convert value
+ when Array then
+ value = value.map do |v|
+ ::Hash === v ? convert(v) : v
+ end
+ value
+ else
+ value
+ end
+ end
+
+ hash
+ end
+
+ def method_missing method, *args
+ self[method.to_s]
+ end
+
+ def merge_enums(other)
+ other.each do |k, v|
+ if self[k] then
+ case v
+ when Array then
+ # HACK dunno
+ if String === self[k] and self[k].empty? then
+ self[k] = v
+ else
+ self[k] += v
+ end
+ when Hash then
+ self[k].update v
+ else
+ # do nothing
+ end
+ else
+ self[k] = v
+ end
+ end
+ end
+ end
+
+ class Error < RDoc::RI::Error; end
+
+ class NotFoundError < Error
+ def message
+ "Nothing known about #{super}"
+ end
+ end
+
+ attr_accessor :homepath # :nodoc:
+
def self.process_args(argv)
options = {}
options[:use_stdout] = !$stdout.tty?
@@ -234,7 +292,7 @@ Options may also be set in the 'RI' environment variable.
@class_cache = if up_to_date then
load_cache_for @class_cache_name
else
- class_cache = {}
+ class_cache = RDoc::RI::Driver::Hash.new
classes = map_dirs('**/cdesc*.yaml', :sys) { |f| Dir[f] }
populate_class_cache class_cache, classes
@@ -261,16 +319,24 @@ Options may also be set in the 'RI' environment variable.
def display_class(name)
klass = class_cache[name]
+ klass = RDoc::RI::Driver::Hash.convert klass
@display.display_class_info klass, class_cache
end
+ def get_info_for(arg)
+ @names = [arg]
+ run
+ end
+
def load_cache_for(klassname)
path = cache_file_for klassname
+ cache = nil
+
if File.exist? path and
File.mtime(path) >= File.mtime(class_cache_file_path) then
File.open path, 'rb' do |fp|
- Marshal.load fp.read
+ cache = Marshal.load fp.read
end
else
class_cache = nil
@@ -283,7 +349,7 @@ Options may also be set in the 'RI' environment variable.
return nil unless klass
method_files = klass["sources"]
- cache = {}
+ cache = RDoc::RI::Driver::Hash.new
sys_dir = @sys_dirs.first
method_files.each do |f|
@@ -296,12 +362,28 @@ Options may also be set in the 'RI' environment variable.
ext_path = f
ext_path = "gem #{$1}" if f =~ %r%gems/[\d.]+/doc/([^/]+)%
method["source_path"] = ext_path unless system_file
- cache[name] = method
+ cache[name] = RDoc::RI::Driver::Hash.convert method
end
end
write_cache cache, path
end
+
+ RDoc::RI::Driver::Hash.convert cache
+ end
+
+ ##
+ # Finds the method
+
+ def lookup_method(name, klass)
+ cache = load_cache_for klass
+ raise NotFoundError, name unless cache
+
+ method = cache[name.gsub('.', '#')]
+ method = cache[name.gsub('.', '::')] unless method
+ raise NotFoundError, name unless method
+
+ method
end
def map_dirs(file_name, system=false)
@@ -318,6 +400,22 @@ Options may also be set in the 'RI' environment variable.
dirs.map { |dir| yield File.join(dir, file_name) }.flatten.compact
end
+ ##
+ # Extract the class and method name parts from +name+ like Foo::Bar#baz
+
+ def parse_name(name)
+ parts = name.split(/(::|\#|\.)/)
+
+ if parts[-2] != '::' or parts.last !~ /^[A-Z]/ then
+ meth = parts.pop
+ parts.pop
+ end
+
+ klass = parts.join
+
+ [klass, meth]
+ end
+
def populate_class_cache(class_cache, classes, extension = false)
classes.each do |cdesc|
desc = read_yaml cdesc
@@ -351,11 +449,6 @@ Options may also be set in the 'RI' environment variable.
YAML.load data
end
- def get_info_for(arg)
- @names = [arg]
- run
- end
-
def run
if @names.empty? then
@display.list_known_classes class_cache.keys.sort
@@ -368,15 +461,10 @@ Options may also be set in the 'RI' environment variable.
else
meth = nil
- parts = name.split(/::|\#|\./)
- meth = parts.pop unless parts.last =~ /^[A-Z]/
- klass = parts.join '::'
+ klass, meth = parse_name name
+
+ method = lookup_method name, klass
- cache = load_cache_for klass
- # HACK Does not support F.n
- abort "Nothing known about #{name}" unless cache
- method = cache[name.gsub(/\./, '#')]
- abort "Nothing known about #{name}" unless method
@display.display_method_info method
end
else
@@ -385,7 +473,7 @@ Options may also be set in the 'RI' environment variable.
else
methods = select_methods(/^#{name}/)
if methods.size == 0
- abort "Nothing known about #{name}"
+ raise NotFoundError, name
elsif methods.size == 1
@display.display_method_info methods.first
else
@@ -395,6 +483,8 @@ Options may also be set in the 'RI' environment variable.
end
end
end
+ rescue NotFoundError => e
+ abort e.message
end
def select_methods(pattern)
@@ -422,31 +512,3 @@ Options may also be set in the 'RI' environment variable.
end
-class Hash # HACK don't add stuff to Hash.
- def method_missing method, *args
- self[method.to_s]
- end
-
- def merge_enums(other)
- other.each do |k,v|
- if self[k] then
- case v
- when Array then
- # HACK dunno
- if String === self[k] and self[k].empty? then
- self[k] = v
- else
- self[k] += v
- end
- when Hash then
- self[k].merge! v
- else
- # do nothing
- end
- else
- self[k] = v
- end
- end
- end
-end
-
diff --git a/test/rdoc/test_rdoc_info_formatting.rb b/test/rdoc/test_rdoc_info_formatting.rb
new file mode 100644
index 0000000000..bcc55ddf59
--- /dev/null
+++ b/test/rdoc/test_rdoc_info_formatting.rb
@@ -0,0 +1,179 @@
+$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib/'
+require 'fileutils'
+require 'test/unit'
+require 'rdoc/generator/texinfo'
+require 'yaml'
+
+# From chapter 18 of the Pickaxe 3rd ed. and the TexInfo manual.
+class TestRdocInfoFormatting < Test::Unit::TestCase
+ OUTPUT_DIR = "/tmp/rdoc-#{$$}"
+
+ def setup
+ # supress stdout
+ $stdout = File.new('/dev/null','w')
+ $stderr = File.new('/dev/null','w')
+
+ RDoc::RDoc.new.document(['--fmt=texinfo',
+ File.expand_path(__FILE__),
+ "--op=#{OUTPUT_DIR}"])
+ @text = File.read(OUTPUT_DIR + '/rdoc.texinfo')
+ # File.open('rdoc.texinfo', 'w') { |f| f.puts @text }
+ end
+
+ def teardown
+ $stdout = STDOUT
+ $stderr = STDERR
+ FileUtils.rm_rf OUTPUT_DIR
+ end
+
+ # Make sure tags like *this* do not make HTML
+ def test_descriptions_are_not_html
+ assert_no_match Regexp.new("\<b\>this\<\/b\>"), @text, "We had some HTML; icky!"
+ end
+
+ # Ensure we get a reasonable amount
+ #
+ # of space in between paragraphs.
+ def test_paragraphs_are_spaced
+ assert_match(/amount\n\n\nof space/, @text)
+ end
+
+ # @ and {} should be at-sign-prefixed
+ def test_escaping
+ assert_match(/@@ and @\{@\} should be at-sign-prefixed/)
+ end
+
+ # This tests that *bold* and <b>bold me</b> become @strong{bolded}
+ def test_bold
+ # Seems like a limitation of the Info format: @strong{bold}
+ # becomes *bold* when read in Info or M-x info. highly lame!
+ assert_match(/@strong\{bold\}/)
+ assert_match(/@strong\{bold me\}/)
+ end
+
+ # Test that _italics_ and <em>italicize me</em> becomes @emph{italicized}
+ def test_italics
+ assert_match(/@emph\{italics\}/)
+ assert_match(/@emph\{italicize me\}/)
+ end
+
+ # And that typewriter +text+ and <tt>typewriter me</tt> becomes @code{typewriter}
+ def test_tt
+ assert_match(/@code\{text\}/)
+ assert_match(/@code\{typewriter me\}/)
+ end
+
+ # Check that
+ # anything indented is
+ # verbatim @verb{|foo bar baz|}
+ def test_literal_code
+ assert_match("@verb{| anything indented is
+ verbatim @@verb@{|foo bar baz|@}
+|}")
+ end
+
+ # = Huge heading should be a @majorheading
+ # == There is also @chapheading
+ # === Everything deeper becomes a regular @heading
+ # ====== Regardless of its nesting level
+ def test_headings
+ assert_match(/@majorheading\{Huge heading should be a @@majorheading\}/)
+ assert_match(/@chapheading\{There is also @@chapheading\}/)
+ assert_match(/@heading\{Everything deeper becomes a regular @@heading\}/)
+ assert_match(/@heading\{Regardless of its nesting level\}/)
+ end
+
+ # * list item
+ # * list item2
+ #
+ # with a paragraph in between
+ #
+ # - hyphen lists
+ # - are also allowed
+ # and items may flow over lines
+ def test_bullet_lists
+ assert_match("@itemize @bullet
+@item
+list item
+@item
+list item2
+@end itemize")
+ assert_match("@itemize @bullet
+@item
+hyphen lists
+@item
+are also allowed and items may flow over lines
+@end itemize")
+ end
+
+ # 2. numbered lists
+ # 8. are made by
+ # 9. a digit followed by a period
+ def test_numbered_lists
+ end
+
+ # a. alpha lists
+ # b. should be parsed too
+ def test_alpha_lists
+ end
+
+ # [cat] small domestic animal
+ # [+cat+] command to copy standard input
+ # to standard output
+ def test_labelled_lists
+ end
+
+ # * First item.
+ # * Inner item.
+ # * Second inner item.
+ # * Second outer item.
+ def test_nested_lists
+ assert_match("@itemize @bullet
+@item
+First item.
+@itemize @bullet
+@item
+Inner item.
+@item
+Second inner item.
+@end itemize
+@item
+Second outer item.
+@end itemize")
+ end
+
+ def test_internal_hyperlinks
+ # be sure to test multi-word hyperlinks as well.
+ end
+
+ def test_hyperlink_targets
+ end
+
+ def test_web_links
+ # An example of the two-argument form: The official
+ # @uref{ftp://ftp.gnu.org/gnu, GNU ftp site} holds programs and texts.
+
+ # produces:
+ # The official GNU ftp site (ftp://ftp.gnu.org/gnu)
+ # holds programs and texts.
+ # and the HTML output is this:
+ # The official <a href="ftp://ftp.gnu.org/gnu">GNU ftp site</a>
+ # holds programs and texts.
+ end
+
+ # three or more hyphens
+ # ----
+ # should produce a horizontal rule
+ def test_horizontal_rule
+ # gah; not sure texinfo supports horizontal rules
+ end
+
+ private
+
+ # We don't want the whole string inspected if we pass our own
+ # message in.
+ def assert_match(regex, string = @text,
+ message = "Didn't find #{regex.inspect} in #{string}.")
+ assert string[regex] #, message
+ end
+end
diff --git a/test/rdoc/test_rdoc_info_sections.rb b/test/rdoc/test_rdoc_info_sections.rb
new file mode 100644
index 0000000000..2b6ff6adcb
--- /dev/null
+++ b/test/rdoc/test_rdoc_info_sections.rb
@@ -0,0 +1,93 @@
+$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib/'
+require 'fileutils'
+require 'test/unit'
+require 'rdoc/generator/texinfo'
+require 'yaml'
+
+# give us access to check this stuff before it's rendered
+class RDoc::Generator::Texinfo; attr_reader :files, :classes; end
+class RDoc::RDoc; attr_reader :options; attr_reader :gen; end
+
+class TestRdocInfoSections < Test::Unit::TestCase
+ OUTPUT_DIR = "/tmp/rdoc-#{$$}"
+
+ def setup
+ # supress stdout
+ $stdout = File.new('/dev/null','w')
+ $stderr = File.new('/dev/null','w')
+
+ @rdoc = RDoc::RDoc.new
+ @rdoc.document(['--fmt=texinfo',
+ File.expand_path(File.dirname(__FILE__) + '/../lib/rdoc/generator/texinfo.rb'),
+ File.expand_path(File.dirname(__FILE__) + '/../README.txt'),
+ "--op=#{OUTPUT_DIR}"])
+ @text = File.read(OUTPUT_DIR + '/rdoc.texinfo')
+ end
+
+ def teardown
+ $stdout = STDOUT
+ $stderr = STDERR
+ FileUtils.rm_rf OUTPUT_DIR
+ end
+
+ def test_output_exists
+ assert ! @text.empty?
+ end
+
+ def test_each_class_has_a_chapter
+ assert_section "Class RDoc::Generator::Texinfo", '@chapter'
+ assert_section "Class RDoc::Generator::TexinfoTemplate", '@chapter'
+ end
+
+ def test_class_descriptions_are_given
+ assert_match(/This generates .*Texinfo.* files for viewing with GNU Info or Emacs from .*RDoc.* extracted from Ruby source files/, @text.gsub("\n", ' '))
+ end
+
+ def test_included_modules_are_given
+ assert_match(/Includes.* Generator::MarkUp/m, @text)
+ end
+
+ def test_class_methods_are_given
+ assert_match(/new\(options\)/, @text)
+ end
+
+ def test_classes_instance_methods_are_given
+ assert_section 'Class RDoc::Generator::Texinfo#generate'
+ assert_match(/generate\(toplevels\)/, @text)
+ end
+
+ def test_each_module_has_a_chapter
+ assert_section "RDoc", '@chapter'
+ assert_section "Generator", '@chapter'
+ end
+
+ def test_methods_are_shown_only_once
+ methods = @rdoc.gen.classes.map { |c| c.methods.map{ |m| c.name + '#' + m.name } }.flatten
+ assert_equal methods, methods.uniq
+ end
+
+# if system "makeinfo --version > /dev/null"
+# def test_compiles_to_info
+# makeinfo_output = `cd #{OUTPUT_DIR} && makeinfo rdoc.texinfo`
+# assert(File.exist?(File.join(OUTPUT_DIR, 'rdoc.info')),
+# "Info file was not compiled: #{makeinfo_output}")
+# end
+# end
+
+# def test_constants_are_documented_somehow
+# assert_section 'DEFAULT_FILENAME' # what kind of section?
+# assert_section 'DEFAULT_INFO_FILENAME'
+# end
+
+# def test_oh_yeah_dont_forget_files
+# end
+
+ private
+ def assert_section(name, command = '@section')
+ assert_match Regexp.new("^#{command}.*#{Regexp.escape name}"), @text, "Could not find a #{command} #{name}"
+ end
+
+# def puts(*args)
+# @real_stdout.puts(*args)
+# end
+end
diff --git a/test/rdoc/test_rdoc_markup_to_html.rb b/test/rdoc/test_rdoc_markup_to_html.rb
new file mode 100644
index 0000000000..463228cc4a
--- /dev/null
+++ b/test/rdoc/test_rdoc_markup_to_html.rb
@@ -0,0 +1,30 @@
+require 'test/unit'
+require 'rdoc/markup'
+require 'rdoc/markup/to_html'
+
+class TestRdocMarkupToHtml < Test::Unit::TestCase
+
+ def setup
+ @am = RDoc::Markup::AttributeManager.new
+ @th = RDoc::Markup::ToHtml.new
+ end
+
+ def test_tt_formatting
+ assert_equal "<p>\n<tt>--</tt> &#8212; <tt>(c)</tt> &#169;\n</p>\n",
+ util_format("<tt>--</tt> -- <tt>(c)</tt> (c)")
+ assert_equal "<p>\n<b>&#8212;</b>\n</p>\n", util_format("<b>--</b>")
+ end
+
+ def util_fragment(text)
+ RDoc::Markup::Fragment.new 0, nil, nil, text
+ end
+
+ def util_format(text)
+ fragment = util_fragment text
+
+ @th.start_accepting
+ @th.accept_paragraph @am, fragment
+ @th.end_accepting
+ end
+
+end
diff --git a/test/rdoc/test_rdoc_markup_to_html_crossref.rb b/test/rdoc/test_rdoc_markup_to_html_crossref.rb
new file mode 100644
index 0000000000..ab4c3e7e9c
--- /dev/null
+++ b/test/rdoc/test_rdoc_markup_to_html_crossref.rb
@@ -0,0 +1,18 @@
+require 'test/unit'
+require 'rdoc/generator'
+require 'rdoc/markup/to_html_crossref'
+
+class TestRdocMarkupToHtmlCrossref < Test::Unit::TestCase
+
+ def setup
+ @xref = RDoc::Markup::ToHtmlCrossref.new 'from_path', nil, nil
+ end
+
+ def test_handle_special_CROSSREF_no_underscore
+ out = @xref.convert 'foo'
+
+ assert_equal "<p>\nfoo\n</p>\n", out
+ end
+
+end
+
diff --git a/test/rdoc/test_rdoc_c_parser.rb b/test/rdoc/test_rdoc_parser_c.rb
index 72d09038a4..a52018f372 100644
--- a/test/rdoc/test_rdoc_c_parser.rb
+++ b/test/rdoc/test_rdoc_parser_c.rb
@@ -1,15 +1,16 @@
require 'stringio'
require 'tempfile'
require 'test/unit'
-require 'rdoc/parsers/parse_c'
+require 'rdoc/options'
+require 'rdoc/parser/c'
-class RDoc::C_Parser
+class RDoc::Parser::C
attr_accessor :classes
public :do_classes, :do_constants
end
-class TestRdocC_Parser < Test::Unit::TestCase
+class TestRdocParserC < Test::Unit::TestCase
def setup
@tempfile = Tempfile.new self.class.name
@@ -252,7 +253,7 @@ Init_Foo(void) {
end
def util_parser(content)
- parser = RDoc::C_Parser.new @top_level, @fn, content, @options, @stats
+ parser = RDoc::Parser::C.new @top_level, @fn, content, @options, @stats
parser.progress = @progress
parser
end
diff --git a/test/rdoc/test_rdoc_parser_ruby.rb b/test/rdoc/test_rdoc_parser_ruby.rb
new file mode 100644
index 0000000000..2bf659e566
--- /dev/null
+++ b/test/rdoc/test_rdoc_parser_ruby.rb
@@ -0,0 +1,500 @@
+require 'stringio'
+require 'tempfile'
+require 'test/unit'
+
+require 'rdoc/options'
+require 'rdoc/parser/ruby'
+require 'rdoc/stats'
+
+class TestRdocParserRuby < Test::Unit::TestCase
+
+ def setup
+ @tempfile = Tempfile.new self.class.name
+ @filename = @tempfile.path
+
+ util_toplevel
+ @options = RDoc::Options.new Hash.new
+ @options.quiet = true
+ @stats = RDoc::Stats.new
+
+ @progress = StringIO.new
+ end
+
+ def teardown
+ @tempfile.unlink
+ end
+
+ def test_look_for_directives_in_commented
+ util_parser ""
+
+ comment = "# how to make a section:\n# # :section: new section\n"
+
+ @parser.look_for_directives_in @top_level, comment
+
+ section = @top_level.current_section
+ assert_equal nil, section.title
+ assert_equal nil, section.comment
+
+ assert_equal "# how to make a section:\n# # :section: new section\n",
+ comment
+ end
+
+ def test_look_for_directives_in_enddoc
+ util_parser ""
+
+ assert_throws :enddoc do
+ @parser.look_for_directives_in @top_level, "# :enddoc:\n"
+ end
+ end
+
+ def test_look_for_directives_in_main
+ util_parser ""
+
+ @parser.look_for_directives_in @top_level, "# :main: new main page\n"
+
+ assert_equal 'new main page', @options.main_page
+ end
+
+ def test_look_for_directives_in_method
+ util_parser ""
+
+ comment = "# :method: my_method\n"
+
+ @parser.look_for_directives_in @top_level, comment
+
+ assert_equal "# :method: my_method\n", comment
+
+ comment = "# :singleton-method: my_method\n"
+
+ @parser.look_for_directives_in @top_level, comment
+
+ assert_equal "# :singleton-method: my_method\n", comment
+ end
+
+ def test_look_for_directives_in_startdoc
+ util_parser ""
+
+ @top_level.stop_doc
+ assert !@top_level.document_self
+ assert !@top_level.document_children
+ assert !@top_level.force_documentation
+
+ @parser.look_for_directives_in @top_level, "# :startdoc:\n"
+
+ assert @top_level.document_self
+ assert @top_level.document_children
+ assert @top_level.force_documentation
+ end
+
+ def test_look_for_directives_in_stopdoc
+ util_parser ""
+
+ assert @top_level.document_self
+ assert @top_level.document_children
+
+ @parser.look_for_directives_in @top_level, "# :stopdoc:\n"
+
+ assert !@top_level.document_self
+ assert !@top_level.document_children
+ end
+
+ def test_look_for_directives_in_section
+ util_parser ""
+
+ comment = "# :section: new section\n# woo stuff\n"
+
+ @parser.look_for_directives_in @top_level, comment
+
+ section = @top_level.current_section
+ assert_equal 'new section', section.title
+ assert_equal "# woo stuff\n", section.comment
+
+ assert_equal '', comment
+ end
+
+ def test_look_for_directives_in_title
+ util_parser ""
+
+ @parser.look_for_directives_in @top_level, "# :title: new title\n"
+
+ assert_equal 'new title', @options.title
+ end
+
+ def test_look_for_directives_in_unhandled
+ util_parser ""
+
+ comment = "# :unhandled: \n# :title: hi\n"
+
+ @parser.look_for_directives_in @top_level, comment
+
+ assert_equal "# :unhandled: \n", comment
+
+ assert_equal 'hi', @options.title
+ end
+
+ def test_parse_meta_method
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ comment = "##\n# my method\n"
+
+ util_parser "add_my_method :foo, :bar\nadd_my_method :baz"
+
+ tk = @parser.get_tk
+
+ @parser.parse_meta_method klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ foo = klass.method_list.first
+ assert_equal 'foo', foo.name
+ assert_equal comment, foo.comment
+
+ assert_equal [], foo.aliases
+ assert_equal nil, foo.block_params
+ assert_equal nil, foo.call_seq
+ assert_equal true, foo.document_children
+ assert_equal true, foo.document_self
+ assert_equal false, foo.done_documenting
+ assert_equal false, foo.dont_rename_initialize
+ assert_equal false, foo.force_documentation
+ assert_equal nil, foo.is_alias_for
+ assert_equal '', foo.params
+ assert_equal klass, foo.parent
+ assert_equal false, foo.singleton
+ assert_equal 'add_my_method :foo', foo.text
+ assert_equal nil, foo.viewer
+ assert_equal :public, foo.visibility
+ assert_equal klass.current_section, foo.section
+
+ stream = [
+ tk(:COMMENT, 1, 1, nil, "# File #{@top_level.file_absolute_name}, line 1"),
+ RDoc::Parser::Ruby::NEWLINE_TOKEN,
+ tk(:SPACE, 1, 1, nil, ''),
+ tk(:IDENTIFIER, 1, 0, 'add_my_method', 'add_my_method'),
+ tk(:SPACE, 1, 13, nil, ' '),
+ tk(:SYMBOL, 1, 14, nil, ':foo'),
+ tk(:COMMA, 1, 18, nil, ','),
+ tk(:SPACE, 1, 19, nil, ' '),
+ tk(:SYMBOL, 1, 20, nil, ':bar'),
+ tk(:NL, 1, 24, nil, "\n"),
+ ]
+
+ assert_equal stream, foo.token_stream
+ end
+
+ def test_parse_meta_method_name
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ comment = "##\n# :method: woo_hoo!\n# my method\n"
+
+ util_parser "add_my_method :foo, :bar\nadd_my_method :baz"
+
+ tk = @parser.get_tk
+
+ @parser.parse_meta_method klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ foo = klass.method_list.first
+ assert_equal 'woo_hoo!', foo.name
+ assert_equal "##\n# my method\n", foo.comment
+ end
+
+ def test_parse_meta_method_singleton
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ comment = "##\n# :singleton-method:\n# my method\n"
+
+ util_parser "add_my_method :foo, :bar\nadd_my_method :baz"
+
+ tk = @parser.get_tk
+
+ @parser.parse_meta_method klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ foo = klass.method_list.first
+ assert_equal 'foo', foo.name
+ assert_equal true, foo.singleton, 'singleton method'
+ assert_equal "##\n# my method\n", foo.comment
+ end
+
+ def test_parse_meta_method_singleton_name
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ comment = "##\n# :singleton-method: woo_hoo!\n# my method\n"
+
+ util_parser "add_my_method :foo, :bar\nadd_my_method :baz"
+
+ tk = @parser.get_tk
+
+ @parser.parse_meta_method klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ foo = klass.method_list.first
+ assert_equal 'woo_hoo!', foo.name
+ assert_equal true, foo.singleton, 'singleton method'
+ assert_equal "##\n# my method\n", foo.comment
+ end
+
+ def test_parse_meta_method_string_name
+ klass = RDoc::NormalClass.new 'Foo'
+ comment = "##\n# my method\n"
+
+ util_parser "add_my_method 'foo'"
+
+ tk = @parser.get_tk
+
+ @parser.parse_meta_method klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ foo = klass.method_list.first
+ assert_equal 'foo', foo.name
+ assert_equal comment, foo.comment
+ end
+
+ def test_parse_method
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ comment = "##\n# my method\n"
+
+ util_parser "def foo() :bar end"
+
+ tk = @parser.get_tk
+
+ @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ foo = klass.method_list.first
+ assert_equal 'foo', foo.name
+ assert_equal comment, foo.comment
+
+ assert_equal [], foo.aliases
+ assert_equal nil, foo.block_params
+ assert_equal nil, foo.call_seq
+ assert_equal nil, foo.is_alias_for
+ assert_equal nil, foo.viewer
+ assert_equal true, foo.document_children
+ assert_equal true, foo.document_self
+ assert_equal '()', foo.params
+ assert_equal false, foo.done_documenting
+ assert_equal false, foo.dont_rename_initialize
+ assert_equal false, foo.force_documentation
+ assert_equal klass, foo.parent
+ assert_equal false, foo.singleton
+ assert_equal :public, foo.visibility
+ assert_equal 'def foo', foo.text
+ assert_equal klass.current_section, foo.section
+
+ stream = [
+ tk(:COMMENT, 1, 1, nil, "# File #{@top_level.file_absolute_name}, line 1"),
+ RDoc::Parser::Ruby::NEWLINE_TOKEN,
+ tk(:SPACE, 1, 1, nil, ''),
+ tk(:DEF, 1, 0, 'def', 'def'),
+ tk(:SPACE, 1, 3, nil, ' '),
+ tk(:IDENTIFIER, 1, 4, 'foo', 'foo'),
+ tk(:LPAREN, 1, 7, nil, '('),
+ tk(:RPAREN, 1, 8, nil, ')'),
+ tk(:SPACE, 1, 9, nil, ' '),
+ tk(:COLON, 1, 10, nil, ':'),
+ tk(:IDENTIFIER, 1, 11, 'bar', 'bar'),
+ tk(:SPACE, 1, 14, nil, ' '),
+ tk(:END, 1, 15, 'end', 'end'),
+ ]
+
+ assert_equal stream, foo.token_stream
+ end
+
+ def test_parse_statements_comment
+ content = <<-EOF
+class Foo
+ ##
+ # :method: my_method
+ # my method comment
+
+end
+ EOF
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ comment = "##\n# :method: foo\n# my method\n"
+
+ util_parser "\n"
+
+ tk = @parser.get_tk
+
+ @parser.parse_comment klass, tk, comment
+
+ foo = klass.method_list.first
+ assert_equal 'foo', foo.name
+ assert_equal comment, foo.comment
+
+ assert_equal [], foo.aliases
+ assert_equal nil, foo.block_params
+ assert_equal nil, foo.call_seq
+ assert_equal nil, foo.is_alias_for
+ assert_equal nil, foo.viewer
+ assert_equal true, foo.document_children
+ assert_equal true, foo.document_self
+ assert_equal '', foo.params
+ assert_equal false, foo.done_documenting
+ assert_equal false, foo.dont_rename_initialize
+ assert_equal false, foo.force_documentation
+ assert_equal klass, foo.parent
+ assert_equal false, foo.singleton
+ assert_equal :public, foo.visibility
+ assert_equal "\n", foo.text
+ assert_equal klass.current_section, foo.section
+
+ stream = [
+ tk(:COMMENT, 1, 1, nil, "# File #{@top_level.file_absolute_name}, line 1"),
+ RDoc::Parser::Ruby::NEWLINE_TOKEN,
+ tk(:SPACE, 1, 1, nil, ''),
+ ]
+
+ assert_equal stream, foo.token_stream
+ end
+
+ def test_parse_statements_identifier_meta_method
+ content = <<-EOF
+class Foo
+ ##
+ # this is my method
+ add_my_method :foo
+end
+ EOF
+
+ util_parser content
+
+ @parser.parse_statements @top_level, RDoc::Parser::Ruby::NORMAL, nil, ''
+
+ foo = @top_level.classes.first.method_list.first
+ assert_equal 'foo', foo.name
+ end
+
+ def test_parse_statements_identifier_alias_method
+ content = "class Foo def foo() end; alias_method :foo2, :foo end"
+
+ util_parser content
+
+ @parser.parse_statements @top_level, RDoc::Parser::Ruby::NORMAL, nil, ''
+
+ foo2 = @top_level.classes.first.method_list.last
+ assert_equal 'foo2', foo2.name
+ assert_equal 'foo', foo2.is_alias_for.name
+ end
+
+ def test_parse_statements_identifier_attr
+ content = "class Foo; attr :foo; end"
+
+ util_parser content
+
+ @parser.parse_statements @top_level, RDoc::Parser::Ruby::NORMAL, nil, ''
+
+ foo = @top_level.classes.first.attributes.first
+ assert_equal 'foo', foo.name
+ assert_equal 'R', foo.rw
+ end
+
+ def test_parse_statements_identifier_attr_accessor
+ content = "class Foo; attr_accessor :foo; end"
+
+ util_parser content
+
+ @parser.parse_statements @top_level, RDoc::Parser::Ruby::NORMAL, nil, ''
+
+ foo = @top_level.classes.first.attributes.first
+ assert_equal 'foo', foo.name
+ assert_equal 'RW', foo.rw
+ end
+
+ def test_parse_statements_identifier_extra_accessors
+ @options.extra_accessors = /^my_accessor$/
+
+ content = "class Foo; my_accessor :foo; end"
+
+ util_parser content
+
+ @parser.parse_statements @top_level, RDoc::Parser::Ruby::NORMAL, nil, ''
+
+ foo = @top_level.classes.first.attributes.first
+ assert_equal 'foo', foo.name
+ assert_equal '?', foo.rw
+ end
+
+ def test_parse_statements_identifier_include
+ content = "class Foo; include Bar; end"
+
+ util_parser content
+
+ @parser.parse_statements @top_level, RDoc::Parser::Ruby::NORMAL, nil, ''
+
+ foo = @top_level.classes.first
+ assert_equal 'Foo', foo.name
+ assert_equal 1, foo.includes.length
+ end
+
+ def test_parse_statements_identifier_module_function
+ content = "module Foo def foo() end; module_function :foo; end"
+
+ util_parser content
+
+ @parser.parse_statements @top_level, RDoc::Parser::Ruby::NORMAL, nil, ''
+
+ foo, s_foo = @top_level.modules.first.method_list
+ assert_equal 'foo', foo.name, 'instance method name'
+ assert_equal :private, foo.visibility, 'instance method visibility'
+ assert_equal false, foo.singleton, 'instance method singleton'
+
+ assert_equal 'foo', s_foo.name, 'module function name'
+ assert_equal :public, s_foo.visibility, 'module function visibility'
+ assert_equal true, s_foo.singleton, 'module function singleton'
+ end
+
+ def test_parse_statements_identifier_private
+ content = "class Foo private; def foo() end end"
+
+ util_parser content
+
+ @parser.parse_statements @top_level, RDoc::Parser::Ruby::NORMAL, nil, ''
+
+ foo = @top_level.classes.first.method_list.first
+ assert_equal 'foo', foo.name
+ assert_equal :private, foo.visibility
+ end
+
+ def test_parse_statements_identifier_require
+ content = "require 'bar'"
+
+ util_parser content
+
+ @parser.parse_statements @top_level, RDoc::Parser::Ruby::NORMAL, nil, ''
+
+ assert_equal 1, @top_level.requires.length
+ end
+
+ def tk(klass, line, char, name, text)
+ klass = RDoc::RubyToken.const_get "Tk#{klass.to_s.upcase}"
+
+ token = if klass.instance_method(:initialize).arity == 2 then
+ raise ArgumentError, "name not used for #{klass}" unless name.nil?
+ klass.new line, char
+ else
+ klass.new line, char, name
+ end
+
+ token.set_text text
+
+ token
+ end
+
+ def util_parser(content)
+ @parser = RDoc::Parser::Ruby.new @top_level, @filename, content, @options,
+ @stats
+ @parser.progress = @progress
+ @parser
+ end
+
+ def util_toplevel
+ RDoc::TopLevel.reset
+ @top_level = RDoc::TopLevel.new @filename
+ end
+
+end
+
diff --git a/test/rdoc/test_rdoc_ri_default_display.rb b/test/rdoc/test_rdoc_ri_default_display.rb
index d92516f3ab..97fa6c94ae 100644
--- a/test/rdoc/test_rdoc_ri_default_display.rb
+++ b/test/rdoc/test_rdoc_ri_default_display.rb
@@ -4,7 +4,7 @@ require 'rdoc/ri/formatter'
require 'rdoc/ri/display'
require 'rdoc/ri/driver'
-class TestRDocRIDefaultDisplay < Test::Unit::TestCase
+class TestRdocRiDefaultDisplay < Test::Unit::TestCase
def setup
@output = StringIO.new
@@ -14,7 +14,7 @@ class TestRDocRIDefaultDisplay < Test::Unit::TestCase
@dd = RDoc::RI::DefaultDisplay.new RDoc::RI::Formatter, @width, true,
@output
- @some_method = {
+ @some_method = h \
'aliases' => [{'name' => 'some_method_alias'}],
'block_params' => 'block_param',
'comment' => [RDoc::Markup::Flow::P.new('some comment')],
@@ -23,13 +23,12 @@ class TestRDocRIDefaultDisplay < Test::Unit::TestCase
'name' => 'some_method',
'params' => '(arg1, arg2) {|block_param| ...}',
'source_path' => '/nonexistent',
- 'visibility' => 'public',
- }
+ 'visibility' => 'public'
end
def test_display_class_info
ri_reader = nil
- klass = {
+ klass = h \
'attributes' => [
{ 'name' => 'attribute', 'rw' => 'RW',
'comment' => [RDoc::Markup::Flow::P.new('attribute comment')] },
@@ -58,8 +57,7 @@ class TestRDocRIDefaultDisplay < Test::Unit::TestCase
'instance_method_extensions' => [
{ 'name' => 'instance_method_extension' },
],
- 'superclass_string' => 'Object',
- }
+ 'superclass_string' => 'Object'
@dd.display_class_info klass, ri_reader
@@ -154,7 +152,7 @@ Attributes:
end
def test_display_method_info_singleton
- method = {
+ method = RDoc::RI::Driver::Hash.new.update \
'aliases' => [],
'block_params' => nil,
'comment' => nil,
@@ -162,8 +160,7 @@ Attributes:
'is_singleton' => true,
'name' => 'some_method',
'params' => '(arg1, arg2)',
- 'visibility' => 'public',
- }
+ 'visibility' => 'public'
@dd.display_method_info method
@@ -179,7 +176,7 @@ Attributes:
def test_display_method_list
methods = [
- {
+ RDoc::RI::Driver::Hash.new.update(
"aliases" => [],
"block_params" => nil,
"comment" => nil,
@@ -187,9 +184,9 @@ Attributes:
"is_singleton" => false,
"name" => "some_method",
"params" => "()",
- "visibility" => "public",
- },
- {
+ "visibility" => "public"
+ ),
+ RDoc::RI::Driver::Hash.new.update(
"aliases" => [],
"block_params" => nil,
"comment" => nil,
@@ -197,8 +194,8 @@ Attributes:
"is_singleton" => false,
"name" => "some_other_method",
"params" => "()",
- "visibility" => "public",
- },
+ "visibility" => "public"
+ ),
]
@dd.display_method_list methods
@@ -291,5 +288,9 @@ install an additional package, or ask the packager to enable ri generation.
assert_equal expected, @output.string
end
+ def h(hash)
+ RDoc::RI::Driver::Hash.convert hash
+ end
+
end
diff --git a/test/rdoc/test_rdoc_ri_driver.rb b/test/rdoc/test_rdoc_ri_driver.rb
new file mode 100644
index 0000000000..5db831915a
--- /dev/null
+++ b/test/rdoc/test_rdoc_ri_driver.rb
@@ -0,0 +1,100 @@
+require 'test/unit'
+require 'tmpdir'
+require 'rdoc/ri/driver'
+
+class TestRDocRIDriver < Test::Unit::TestCase
+
+ def setup
+ @tmpdir = File.join Dir.tmpdir, "test_rdoc_ri_driver_#{$$}"
+ @home_ri = File.join @tmpdir, 'dot_ri'
+ @cache_dir = File.join @home_ri, 'cache'
+ @class_cache = File.join @cache_dir, 'classes'
+
+ FileUtils.mkdir_p @tmpdir
+ FileUtils.mkdir_p @home_ri
+ FileUtils.mkdir_p @cache_dir
+
+ @driver = RDoc::RI::Driver.new
+ @driver.homepath = @home_ri
+ end
+
+ def teardown
+ FileUtils.rm_rf @tmpdir
+ end
+
+ def test_lookup_method
+ def @driver.load_cache_for(klassname)
+ { 'Foo#bar' => :found }
+ end
+
+ assert @driver.lookup_method('Foo#bar', 'Foo')
+ end
+
+ def test_lookup_method_class_method
+ def @driver.load_cache_for(klassname)
+ { 'Foo::Bar' => :found }
+ end
+
+ assert @driver.lookup_method('Foo::Bar', 'Foo::Bar')
+ end
+
+ def test_lookup_method_class_missing
+ def @driver.load_cache_for(klassname) end
+
+ e = assert_raise RDoc::RI::Driver::NotFoundError do
+ @driver.lookup_method 'Foo#bar', 'Foo'
+ end
+
+ assert_equal 'Nothing known about Foo#bar', e.message
+ end
+
+ def test_lookup_method_dot_instance
+ def @driver.load_cache_for(klassname)
+ { 'Foo#bar' => :instance, 'Foo::bar' => :klass }
+ end
+
+ assert_equal :instance, @driver.lookup_method('Foo.bar', 'Foo')
+ end
+
+ def test_lookup_method_dot_class
+ def @driver.load_cache_for(klassname)
+ { 'Foo::bar' => :found }
+ end
+
+ assert @driver.lookup_method('Foo.bar', 'Foo')
+ end
+
+ def test_lookup_method_method_missing
+ def @driver.load_cache_for(klassname) {} end
+
+ e = assert_raise RDoc::RI::Driver::NotFoundError do
+ @driver.lookup_method 'Foo#bar', 'Foo'
+ end
+
+ assert_equal 'Nothing known about Foo#bar', e.message
+ end
+
+ def test_parse_name
+ klass, meth = @driver.parse_name 'Foo::Bar'
+
+ assert_equal 'Foo::Bar', klass, 'Foo::Bar class'
+ assert_equal nil, meth, 'Foo::Bar method'
+
+ klass, meth = @driver.parse_name 'Foo#Bar'
+
+ assert_equal 'Foo', klass, 'Foo#Bar class'
+ assert_equal 'Bar', meth, 'Foo#Bar method'
+
+ klass, meth = @driver.parse_name 'Foo.Bar'
+
+ assert_equal 'Foo', klass, 'Foo#Bar class'
+ assert_equal 'Bar', meth, 'Foo#Bar method'
+
+ klass, meth = @driver.parse_name 'Foo::bar'
+
+ assert_equal 'Foo', klass, 'Foo::bar class'
+ assert_equal 'bar', meth, 'Foo::bar method'
+ end
+
+end
+