summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordrbrain <drbrain@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2010-12-20 03:22:49 +0000
committerdrbrain <drbrain@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2010-12-20 03:22:49 +0000
commit2ef9c50c6e405717d06362787c4549ca4f1c6485 (patch)
treeee99486567461dd5796f3d6edcc9e204187f2666
parentd7effd506f5b91a636f2e6452ef1946b923007c7 (diff)
Import RDoc 3
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@30249 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--ChangeLog4
-rw-r--r--lib/rdoc.rb313
-rw-r--r--lib/rdoc/alias.rb79
-rw-r--r--lib/rdoc/any_method.rb177
-rw-r--r--lib/rdoc/attr.rb142
-rw-r--r--lib/rdoc/class_module.rb258
-rw-r--r--lib/rdoc/code_object.rb107
-rw-r--r--lib/rdoc/constant.rb36
-rw-r--r--lib/rdoc/context.rb711
-rw-r--r--lib/rdoc/encoding.rb79
-rw-r--r--lib/rdoc/erbio.rb37
-rw-r--r--lib/rdoc/generator.rb34
-rw-r--r--lib/rdoc/generator/darkfish.rb658
-rw-r--r--lib/rdoc/generator/markup.rb108
-rw-r--r--lib/rdoc/generator/ri.rb6
-rw-r--r--lib/rdoc/generator/template/darkfish/classpage.rhtml539
-rw-r--r--lib/rdoc/generator/template/darkfish/filepage.rhtml182
-rw-r--r--lib/rdoc/generator/template/darkfish/index.rhtml90
-rw-r--r--lib/rdoc/generator/template/darkfish/rdoc.css596
-rw-r--r--lib/rdoc/include.rb41
-rw-r--r--lib/rdoc/markup.rb469
-rw-r--r--lib/rdoc/markup/attribute_manager.rb33
-rw-r--r--lib/rdoc/markup/blank_line.rb14
-rw-r--r--lib/rdoc/markup/document.rb6
-rw-r--r--lib/rdoc/markup/formatter.rb10
-rw-r--r--lib/rdoc/markup/formatter_test_case.rb342
-rw-r--r--lib/rdoc/markup/heading.rb3
-rw-r--r--lib/rdoc/markup/inline.rb12
-rw-r--r--lib/rdoc/markup/list.rb3
-rw-r--r--lib/rdoc/markup/list_item.rb3
-rw-r--r--lib/rdoc/markup/paragraph.rb3
-rw-r--r--lib/rdoc/markup/parser.rb426
-rw-r--r--lib/rdoc/markup/pre_process.rb (renamed from lib/rdoc/markup/preprocess.rb)79
-rw-r--r--lib/rdoc/markup/raw.rb4
-rw-r--r--lib/rdoc/markup/rule.rb3
-rw-r--r--lib/rdoc/markup/text_formatter_test_case.rb116
-rw-r--r--lib/rdoc/markup/to_ansi.rb16
-rw-r--r--lib/rdoc/markup/to_bs.rb10
-rw-r--r--lib/rdoc/markup/to_html.rb175
-rw-r--r--lib/rdoc/markup/to_html_crossref.rb109
-rw-r--r--lib/rdoc/markup/to_rdoc.rb143
-rw-r--r--lib/rdoc/markup/to_test.rb10
-rw-r--r--lib/rdoc/markup/verbatim.rb9
-rw-r--r--lib/rdoc/method_attr.rb353
-rw-r--r--lib/rdoc/normal_class.rb13
-rw-r--r--lib/rdoc/normal_module.rb5
-rw-r--r--lib/rdoc/options.rb459
-rw-r--r--lib/rdoc/parser.rb69
-rw-r--r--lib/rdoc/parser/c.rb414
-rw-r--r--lib/rdoc/parser/perl.rb165
-rw-r--r--lib/rdoc/parser/ruby.rb333
-rw-r--r--lib/rdoc/parser/ruby_tools.rb5
-rw-r--r--lib/rdoc/parser/simple.rb13
-rw-r--r--lib/rdoc/rdoc.rb104
-rw-r--r--lib/rdoc/require.rb21
-rw-r--r--lib/rdoc/ri/driver.rb68
-rw-r--r--lib/rdoc/ri/paths.rb15
-rw-r--r--lib/rdoc/ri/store.rb25
-rw-r--r--lib/rdoc/ruby_lex.rb23
-rw-r--r--lib/rdoc/ruby_token.rb4
-rw-r--r--lib/rdoc/single_class.rb3
-rw-r--r--lib/rdoc/stats.rb364
-rw-r--r--lib/rdoc/stats/normal.rb51
-rw-r--r--lib/rdoc/stats/quiet.rb59
-rw-r--r--lib/rdoc/stats/verbose.rb45
-rw-r--r--lib/rdoc/task.rb231
-rw-r--r--lib/rdoc/text.rb137
-rw-r--r--lib/rdoc/token_stream.rb (renamed from lib/rdoc/tokenstream.rb)2
-rw-r--r--lib/rdoc/top_level.rb269
-rw-r--r--test/rdoc/test.ja.rdoc2
-rw-r--r--test/rdoc/test_attribute_manager.rb69
-rw-r--r--test/rdoc/test_rdoc_alias.rb13
-rw-r--r--test/rdoc/test_rdoc_any_method.rb45
-rw-r--r--test/rdoc/test_rdoc_attr.rb29
-rw-r--r--test/rdoc/test_rdoc_class_module.rb133
-rw-r--r--test/rdoc/test_rdoc_code_object.rb67
-rw-r--r--test/rdoc/test_rdoc_context.rb101
-rw-r--r--test/rdoc/test_rdoc_encoding.rb145
-rw-r--r--test/rdoc/test_rdoc_generator_darkfish.rb119
-rw-r--r--test/rdoc/test_rdoc_generator_ri.rb24
-rw-r--r--test/rdoc/test_rdoc_include.rb79
-rw-r--r--test/rdoc/test_rdoc_markup_attribute_manager.rb8
-rw-r--r--test/rdoc/test_rdoc_markup_parser.rb244
-rw-r--r--test/rdoc/test_rdoc_markup_pre_process.rb12
-rw-r--r--test/rdoc/test_rdoc_markup_to_ansi.rb196
-rw-r--r--test/rdoc/test_rdoc_markup_to_bs.rb198
-rw-r--r--test/rdoc/test_rdoc_markup_to_html.rb188
-rw-r--r--test/rdoc/test_rdoc_markup_to_html_crossref.rb25
-rw-r--r--test/rdoc/test_rdoc_markup_to_rdoc.rb191
-rw-r--r--test/rdoc/test_rdoc_method_attr.rb122
-rw-r--r--test/rdoc/test_rdoc_normal_class.rb2
-rw-r--r--test/rdoc/test_rdoc_normal_module.rb7
-rw-r--r--test/rdoc/test_rdoc_options.rb249
-rw-r--r--test/rdoc/test_rdoc_parser.rb35
-rw-r--r--test/rdoc/test_rdoc_parser_c.rb366
-rw-r--r--test/rdoc/test_rdoc_parser_perl.rb73
-rw-r--r--test/rdoc/test_rdoc_parser_ruby.rb497
-rw-r--r--test/rdoc/test_rdoc_parser_simple.rb26
-rw-r--r--test/rdoc/test_rdoc_rdoc.rb89
-rw-r--r--test/rdoc/test_rdoc_ri_driver.rb72
-rw-r--r--test/rdoc/test_rdoc_ri_store.rb49
-rw-r--r--test/rdoc/test_rdoc_task.rb2
-rw-r--r--test/rdoc/test_rdoc_text.rb110
-rw-r--r--test/rdoc/test_rdoc_top_level.rb17
-rw-r--r--test/rdoc/xref_data.rb8
-rw-r--r--test/rdoc/xref_test_case.rb6
106 files changed, 8881 insertions, 4182 deletions
diff --git a/ChangeLog b/ChangeLog
index 579dbaa712..6c0d268288 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+Mon Dec 20 12:15:32 2010 Eric Hodel <drbrain@segment7.net>
+
+ * lib/rdoc: Import RDoc 3.0.
+
Mon Dec 20 01:55:03 2010 KOSAKI Motohiro <kosaki.motohiro@gmail.com>
* io.c (Init_IO): Added O_DIRECT. This feature was propsed by Run Paint Run Run.
diff --git a/lib/rdoc.rb b/lib/rdoc.rb
index 7ce7b53a35..a453ee1039 100644
--- a/lib/rdoc.rb
+++ b/lib/rdoc.rb
@@ -18,14 +18,16 @@ $DEBUG_RDOC = nil
# == Roadmap
#
# * If you want to use RDoc to create documentation for your Ruby source files,
-# read on.
+# read the summary below, and refer to <tt>rdoc --help</tt> for command line
+# usage, and RDoc::Markup for a detailed description of RDoc's markup.
# * If you want to generate documentation for extensions written in C, see
# RDoc::Parser::C
# * If you want to drive RDoc programmatically, see RDoc::RDoc.
-# * If you want to use the library to format text blocks into HTML, have a look
-# at RDoc::Markup.
-# * If you want to try writing your own HTML output template, see
-# RDoc::Generator::HTML
+# * If you want to use the library to format text blocks into HTML, look at
+# RDoc::Markup.
+# * If you want to make an RDoc plugin such as a generator or directive
+# handler see RDoc::RDoc.
+# * If you want to try writing your own output generator see RDoc::Generator.
#
# == Summary
#
@@ -50,7 +52,7 @@ $DEBUG_RDOC = nil
# index page contain the documentation for the primary file. In our
# case, we could type
#
-# % rdoc --main rdoc.rb
+# % rdoc --main README.txt
#
# You'll find information on the various formatting tricks you can use
# in comment blocks in the documentation this generates.
@@ -62,281 +64,9 @@ $DEBUG_RDOC = nil
# markers). If directory names are passed to RDoc, they are scanned
# recursively for C and Ruby source files only.
#
-# == \Options
-#
-# rdoc can be passed a variety of command-line options. In addition,
-# options can be specified via the +RDOCOPT+ environment variable, which
-# functions similarly to the +RUBYOPT+ environment variable.
-#
-# % export RDOCOPT="-S"
-#
-# will make rdoc default to inline method source code. Command-line options
-# always will override those in +RDOCOPT+.
-#
-# Run:
-#
-# rdoc --help
-#
-# for full details on rdoc's options.
-#
-# == Documenting Source Code
-#
-# Comment blocks can be written fairly naturally, either using <tt>#</tt> on
-# successive lines of the comment, or by including the comment in
-# a =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.
-# #--
-# # FIXME: fails if the birthday falls on February 29th
-# #++
-# # The DOB is returned as a Time object.
-#
-# def get_dob(person)
-# # ...
-# end
-#
-# Names of classes, files, and any method names containing an
-# underscore or preceded by a hash character are automatically hyperlinked
-# from comment text to their description.
-#
-# 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.
-#
-# RDoc automatically cross-references words with underscores or camel-case.
-# To suppress cross-references, prefix the word with a \\ character. To
-# include special characters like "\\n", you'll need to use two \\
-# characters like "\\\\\\n".
-#
-# == \Markup
-#
-# * The markup engine looks for a document's natural left margin. This is
-# used as the initial margin for the document.
-#
-# * Consecutive lines starting at this margin are considered to be a
-# paragraph.
-#
-# * If a paragraph starts with a "*", "-", or with "<digit>.", then it is
-# taken to be the start of a list. The margin in increased to be the first
-# non-space following the list start flag. Subsequent lines should be
-# indented to this new margin until the list ends. For example:
-#
-# * this is a list with three paragraphs in
-# the first item. This is the first paragraph.
-#
-# And this is the second paragraph.
-#
-# 1. This is an indented, numbered list.
-# 2. This is the second item in that list
-#
-# This is the third conventional paragraph in the
-# first list item.
-#
-# * This is the second item in the original list
-#
-# * You can also construct labeled lists, sometimes called description
-# or definition lists. Do this by putting the label in square brackets
-# and indenting the list body:
-#
-# [cat] a small furry mammal
-# that seems to sleep a lot
-#
-# [ant] a little insect that is known
-# to enjoy picnics
-#
-# A minor variation on labeled lists uses two colons to separate the
-# label from the list body:
-#
-# cat:: a small furry mammal
-# that seems to sleep a lot
-#
-# ant:: a little insect that is known
-# to enjoy picnics
-#
-# This latter style guarantees that the list bodies' left margins are
-# aligned: think of them as a two column table.
-#
-# * Any line that starts to the right of the current margin is treated
-# as verbatim text. This is useful for code listings. The example of a
-# list above is also verbatim text.
-#
-# * A line starting with an equals sign (=) is treated as a
-# heading. Level one headings have one equals sign, level two headings
-# have two,and so on.
-#
-# * A line starting with three or more hyphens (at the current indent)
-# generates a horizontal rule. The more hyphens, the thicker the rule
-# (within reason, and if supported by the output device)
-#
-# * You can use markup within text (except verbatim) to change the
-# appearance of parts of that text. Out of the box, RDoc::Markup
-# supports word-based and general markup.
-#
-# Word-based markup uses flag characters around individual words:
-#
-# [<tt>\*word*</tt>] displays word in a *bold* font
-# [<tt>\_word_</tt>] displays word in an _emphasized_ font
-# [<tt>\+word+</tt>] displays word in a +code+ font
-#
-# General markup affects text between a start delimiter and and end
-# delimiter. Not surprisingly, these delimiters look like HTML markup.
-#
-# [<tt>\<b>text...</b></tt>] displays word in a *bold* font
-# [<tt>\<em>text...</em></tt>] displays word in an _emphasized_ font
-# [<tt>\<i>text...</i></tt>] displays word in an <i>italicized</i> font
-# [<tt>\<tt>text...\</tt></tt>] displays word in a +code+ font
-#
-# Unlike conventional Wiki markup, general markup can cross line
-# boundaries. You can turn off the interpretation of markup by
-# preceding the first character with a backslash. This only works for
-# simple markup, not HTML-style markup.
-#
-# * Hyperlinks to the web starting http:, mailto:, ftp:, or www. are
-# recognized. An HTTP url that references an external image file is
-# converted into an inline \<IMG..>. Hyperlinks starting 'link:' are
-# assumed to refer to local files whose path is relative to the --op
-# directory.
-#
-# Hyperlinks can also be of the form <tt>label</tt>[url], in which
-# case the label is used in the displayed text, and +url+ is
-# used as the target. If +label+ contains multiple words,
-# put it in braces: <em>{multi word label}[</em>url<em>]</em>.
-#
-# Example hyperlinks:
-#
-# link:RDoc.html
-# http://rdoc.rubyforge.org
-# mailto:user@example.com
-# {RDoc Documentation}[http://rdoc.rubyforge.org]
-# {RDoc Markup}[link:RDoc/Markup.html]
-#
-# == Directives
-#
-# [+:nodoc:+ / +:nodoc:+ all]
-# This directive prevents documentation for the element from
-# being generated. For classes and modules, the methods, aliases,
-# constants, and attributes directly within the affected class or
-# module also will be omitted. By default, though, modules and
-# classes within that class of module _will_ be documented. This is
-# turned off by adding the +all+ modifier.
-#
-# module MyModule # :nodoc:
-# class Input
-# end
-# end
-#
-# module OtherModule # :nodoc: all
-# class Output
-# end
-# end
-#
-# In the above code, only class <tt>MyModule::Input</tt> will be documented.
-# The +:nodoc:+ directive is global across all files for the class or module
-# to which it applies, so use +:stopdoc:+/+:startdoc:+ to suppress
-# documentation only for a particular set of methods, etc.
-#
-# [+:doc:+]
-# Forces a method or attribute to be documented even if it wouldn't be
-# otherwise. Useful if, for example, you want to include documentation of a
-# particular private method.
-#
-# [+:notnew:+]
-# Only applicable to the +initialize+ instance method. Normally RDoc
-# assumes that the documentation and parameters for +initialize+ are
-# actually for the +new+ method, and so fakes out a +new+ for the class.
-# The +:notnew:+ modifier stops this. Remember that +initialize+ is private,
-# 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
-# the section is used as introductory text. Subsequent methods, aliases,
-# attributes, and classes will be documented in this section. A :section:
-# comment block may have one or more lines before the :section: directive.
-# These will be removed, and any identical lines at the end of the block are
-# also removed. This allows you to add visual cues such as:
-#
-# # ----------------------------------------
-# # :section: My Section
-# # 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.
-#
-# [+: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.
-#
-# Further directives can be found in RDoc::Parser::Ruby and RDoc::Parser::C
-#
# == Other stuff
#
-# RDoc is currently being maintained by Eric Hodel <drbrain@segment7.net>
+# RDoc is currently being maintained by Eric Hodel <drbrain@segment7.net>.
#
# Dave Thomas <dave@pragmaticprogrammer.com> is the original author of RDoc.
#
@@ -345,24 +75,6 @@ $DEBUG_RDOC = nil
# * 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.
-#
-# * 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.
module RDoc
@@ -383,7 +95,12 @@ module RDoc
##
# RDoc version you are using
- VERSION = '2.5.8'
+ VERSION = '3.0'
+
+ ##
+ # Method visibilities
+
+ VISIBILITIES = [:public, :protected, :private]
##
# Name of the dotfile that contains the description of files to be processed
diff --git a/lib/rdoc/alias.rb b/lib/rdoc/alias.rb
index 6cd9af09d4..fa433dc0a9 100644
--- a/lib/rdoc/alias.rb
+++ b/lib/rdoc/alias.rb
@@ -3,41 +3,77 @@ require 'rdoc/code_object'
##
# Represent an alias, which is an old_name/new_name pair associated with a
# particular context
+#--
+# TODO implement Alias as a proxy to a method/attribute, inheriting from
+# MethodAttr
class RDoc::Alias < RDoc::CodeObject
##
- # Allow comments to be overridden
+ # Aliased method's name
- attr_writer :comment
+ attr_reader :new_name
+
+ alias name new_name
##
- # Aliased name
+ # Aliasee method's name
- attr_accessor :new_name
+ attr_reader :old_name
##
- # Aliasee's name
+ # Is this an alias declared in a singleton context?
- attr_accessor :old_name
+ attr_accessor :singleton
##
# Source file token stream
- attr_accessor :text
+ attr_reader :text
##
# Creates a new Alias with a token stream of +text+ that aliases +old_name+
- # to +new_name+ and has +comment+
+ # to +new_name+, has +comment+ and is a +singleton+ context.
- def initialize(text, old_name, new_name, comment)
+ def initialize(text, old_name, new_name, comment, singleton = false)
super()
+
@text = text
+ @singleton = singleton
@old_name = old_name
@new_name = new_name
self.comment = comment
end
+ ##
+ # Order by #singleton then #new_name
+
+ def <=>(other)
+ [@singleton ? 0 : 1, new_name] <=> [other.singleton ? 0 : 1, other.new_name]
+ end
+
+ ##
+ # HTML fragment reference for this alias
+
+ def aref
+ type = singleton ? 'c' : 'i'
+ "#alias-#{type}-#{html_name}"
+ end
+
+ ##
+ # Full old name including namespace
+
+ def full_old_name
+ @full_name || "#{parent.name}#{pretty_old_name}"
+ end
+
+ ##
+ # HTML id-friendly version of +#new_name+.
+
+ def html_name
+ CGI.escape(@new_name.gsub('-', '-2D')).gsub('%','-').sub(/^-/, '')
+ end
+
def inspect # :nodoc:
parent_name = parent ? parent.name : '(unknown)'
"#<%s:0x%x %s.alias_method %s, %s>" % [
@@ -46,8 +82,31 @@ class RDoc::Alias < RDoc::CodeObject
]
end
+ ##
+ # '::' for the alias of a singleton method/attribute, '#' for instance-level.
+
+ def name_prefix
+ singleton ? '::' : '#'
+ end
+
+ ##
+ # Old name with prefix '::' or '#'.
+
+ def pretty_old_name
+ "#{singleton ? '::' : '#'}#{@old_name}"
+ end
+
+ ##
+ # New name with prefix '::' or '#'.
+
+ def pretty_new_name
+ "#{singleton ? '::' : '#'}#{@new_name}"
+ end
+
+ alias pretty_name pretty_new_name
+
def to_s # :nodoc:
- "alias: #{self.old_name} -> #{self.new_name}\n#{self.comment}"
+ "alias: #{self.new_name} -> #{self.pretty_old_name} in: #{parent}"
end
end
diff --git a/lib/rdoc/any_method.rb b/lib/rdoc/any_method.rb
index d742daa7b8..f993621f8b 100644
--- a/lib/rdoc/any_method.rb
+++ b/lib/rdoc/any_method.rb
@@ -1,29 +1,12 @@
-require 'rdoc/code_object'
-require 'rdoc/tokenstream'
+require 'rdoc/method_attr'
+require 'rdoc/token_stream'
##
# AnyMethod is the base class for objects representing methods
-class RDoc::AnyMethod < RDoc::CodeObject
+class RDoc::AnyMethod < RDoc::MethodAttr
- MARSHAL_VERSION = 1 # :nodoc:
-
- include Comparable
-
- ##
- # Method name
-
- attr_writer :name
-
- ##
- # public, protected, private
-
- attr_accessor :visibility
-
- ##
- # Parameters yielded by the called block
-
- attr_accessor :block_params
+ MARSHAL_VERSION = 0 # :nodoc:
##
# Don't rename \#initialize to \::new
@@ -31,76 +14,49 @@ class RDoc::AnyMethod < RDoc::CodeObject
attr_accessor :dont_rename_initialize
##
- # Is this a singleton method?
-
- attr_accessor :singleton
-
- ##
- # Source file token stream
-
- attr_reader :text
-
- ##
- # Array of other names for this method
-
- attr_reader :aliases
-
- ##
- # The method we're aliasing
+ # Different ways to call this method
- attr_accessor :is_alias_for
+ attr_accessor :call_seq
##
# Parameters for this method
attr_accessor :params
- ##
- # Different ways to call this method
-
- attr_accessor :call_seq
-
include RDoc::TokenStream
- def initialize(text, name)
- super()
+ ##
+ # Creates a new AnyMethod with a token stream +text+ and +name+
- @text = text
- @name = name
+ def initialize text, name
+ super
- @aliases = []
- @block_params = nil
- @call_seq = nil
@dont_rename_initialize = false
- @is_alias_for = nil
- @params = nil
- @parent_name = nil
- @singleton = nil
@token_stream = nil
- @visibility = :public
end
##
- # Order by #singleton then #name
+ # Adds +an_alias+ as an alias for this method in +context+.
- def <=>(other)
- [@singleton ? 0 : 1, @name] <=> [other.singleton ? 0 : 1, other.name]
- end
-
- ##
- # Adds +method+ as an alias for this method
+ def add_alias(an_alias, context)
+ method = self.class.new an_alias.text, an_alias.new_name
- def add_alias(method)
+ method.record_location an_alias.file
+ method.singleton = self.singleton
+ method.params = self.params
+ method.visibility = self.visibility
+ method.comment = an_alias.comment
+ method.is_alias_for = self
@aliases << method
+ context.add_method method
+ method
end
##
- # HTML fragment reference for this method
+ # Prefix for +aref+ is 'method'.
- def aref
- type = singleton ? 'c' : 'i'
-
- "method-#{type}-#{CGI.escape name}"
+ def aref_prefix
+ 'method'
end
##
@@ -117,30 +73,6 @@ class RDoc::AnyMethod < RDoc::CodeObject
end
##
- # HTML id-friendly method name
-
- def html_name
- @name.gsub(/[^a-z]+/, '-')
- end
-
- def inspect # :nodoc:
- alias_for = @is_alias_for ? " (alias for #{@is_alias_for.name})" : nil
- "#<%s:0x%x %s (%s)%s>" % [
- self.class, object_id,
- full_name,
- visibility,
- alias_for,
- ]
- end
-
- ##
- # Full method name including namespace
-
- def full_name
- @full_name ||= "#{@parent ? @parent.full_name : '(unknown)'}#{pretty_name}"
- end
-
- ##
# Dumps this AnyMethod for use by ri. See also #marshal_load
def marshal_dump
@@ -192,12 +124,14 @@ class RDoc::AnyMethod < RDoc::CodeObject
end
array[8].each do |new_name, comment|
- add_alias RDoc::Alias.new(nil, @name, new_name, comment)
+ add_alias RDoc::Alias.new(nil, @name, new_name, comment, @singleton)
end
end
##
# Method name
+ #
+ # If the method has no assigned name, it extracts it from #call_seq.
def name
return @name if @name
@@ -229,62 +163,5 @@ class RDoc::AnyMethod < RDoc::CodeObject
params
end
- ##
- # Name of our parent with special handling for un-marshaled methods
-
- def parent_name
- @parent_name || super
- end
-
- ##
- # Path to this method
-
- def path
- "#{@parent.path}##{aref}"
- end
-
- ##
- # Method name with class/instance indicator
-
- def pretty_name
- "#{singleton ? '::' : '#'}#{@name}"
- end
-
- def pretty_print q # :nodoc:
- alias_for = @is_alias_for ? "alias for #{@is_alias_for.name}" : nil
-
- q.group 2, "[#{self.class.name} #{full_name} #{visibility}", "]" do
- if alias_for then
- q.breakable
- q.text alias_for
- end
-
- if text then
- q.breakable
- q.text "text:"
- q.breakable
- q.pp @text
- end
-
- unless comment.empty? then
- q.breakable
- q.text "comment:"
- q.breakable
- q.pp @comment
- end
- end
- end
-
- def to_s # :nodoc:
- "#{self.class.name}: #{full_name} (#{@text})\n#{@comment}"
- end
-
- ##
- # Type of method (class or instance)
-
- def type
- singleton ? 'class' : 'instance'
- end
-
end
diff --git a/lib/rdoc/attr.rb b/lib/rdoc/attr.rb
index 9b8c4562c2..97fac3222d 100644
--- a/lib/rdoc/attr.rb
+++ b/lib/rdoc/attr.rb
@@ -1,104 +1,71 @@
-require 'rdoc/code_object'
+require 'rdoc/method_attr'
##
# An attribute created by \#attr, \#attr_reader, \#attr_writer or
# \#attr_accessor
-class RDoc::Attr < RDoc::CodeObject
+class RDoc::Attr < RDoc::MethodAttr
- MARSHAL_VERSION = 0 # :nodoc:
+ MARSHAL_VERSION = 1 # :nodoc:
##
- # Name of the attribute
-
- attr_accessor :name
-
- ##
- # Is the attribute readable, writable or both?
+ # Is the attribute readable ('R'), writable ('W') or both ('RW')?
attr_accessor :rw
##
- # Source file token stream
+ # Creates a new Attr with body +text+, +name+, read/write status +rw+ and
+ # +comment+. +singleton+ marks this as a class attribute.
- attr_accessor :text
+ def initialize(text, name, rw, comment, singleton = false)
+ super text, name
- ##
- # public, protected, private
-
- attr_accessor :visibility
-
- def initialize(text, name, rw, comment)
- super()
- @text = text
- @name = name
@rw = rw
- @visibility = :public
+ @singleton = singleton
self.comment = comment
end
##
- # Attributes are ordered by name
-
- def <=>(other)
- self.name <=> other.name
- end
-
- ##
- # Attributes are equal when their names and rw is identical
+ # Attributes are equal when their names, singleton and rw are identical
def == other
self.class == other.class and
self.name == other.name and
- self.rw == other.rw
+ self.rw == other.rw and
+ self.singleton == other.singleton
end
##
- # Returns nil, for duck typing with RDoc::AnyMethod
+ # Add +an_alias+ as an attribute in +context+.
- def arglists
- end
-
- ##
- # Returns nil, for duck typing with RDoc::AnyMethod
+ def add_alias(an_alias, context)
+ new_attr = self.class.new(self.text, an_alias.new_name, self.rw,
+ self.comment, self.singleton)
- def block_params
+ new_attr.record_location an_alias.file
+ new_attr.visibility = self.visibility
+ new_attr.is_alias_for = self
+ @aliases << new_attr
+ context.add_attribute new_attr
+ new_attr
end
##
- # Returns nil, for duck typing with RDoc::AnyMethod
+ # The #aref prefix for attributes
- def call_seq
+ def aref_prefix
+ 'attribute'
end
##
- # Partially bogus as Attr has no parent. For duck typing with
- # RDoc::AnyMethod.
+ # Returns attr_reader, attr_writer or attr_accessor as appropriate.
- def full_name
- @full_name ||= "#{@parent ? @parent.full_name : '(unknown)'}##{name}"
- end
-
- ##
- # An HTML id-friendly representation of #name
-
- def html_name
- @name.gsub(/[^a-z]+/, '-')
- end
-
- def inspect # :nodoc:
- 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,
- ]
+ def definition
+ case @rw
+ when 'RW' then 'attr_accessor'
+ when 'R' then 'attr_reader'
+ when 'W' then 'attr_writer'
+ end
end
##
@@ -111,11 +78,12 @@ class RDoc::Attr < RDoc::CodeObject
@rw,
@visibility,
parse(@comment),
+ singleton,
]
end
##
- # Loads this AnyMethod from +array+. For a loaded AnyMethod the following
+ # Loads this Attr from +array+. For a loaded Attr the following
# methods will return cached values:
#
# * #full_name
@@ -127,51 +95,13 @@ class RDoc::Attr < RDoc::CodeObject
@rw = array[3]
@visibility = array[4]
@comment = array[5]
+ @singleton = array[6] || false # MARSHAL_VERSION == 0
@parent_name = @full_name
end
- ##
- # Name of our parent with special handling for un-marshaled methods
-
- def parent_name
- @parent_name || super
- end
-
- ##
- # For duck typing with RDoc::AnyMethod, returns nil
-
- def params
- nil
- end
-
- ##
- # URL path for this attribute
-
- def path
- "#{@parent.path}##{@name}"
- end
-
- ##
- # For duck typing with RDoc::AnyMethod
-
- def singleton
- false
- end
-
def to_s # :nodoc:
- "#{type} #{name}\n#{comment}"
- end
-
- ##
- # Returns attr_reader, attr_writer or attr_accessor as appropriate
-
- def type
- case @rw
- when 'RW' then 'attr_accessor'
- when 'R' then 'attr_reader'
- when 'W' then 'attr_writer'
- end
+ "#{definition} #{name} in: #{parent}"
end
end
diff --git a/lib/rdoc/class_module.rb b/lib/rdoc/class_module.rb
index 16b85d7918..1e75699ffa 100644
--- a/lib/rdoc/class_module.rb
+++ b/lib/rdoc/class_module.rb
@@ -8,31 +8,114 @@ class RDoc::ClassModule < RDoc::Context
MARSHAL_VERSION = 0 # :nodoc:
- attr_accessor :diagram
+ ##
+ # Constants that are aliases for this class or module
+
+ attr_accessor :constant_aliases
+
+ attr_accessor :diagram # :nodoc:
##
- # Creates a new ClassModule with +name+ with optional +superclass+
+ # Class or module this constant is an alias for
- def initialize(name, superclass = 'Object')
- @diagram = nil
- @full_name = nil
- @name = name
- @superclass = superclass
+ attr_accessor :is_alias_for
+
+ ##
+ # Return a RDoc::ClassModule of class +class_type+ that is a copy
+ # of module +module+. Used to promote modules to classes.
+
+ def self.from_module(class_type, mod)
+ klass = class_type.new(mod.name)
+ klass.comment = mod.comment
+ klass.parent = mod.parent
+ klass.section = mod.section
+ klass.viewer = mod.viewer
+
+ klass.attributes.concat mod.attributes
+ klass.method_list.concat mod.method_list
+ klass.aliases.concat mod.aliases
+ klass.external_aliases.concat mod.external_aliases
+ klass.constants.concat mod.constants
+ klass.includes.concat mod.includes
+
+ klass.methods_hash.update mod.methods_hash
+ klass.constants_hash.update mod.constants_hash
+
+ klass.current_section = mod.current_section
+ klass.in_files.concat mod.in_files
+ klass.sections.concat mod.sections
+ klass.unmatched_alias_lists = mod.unmatched_alias_lists
+ klass.current_section = mod.current_section
+ klass.visibility = mod.visibility
+
+ klass.classes_hash.update mod.classes_hash
+ klass.modules_hash.update mod.modules_hash
+ klass.metadata.update mod.metadata
+
+ klass.document_self = mod.received_nodoc ? nil : mod.document_self
+ klass.document_children = mod.document_children
+ klass.force_documentation = mod.force_documentation
+ klass.done_documenting = mod.done_documenting
+
+ # update the parent of all children
+
+ (klass.attributes +
+ klass.method_list +
+ klass.aliases +
+ klass.external_aliases +
+ klass.constants +
+ klass.includes +
+ klass.classes +
+ klass.modules).each do |obj|
+ obj.parent = klass
+ obj.full_name = nil
+ end
+
+ klass
+ end
+
+ ##
+ # Creates a new ClassModule with +name+ with optional +superclass+
+ #
+ # This is a constructor for subclasses, and must never be called directly.
+
+ def initialize(name, superclass = nil)
+ @constant_aliases = []
+ @diagram = nil
+ @is_alias_for = nil
+ @name = name
+ @superclass = superclass
super()
end
##
- # Ancestors list for this ClassModule (abstract)
+ # Ancestors list for this ClassModule: the list of included modules
+ # (classes will add their superclass if any).
+ #
+ # Returns the included classes or modules, not the includes
+ # themselves. The returned values are either String or
+ # RDoc::NormalModule instances (see RDoc::Include#module).
+ #
+ # The values are returned in reverse order of their inclusion,
+ # which is the order suitable for searching methods/attributes
+ # in the ancestors. The superclass, if any, comes last.
def ancestors
- raise NotImplementedError
+ includes.map { |i| i.module }.reverse
+ end
+
+ ##
+ # Clears the comment. Used by the ruby parser.
+
+ def clear_comment
+ @comment = ''
end
##
# Appends +comment+ to the current comment, but separated by a rule. Works
# more like <tt>+=</tt>.
- def comment=(comment)
+ def comment= comment
return if comment.empty?
comment = "#{@comment}\n---\n#{normalize_comment comment}" unless
@@ -42,9 +125,34 @@ class RDoc::ClassModule < RDoc::Context
end
##
+ # Prepares this ClassModule for use by a generator.
+ #
+ # See RDoc::TopLevel::complete
+
+ def complete min_visibility
+ update_aliases
+ remove_nodoc_children
+ update_includes
+ remove_invisible min_visibility
+ end
+
+ ##
+ # Looks for a symbol in the #ancestors. See Context#find_local_symbol.
+
+ def find_ancestor_local_symbol symbol
+ ancestors.each do |m|
+ next if m.is_a?(String)
+ res = m.find_local_symbol(symbol)
+ return res if res
+ end
+
+ nil
+ end
+
+ ##
# Finds a class or module with +name+ in this namespace or its descendents
- def find_class_named(name)
+ def find_class_named name
return self if full_name == name
return self if @name == name
@@ -65,14 +173,8 @@ class RDoc::ClassModule < RDoc::Context
end
end
- ##
- # 'module' or 'class'
-
- def type
- module? ? 'module' : 'class'
- end
-
def marshal_dump # :nodoc:
+ # TODO must store the singleton attribute
attrs = attributes.sort.map do |attr|
[attr.name, attr.rw]
end
@@ -106,10 +208,13 @@ class RDoc::ClassModule < RDoc::Context
end
def marshal_load array # :nodoc:
+ # TODO must restore the singleton attribute
initialize_methods_etc
@document_self = true
@done_documenting = false
@current_section = nil
+ @parent = nil
+ @visibility = nil
@name = array[1]
@full_name = array[2]
@@ -184,6 +289,15 @@ class RDoc::ClassModule < RDoc::Context
end
##
+ # Allows overriding the initial name.
+ #
+ # Used for modules and classes that are constant aliases.
+
+ def name= new_name
+ @name = new_name
+ end
+
+ ##
# Path to this class or module
def path
@@ -191,11 +305,51 @@ class RDoc::ClassModule < RDoc::Context
end
##
+ # Name to use to generate the url:
+ # modules and classes that are aliases for another
+ # module or classe return the name of the latter.
+
+ def name_for_path
+ is_alias_for ? is_alias_for.full_name : full_name
+ end
+
+ ##
+ # Returns the classes and modules that are not constants
+ # aliasing another class or module. For use by formatters
+ # only (caches its result).
+
+ def non_aliases
+ @non_aliases ||= classes_and_modules.reject { |cm| cm.is_alias_for }
+ end
+
+ ##
+ # Updates the child modules or classes of class/module +parent+ by
+ # deleting the ones that have been removed from the documentation.
+ #
+ # +parent_hash+ is either <tt>parent.modules_hash</tt> or
+ # <tt>parent.classes_hash</tt> and +all_hash+ is ::all_modules_hash or
+ # ::all_classes_hash.
+
+ def remove_nodoc_children
+ prefix = self.full_name + '::'
+
+ modules_hash.each_key do |name|
+ full_name = prefix + name
+ modules_hash.delete name unless RDoc::TopLevel.all_modules_hash[full_name]
+ end
+
+ classes_hash.each_key do |name|
+ full_name = prefix + name
+ classes_hash.delete name unless RDoc::TopLevel.all_classes_hash[full_name]
+ end
+ end
+
+ ##
# Get the superclass of this class. Attempts to retrieve the superclass
# object, returns the name if it is not known.
def superclass
- RDoc::TopLevel.find_class_named_from(@superclass, parent) || @superclass
+ RDoc::TopLevel.find_class_named(@superclass) || @superclass
end
##
@@ -203,12 +357,72 @@ class RDoc::ClassModule < RDoc::Context
def superclass=(superclass)
raise NoMethodError, "#{full_name} is a module" if module?
-
- @superclass = superclass if @superclass.nil? or @superclass == 'Object'
+ @superclass = superclass
end
def to_s # :nodoc:
- "#{self.class}: #{full_name} #{@comment} #{super}"
+ if is_alias_for then
+ "#{self.class.name} #{self.full_name} -> #{is_alias_for}"
+ else
+ super
+ end
+ end
+
+ ##
+ # 'module' or 'class'
+
+ def type
+ module? ? 'module' : 'class'
+ end
+
+ ##
+ # Updates the child modules & classes by replacing the ones that are
+ # aliases through a constant.
+ #
+ # The aliased module/class is replaced in the children and in
+ # RDoc::TopLevel::all_modules_hash or RDoc::TopLevel::all_classes_hash
+ # by a copy that has <tt>RDoc::ClassModule#is_alias_for</tt> set to
+ # the aliased module/class, and this copy is added to <tt>#aliases</tt>
+ # of the aliased module/class.
+ #
+ # Formatters can use the #non_aliases method to retrieve children that
+ # are not aliases, for instance to list the namespace content, since
+ # the aliased modules are included in the constants of the class/module,
+ # that are listed separately.
+
+ def update_aliases
+ constants.each do |const|
+ next unless cm = const.is_alias_for
+ cm_alias = cm.dup
+ cm_alias.name = const.name
+ cm_alias.parent = self
+ cm_alias.full_name = nil # force update for new parent
+ cm_alias.aliases.clear
+ cm_alias.is_alias_for = cm
+
+ if cm.module? then
+ RDoc::TopLevel.all_modules_hash[cm_alias.full_name] = cm_alias
+ modules_hash[const.name] = cm_alias
+ else
+ RDoc::TopLevel.all_classes_hash[cm_alias.full_name] = cm_alias
+ classes_hash[const.name] = cm_alias
+ end
+
+ cm.aliases << cm_alias
+ end
+ end
+
+ ##
+ # Deletes from #includes those whose module has been removed from the
+ # documentation.
+ #--
+ # FIXME: includes are not reliably removed, see _possible_bug test case
+
+ def update_includes
+ includes.reject! do |include|
+ mod = include.module
+ !(String === mod) && RDoc::TopLevel.all_modules_hash[mod.full_name].nil?
+ end
end
end
diff --git a/lib/rdoc/code_object.rb b/lib/rdoc/code_object.rb
index f8c4e33f7e..02b2f2fcf6 100644
--- a/lib/rdoc/code_object.rb
+++ b/lib/rdoc/code_object.rb
@@ -12,15 +12,16 @@ require 'rdoc/text'
# * RDoc::Context
# * RDoc::TopLevel
# * RDoc::ClassModule
-# * RDoc::AnonClass
+# * RDoc::AnonClass (never used so far)
# * RDoc::NormalClass
# * RDoc::NormalModule
# * RDoc::SingleClass
-# * RDoc::AnyMethod
-# * RDoc::GhostMethod
-# * RDoc::MetaMethod
+# * RDoc::MethodAttr
+# * RDoc::Attr
+# * RDoc::AnyMethod
+# * RDoc::GhostMethod
+# * RDoc::MetaMethod
# * RDoc::Alias
-# * RDoc::Attr
# * RDoc::Constant
# * RDoc::Require
# * RDoc::Include
@@ -47,12 +48,17 @@ class RDoc::CodeObject
##
# Are we done documenting (ie, did we come across a :enddoc:)?
- attr_accessor :done_documenting
+ attr_reader :done_documenting
+
+ ##
+ # Which file this code object was defined in
+
+ attr_reader :file
##
# Force documentation of this CodeObject
- attr_accessor :force_documentation
+ attr_reader :force_documentation
##
# Hash of arbitrary metadata for this CodeObject
@@ -65,6 +71,11 @@ class RDoc::CodeObject
attr_accessor :parent
##
+ # Did we ever receive a +:nodoc:+ directive?
+
+ attr_reader :received_nodoc
+
+ ##
# Which section are we in
attr_accessor :section
@@ -80,15 +91,17 @@ class RDoc::CodeObject
# Creates a new CodeObject that will document itself and its children
def initialize
- @metadata = {}
- @comment = ''
+ @metadata = {}
+ @comment = ''
+ @parent = nil
+ @file = nil
+ @full_name = nil
@document_children = true
@document_self = true
@done_documenting = false
@force_documentation = false
-
- @parent = nil
+ @received_nodoc = false
end
##
@@ -108,28 +121,64 @@ class RDoc::CodeObject
end
##
- # Enables or disables documentation of this CodeObject's children. Calls
- # remove_classes_and_modules when disabling.
+ # Enables or disables documentation of this CodeObject's children unless it
+ # has been turned off by :enddoc:
def document_children=(document_children)
- @document_children = document_children
- remove_classes_and_modules unless document_children
+ @document_children = document_children unless @done_documenting
end
##
- # Enables or disables documentation of this CodeObject. Calls
- # remove_methods_etc when disabling.
+ # Enables or disables documentation of this CodeObject unless it has been
+ # turned off by :enddoc:. If the argument is +nil+ it means the
+ # documentation is turned off by +:nodoc:+.
def document_self=(document_self)
+ return if @done_documenting
+
@document_self = document_self
- remove_methods_etc unless document_self
+ @received_nodoc = true if document_self.nil?
end
##
- # Does this class have a comment with content or is document_self false.
+ # Does this object have a comment with content or is #received_nodoc true?
def documented?
- !(@document_self and @comment.empty?)
+ @received_nodoc or !@comment.empty?
+ end
+
+ ##
+ # Turns documentation on/off, and turns on/off #document_self
+ # and #document_children.
+ #
+ # Once documentation has been turned off (by +:enddoc:+),
+ # the object will refuse to turn #document_self or
+ # #document_children on, so +:doc:+ and +:start_doc:+ directives
+ # will have no effect in the current file.
+
+ def done_documenting=(value)
+ @done_documenting = value
+ @document_self = !value
+ @document_children = @document_self
+ end
+
+ ##
+ # Force the documentation of this object unless documentation
+ # has been turned off by :endoc:
+ #--
+ # HACK untested, was assigning to an ivar
+
+ def force_documentation=(value)
+ @force_documentation = value unless @done_documenting
+ end
+
+ ##
+ # Sets the full_name overriding any computed full name.
+ #
+ # Set to +nil+ to clear RDoc's cached value
+
+ def full_name= full_name
+ @full_name = full_name
end
##
@@ -147,23 +196,19 @@ class RDoc::CodeObject
end
##
- # Callback called upon disabling documentation of children. See
- # #document_children=
-
- def remove_classes_and_modules
- end
-
- ##
- # Callback called upon disabling documentation of ourself. See
- # #document_self=
+ # Records the RDoc::TopLevel (file) where this code object was defined
- def remove_methods_etc
+ def record_location top_level
+ @file = top_level
end
##
- # Enable capture of documentation
+ # Enable capture of documentation unless documentation has been
+ # turned off by :endoc:
def start_doc
+ return if @done_documenting
+
@document_self = true
@document_children = true
end
diff --git a/lib/rdoc/constant.rb b/lib/rdoc/constant.rb
index 908990855d..056ce130be 100644
--- a/lib/rdoc/constant.rb
+++ b/lib/rdoc/constant.rb
@@ -6,6 +6,13 @@ require 'rdoc/code_object'
class RDoc::Constant < RDoc::CodeObject
##
+ # If this constant is an alias for a module or class,
+ # this is the RDoc::ClassModule it is an alias for.
+ # +nil+ otherwise.
+
+ attr_accessor :is_alias_for
+
+ ##
# The constant's name
attr_accessor :name
@@ -22,6 +29,7 @@ class RDoc::Constant < RDoc::CodeObject
super()
@name = name
@value = value
+ @is_alias_for = nil
self.comment = comment
end
@@ -34,17 +42,28 @@ class RDoc::Constant < RDoc::CodeObject
[parent_name, name] <=> [other.parent_name, other.name]
end
+ ##
+ # Constants are equal when their #parent and #name is the same
+
def == other
self.class == other.class and
@parent == other.parent and
@name == other.name
end
+ ##
+ # A constant is documented if it has a comment, or is an alias
+ # for a documented class or module.
+
+ def documented?
+ super or is_alias_for && is_alias_for.documented?
+ end
+
def inspect # :nodoc:
- "#<%s:0x%x %s::%s>" % [
- self.class, object_id,
- parent_name, @name,
- ]
+ "#<%s:0x%x %s::%s>" % [
+ self.class, object_id,
+ parent_name, @name,
+ ]
end
##
@@ -54,5 +73,14 @@ class RDoc::Constant < RDoc::CodeObject
"#{@parent.path}##{@name}"
end
+ def to_s # :nodoc:
+ parent_name = parent ? parent.full_name : '(unknown)'
+ if is_alias_for
+ "constant #{parent_name}::#@name -> #{is_alias_for}"
+ else
+ "constant #{parent_name}::#@name"
+ end
+ end
+
end
diff --git a/lib/rdoc/context.rb b/lib/rdoc/context.rb
index d55c5a9164..c424ef1676 100644
--- a/lib/rdoc/context.rb
+++ b/lib/rdoc/context.rb
@@ -15,17 +15,12 @@ class RDoc::Context < RDoc::CodeObject
TYPES = %w[class instance]
##
- # Method visibilities
-
- VISIBILITIES = [:public, :protected, :private]
-
- ##
- # Aliased methods
+ # Class/module aliases
attr_reader :aliases
##
- # attr* methods
+ # All attr* methods
attr_reader :attributes
@@ -37,7 +32,7 @@ class RDoc::Context < RDoc::CodeObject
##
# Current section of documentation
- attr_reader :current_section
+ attr_accessor :current_section
##
# Files this context is found in
@@ -70,19 +65,37 @@ class RDoc::Context < RDoc::CodeObject
attr_reader :sections
##
- # Aliases that haven't been resolved to a method
+ # Hash <tt>old_name => [aliases]</tt>, for aliases
+ # that haven't (yet) been resolved to a method/attribute.
+ # (Not to be confused with the aliases of the context.)
attr_accessor :unmatched_alias_lists
##
+ # Aliases that could not eventually be resolved.
+
+ attr_reader :external_aliases
+
+ ##
# Current visibility of this context
- attr_reader :visibility
+ attr_accessor :visibility
+
+ ##
+ # Hash of registered methods. Attributes are also registered here,
+ # twice if they are RW.
+
+ attr_reader :methods_hash
+
+ ##
+ # Hash of registered constants.
+
+ attr_reader :constants_hash
##
# A per-comment section of documentation like:
#
- # # :SECTION: The title
+ # # :section: The title
# # The body
class Section
@@ -137,14 +150,12 @@ class RDoc::Context < RDoc::CodeObject
end
##
- # Set the comment for this section from the original comment block If
+ # 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
#
- # # blah blah blah
- # #
- # # :SECTION: The title
+ # # :section: The title
# # The body
def set_comment(comment)
@@ -169,7 +180,7 @@ class RDoc::Context < RDoc::CodeObject
end
##
- # Creates an unnamed empty context with public visibility
+ # Creates an unnamed empty context with public current visibility
def initialize
super
@@ -184,16 +195,10 @@ class RDoc::Context < RDoc::CodeObject
@current_section = Section.new self, nil, nil
@sections = [@current_section]
- initialize_methods_etc
- initialize_classes_and_modules
- end
-
- ##
- # Sets the defaults for classes and modules
-
- def initialize_classes_and_modules
@classes = {}
@modules = {}
+
+ initialize_methods_etc
end
##
@@ -206,10 +211,14 @@ class RDoc::Context < RDoc::CodeObject
@requires = []
@includes = []
@constants = []
+ @external_aliases = []
# This Hash maps a method name to a list of unmatched aliases (aliases of
# a method not yet encountered).
@unmatched_alias_lists = {}
+
+ @methods_hash = {}
+ @constants_hash = {}
end
##
@@ -222,14 +231,18 @@ class RDoc::Context < RDoc::CodeObject
##
# Adds +an_alias+ that is automatically resolved
- def add_alias(an_alias)
- meth = find_instance_method_named(an_alias.old_name)
+ def add_alias an_alias
+ return an_alias unless @document_self
+
+ method_attr = find_method(an_alias.old_name, an_alias.singleton) ||
+ find_attribute(an_alias.old_name, an_alias.singleton)
- if meth then
- add_alias_impl an_alias, meth
+ if method_attr then
+ method_attr.add_alias an_alias, self
else
- add_to @aliases, an_alias
- unmatched_alias_list = @unmatched_alias_lists[an_alias.old_name] ||= []
+ add_to @external_aliases, an_alias
+ unmatched_alias_list =
+ @unmatched_alias_lists[an_alias.pretty_old_name] ||= []
unmatched_alias_list.push an_alias
end
@@ -237,175 +250,279 @@ class RDoc::Context < RDoc::CodeObject
end
##
- # Turns +an_alias+ into an AnyMethod that points to +meth+
-
- def add_alias_impl(an_alias, meth)
- new_meth = RDoc::AnyMethod.new an_alias.text, an_alias.new_name
- new_meth.is_alias_for = meth
- new_meth.singleton = meth.singleton
- new_meth.params = meth.params
-
- new_meth.comment = an_alias.comment
-
- meth.add_alias new_meth
-
- add_method new_meth
-
- # aliases don't use ongoing visibility
- new_meth.visibility = meth.visibility
-
- new_meth
- end
+ # Adds +attribute+ if not already there. If it is (as method(s) or attribute),
+ # updates the comment if it was empty.
+ #
+ # The attribute is registered only if it defines a new method.
+ # For instance, <tt>attr_reader :foo</tt> will not be registered
+ # if method +foo+ exists, but <tt>attr_accessor :foo</tt> will be registered
+ # if method +foo+ exists, but <tt>foo=</tt> does not.
+
+ def add_attribute attribute
+ return attribute unless @document_self
+
+ # mainly to check for redefinition of an attribute as a method
+ # TODO find a policy for 'attr_reader :foo' + 'def foo=()'
+ register = false
+
+ if attribute.rw.index('R') then
+ key = attribute.pretty_name
+ known = @methods_hash[key]
+ if known then
+ known.comment = attribute.comment if known.comment.empty?
+ else
+ @methods_hash[key] = attribute
+ register = true
+ end
+ end
- ##
- # Adds +attribute+
+ if attribute.rw.index('W')
+ key = attribute.pretty_name << '='
+ known = @methods_hash[key]
+ if known then
+ known.comment = attribute.comment if known.comment.empty?
+ else
+ @methods_hash[key] = attribute
+ register = true
+ end
+ end
- def add_attribute(attribute)
- add_to @attributes, attribute
+ if register then
+ attribute.visibility = @visibility
+ add_to @attributes, attribute
+ resolve_aliases attribute
+ end
end
##
- # Adds a class named +name+ with +superclass+.
+ # Adds a class named +given_name+ with +superclass+.
+ #
+ # Both +given_name+ and +superclass+ may contain '::', and are
+ # interpreted relative to the +self+ context. This allows handling correctly
+ # examples like these:
+ # class RDoc::Gauntlet < Gauntlet
+ # module Mod
+ # class Object # implies < ::Object
+ # class SubObject < Object # this is _not_ ::Object
#
# Given <tt>class Container::Item</tt> RDoc assumes +Container+ is a module
- # unless it later sees <tt>class Container</tt>. add_class automatically
- # upgrades +name+ to a class in this case.
+ # unless it later sees <tt>class Container</tt>. +add_class+ automatically
+ # upgrades +given_name+ to a class in this case.
+
+ def add_class class_type, given_name, superclass = '::Object'
+ # superclass +nil+ is passed by the C parser in the following cases:
+ # - registering Object in 1.8 (correct)
+ # - registering BasicObject in 1.9 (correct)
+ # - registering RubyVM in 1.9 in iseq.c (incorrect: < Object in vm.c)
+ #
+ # If we later find a superclass for a registered class with a nil
+ # superclass, we must honor it.
+
+ # find the name & enclosing context
+ if given_name =~ /^:+(\w+)$/ then
+ full_name = $1
+ enclosing = top_level
+ name = full_name.split(/:+/).last
+ else
+ full_name = child_name given_name
+
+ if full_name =~ /^(.+)::(\w+)$/ then
+ name = $2
+ ename = $1
+ enclosing = RDoc::TopLevel.classes_hash[ename] ||
+ RDoc::TopLevel.modules_hash[ename]
+ # HACK: crashes in actionpack/lib/action_view/helpers/form_helper.rb (metaprogramming)
+ unless enclosing then
+ # try the given name at top level (will work for the above example)
+ enclosing = RDoc::TopLevel.classes_hash[given_name] || RDoc::TopLevel.modules_hash[given_name]
+ return enclosing if enclosing
+ # not found: create the parent(s)
+ names = ename.split('::')
+ enclosing = self
+ names.each do |n|
+ enclosing = enclosing.classes_hash[n] ||
+ enclosing.modules_hash[n] ||
+ enclosing.add_module(RDoc::NormalModule, n)
+ end
+ end
+ else
+ name = full_name
+ enclosing = self
+ end
+ end
+
+ # find the superclass full name
+ if superclass then
+ if superclass =~ /^:+/ then
+ superclass = $' #'
+ else
+ if superclass =~ /^(\w+):+(.+)$/ then
+ suffix = $2
+ mod = find_module_named($1)
+ superclass = mod.full_name + '::' + suffix if mod
+ else
+ mod = find_module_named(superclass)
+ superclass = mod.full_name if mod
+ end
+ end
- def add_class(class_type, name, superclass = 'Object')
- klass = add_class_or_module @classes, class_type, name, superclass
+ # did we believe it was a module?
+ mod = RDoc::TopLevel.modules_hash.delete superclass
- existing = klass.superclass
- existing = existing.name if existing and not String === existing
+ upgrade_to_class mod, RDoc::NormalClass, mod.parent if mod
- if superclass != existing and superclass != 'Object' then
- klass.superclass = superclass
+ # e.g., Object < Object
+ superclass = nil if superclass == full_name
end
- # If the parser encounters Container::Item before encountering
- # Container, then it assumes that Container is a module. This may not
- # be the case, so remove Container from the module list if present and
- # transfer any contained classes and modules to the new class.
+ klass = RDoc::TopLevel.classes_hash[full_name]
+
+ if klass then
+ # if TopLevel, it may not be registered in the classes:
+ enclosing.classes_hash[name] = klass
+ # update the superclass if needed
+ if superclass then
+ existing = klass.superclass
+ existing = existing.full_name unless existing.is_a?(String) if existing
+ if existing.nil? ||
+ (existing == 'Object' && superclass != 'Object') then
+ klass.superclass = superclass
+ end
+ end
+ else
+ # this is a new class
+ mod = RDoc::TopLevel.modules_hash.delete full_name
- mod = RDoc::TopLevel.modules_hash.delete klass.full_name
+ if mod then
+ klass = upgrade_to_class mod, RDoc::NormalClass, enclosing
- if mod then
- klass.classes_hash.update mod.classes_hash
- klass.modules_hash.update mod.modules_hash
- klass.method_list.concat mod.method_list
+ klass.superclass = superclass unless superclass.nil?
+ else
+ klass = class_type.new name, superclass
- @modules.delete klass.name
+ enclosing.add_class_or_module(klass, enclosing.classes_hash,
+ RDoc::TopLevel.classes_hash)
+ end
end
- RDoc::TopLevel.classes_hash[klass.full_name] = klass
-
klass
end
##
- # Instantiates a +class_type+ named +name+ and adds it the modules or
- # classes Hash +collection+.
-
- def add_class_or_module(collection, class_type, name, superclass = nil)
- full_name = child_name name
-
- mod = collection[name]
-
- if mod then
- mod.superclass = superclass unless mod.module?
- else
- all = if class_type == RDoc::NormalModule then
- RDoc::TopLevel.modules_hash
- else
- RDoc::TopLevel.classes_hash
- end
-
- mod = all[full_name]
-
- unless mod then
- mod = class_type.new name, superclass
- else
- # If the class has been encountered already, check that its
- # superclass has been set (it may not have been, depending on the
- # context in which it was encountered).
- if class_type == RDoc::NormalClass then
- mod.superclass = superclass unless mod.superclass
- end
- end
+ # Adds the class or module +mod+ to the modules or
+ # classes Hash +self_hash+, and to +all_hash+ (either
+ # <tt>TopLevel::modules_hash</tt> or <tt>TopLevel::classes_hash</tt>),
+ # unless #done_documenting is +true+. Sets the #parent of +mod+
+ # to +self+, and its #section to #current_section. Returns +mod+.
- unless @done_documenting then
- all[full_name] = mod
- collection[name] = mod
- end
+ def add_class_or_module mod, self_hash, all_hash
+ mod.section = @current_section # TODO declaring context? something is
+ # wrong here...
+ mod.parent = self
- mod.section = @current_section
- mod.parent = self
+ unless @done_documenting then
+ self_hash[mod.name] = mod
+ # this must be done AFTER adding mod to its parent, so that the full
+ # name is correct:
+ all_hash[mod.full_name] = mod
end
mod
end
##
- # Adds +constant+
+ # Adds +constant+ if not already there. If it is, updates the comment,
+ # value and/or is_alias_for of the known constant if they were empty/nil.
def add_constant(constant)
- add_to @constants, constant
+ return constant unless @document_self
+
+ # HACK: avoid duplicate 'PI' & 'E' in math.c (1.8.7 source code)
+ # (this is a #ifdef: should be handled by the C parser)
+ known = @constants_hash[constant.name]
+ if known
+ #$stderr.puts "\nconstant #{constant.name} already registered"
+ known.comment = constant.comment if known.comment.empty?
+ known.value = constant.value if known.value.nil? or known.value.strip.empty?
+ known.is_alias_for ||= constant.is_alias_for
+ else
+ @constants_hash[constant.name] = constant
+ add_to @constants, constant
+ end
end
##
- # Adds included module +include+
+ # Adds included module +include+ which should be an RDoc::Include
def add_include(include)
add_to @includes, include
end
##
- # Adds +method+
+ # Adds +method+ if not already there. If it is (as method or attribute),
+ # updates the comment if it was empty.
def add_method(method)
- method.visibility = @visibility
- add_to @method_list, method
-
- unmatched_alias_list = @unmatched_alias_lists[method.name]
- if unmatched_alias_list then
- unmatched_alias_list.each do |unmatched_alias|
- add_alias_impl unmatched_alias, method
- @aliases.delete unmatched_alias
- end
-
- @unmatched_alias_lists.delete method.name
+ return method unless @document_self
+
+ # HACK: avoid duplicate 'new' in io.c & struct.c (1.8.7 source code)
+ key = method.pretty_name
+ known = @methods_hash[key]
+ if known
+ # TODO issue stderr messages if --verbose
+ #$stderr.puts "\n#{display(method)} already registered as #{display(known)}"
+ known.comment = method.comment if known.comment.empty?
+ else
+ @methods_hash[key] = method
+ method.visibility = @visibility
+ add_to @method_list, method
+ resolve_aliases method
end
end
##
# Adds a module named +name+. If RDoc already knows +name+ is a class then
- # that class is returned instead. See also #add_class
+ # that class is returned instead. See also #add_class.
def add_module(class_type, name)
- return @classes[name] if @classes.key? name
+ mod = @classes[name] || @modules[name]
+ return mod if mod
+
+ full_name = child_name name
+ mod = RDoc::TopLevel.modules_hash[full_name] || class_type.new(name)
- add_class_or_module @modules, class_type, name, nil
+ add_class_or_module(mod, @modules, RDoc::TopLevel.modules_hash)
end
##
- # Adds an alias from +from+ to +name+
+ # Adds an alias from +from+ (a class or module) to +name+.
def add_module_alias from, name
- to_name = child_name name
+ return from if @done_documenting
- unless @done_documenting then
- if from.module? then
- RDoc::TopLevel.modules_hash
- else
- RDoc::TopLevel.classes_hash
- end[to_name] = from
+ to_name = child_name(name)
- if from.module? then
- @modules
- else
- @classes
- end[name] = from
+ # if we already know this name, don't register an alias:
+ # see the metaprogramming in lib/active_support/basic_object.rb,
+ # where we already know BasicObject as a class when we find
+ # BasicObject = BlankSlate
+ return from if RDoc::TopLevel.find_class_or_module(to_name)
+
+ if from.module? then
+ RDoc::TopLevel.modules_hash[to_name] = from
+ @modules[name] = from
+ else
+ RDoc::TopLevel.classes_hash[to_name] = from
+ @classes[name] = from
end
+ # HACK: register a constant for this alias:
+ # constant value and comment will be updated after,
+ # when the Ruby parser adds the constant
+ const = RDoc::Constant.new(name, nil, '')
+ const.is_alias_for = from
+ add_constant const
+
from
end
@@ -413,6 +530,8 @@ class RDoc::Context < RDoc::CodeObject
# Adds +require+ to this context's top level
def add_require(require)
+ return require unless @document_self
+
if RDoc::TopLevel === self then
add_to @requires, require
else
@@ -424,16 +543,37 @@ class RDoc::Context < RDoc::CodeObject
# Adds +thing+ to the collection +array+
def add_to(array, thing)
- array << thing if @document_self and not @done_documenting
+ array << thing if @document_self
thing.parent = self
thing.section = @current_section
end
##
+ # Is there any content?
+ # This means any of: comment, aliases, methods, attributes,
+ # external aliases, require, constant.
+ # Includes are also checked unless <tt>includes == false</tt>.
+
+ def any_content(includes = true)
+ @any_content ||= !(
+ @comment.empty? &&
+ @method_list.empty? &&
+ @attributes.empty? &&
+ @aliases.empty? &&
+ @external_aliases.empty? &&
+ @requires.empty? &&
+ @constants.empty?
+ )
+ @any_content || (includes && !@includes.empty?)
+ end
+
+ ##
# Creates the full name for a child with +name+
def child_name name
- if RDoc::TopLevel === self then
+ if name =~ /^:+/
+ $' #'
+ elsif RDoc::TopLevel === self then
name
else
"#{self.full_name}::#{name}"
@@ -441,6 +581,20 @@ class RDoc::Context < RDoc::CodeObject
end
##
+ # Class attributes
+
+ def class_attributes
+ @class_attributes ||= attributes.select { |a| a.singleton }
+ end
+
+ ##
+ # Class methods
+
+ def class_method_list
+ @class_method_list ||= method_list.select { |a| a.singleton }
+ end
+
+ ##
# Array of classes in this context
def classes
@@ -468,6 +622,14 @@ class RDoc::Context < RDoc::CodeObject
@in_files.include?(file)
end
+ def display(method_attr) # :nodoc:
+ if method_attr.is_a? RDoc::Attr
+ "#{method_attr.definition} #{method_attr.pretty_name}"
+ else
+ "method #{method_attr.pretty_name}"
+ end
+ end
+
##
# Iterator for attributes
@@ -504,10 +666,25 @@ class RDoc::Context < RDoc::CodeObject
end
##
+ # Finds an attribute +name+ with singleton value +singleton+.
+
+ def find_attribute(name, singleton)
+ name = $1 if name =~ /^(.*)=$/
+ @attributes.find { |a| a.name == name && a.singleton == singleton }
+ end
+
+ ##
# Finds an attribute with +name+ in this context
def find_attribute_named(name)
- @attributes.find { |m| m.name == name }
+ case name
+ when /\A#/ then
+ find_attribute name[1..-1], false
+ when /\A::/ then
+ find_attribute name[2..-1], true
+ else
+ @attributes.find { |a| a.name == name }
+ end
end
##
@@ -532,6 +709,27 @@ class RDoc::Context < RDoc::CodeObject
end
##
+ # Finds an external alias +name+ with singleton value +singleton+.
+
+ def find_external_alias(name, singleton)
+ @external_aliases.find { |m| m.name == name && m.singleton == singleton }
+ end
+
+ ##
+ # Finds an external alias with +name+ in this context
+
+ def find_external_alias_named(name)
+ case name
+ when /\A#/ then
+ find_external_alias name[1..-1], false
+ when /\A::/ then
+ find_external_alias name[2..-1], true
+ else
+ @external_aliases.find { |a| a.name == name }
+ end
+ end
+
+ ##
# Finds a file with +name+ in this context
def find_file_named(name)
@@ -546,26 +744,34 @@ class RDoc::Context < RDoc::CodeObject
end
##
- # Finds a method, constant, attribute, module or files named +symbol+ in
- # this context
+ # Finds a method, constant, attribute, external alias, module or file
+ # named +symbol+ in this context.
def find_local_symbol(symbol)
find_method_named(symbol) or
find_constant_named(symbol) or
find_attribute_named(symbol) or
+ find_external_alias_named(symbol) or
find_module_named(symbol) or
find_file_named(symbol)
end
##
+ # Finds a method named +name+ with singleton value +singleton+.
+
+ def find_method(name, singleton)
+ @method_list.find { |m| m.name == name && m.singleton == singleton }
+ end
+
+ ##
# Finds a instance or module method with +name+ in this context
def find_method_named(name)
case name
when /\A#/ then
- find_instance_method_named name[1..-1]
+ find_method name[1..-1], false
when /\A::/ then
- find_class_method_named name[2..-1]
+ find_method name[2..-1], true
else
@method_list.find { |meth| meth.name == name }
end
@@ -582,51 +788,42 @@ class RDoc::Context < RDoc::CodeObject
end
##
- # Look up +symbol+. If +method+ is non-nil, then we assume the symbol
- # references a module that contains that method.
+ # Look up +symbol+, first as a module, then as a local symbol.
+
+ def find_symbol(symbol)
+ find_symbol_module(symbol) || find_local_symbol(symbol)
+ end
+
+ ##
+ # Look up a module named +symbol+.
- def find_symbol(symbol, method = nil)
+ def find_symbol_module(symbol)
result = nil
+ # look for a class or module 'symbol'
case symbol
- when /^::([A-Z].*)/ then
- result = top_level.find_symbol($1)
- when /::/ then
- modules = symbol.split(/::/)
-
- unless modules.empty? then
- module_name = modules.shift
- result = find_module_named(module_name)
-
- if result then
- modules.each do |name|
- result = result.find_module_named name
- break unless result
- end
- end
+ when /^::/ then
+ result = RDoc::TopLevel.find_class_or_module(symbol)
+ when /^(\w+):+(.+)$/
+ suffix = $2
+ top = $1
+ searched = self
+ loop do
+ mod = searched.find_module_named(top)
+ break unless mod
+ result = RDoc::TopLevel.find_class_or_module(mod.full_name + '::' + suffix)
+ break if result || searched.is_a?(RDoc::TopLevel)
+ searched = searched.parent
end
- end
-
- unless result then
- # if a method is specified, then we're definitely looking for
- # a module, otherwise it could be any symbol
- if method then
- result = find_module_named symbol
- else
- result = find_local_symbol symbol
- if result.nil? then
- if symbol =~ /^[A-Z]/ then
- result = parent
- while result && result.name != symbol do
- result = result.parent
- end
- end
- end
+ else
+ searched = self
+ loop do
+ result = searched.find_module_named(symbol)
+ break if result || searched.is_a?(RDoc::TopLevel)
+ searched = searched.parent
end
end
- result = result.find_local_symbol method if result and method
-
result
end
@@ -638,10 +835,22 @@ class RDoc::Context < RDoc::CodeObject
end
##
+ # Does this context and its methods and constants all have documentation?
+ #
+ # (Yes, fully documented doesn't mean everything.)
+
+ def fully_documented?
+ documented? and
+ attributes.all? { |a| a.documented? } and
+ method_list.all? { |m| m.documented? } and
+ constants.all? { |c| c.documented? }
+ end
+
+ ##
# URL for this with a +prefix+
def http_url(prefix)
- path = full_name
+ path = name_for_path
path = path.gsub(/<<\s*(\w*)/, 'from-\1') if path =~ /<</
path = [prefix] + path.split('::')
@@ -649,15 +858,29 @@ class RDoc::Context < RDoc::CodeObject
end
##
+ # Instance attributes
+
+ def instance_attributes
+ @instance_attributes ||= attributes.reject { |a| a.singleton }
+ end
+
+ ##
+ # Instance methods
+
+ def instance_method_list
+ @instance_method_list ||= method_list.reject { |a| a.singleton }
+ end
+
+ ##
# Breaks method_list into a nested hash by type (class or instance) and
- # visibility (public, protected private)
+ # visibility (public, protected, private)
def methods_by_type
methods = {}
TYPES.each do |type|
visibilities = {}
- VISIBILITIES.each do |vis|
+ RDoc::VISIBILITIES.each do |vis|
visibilities[vis] = []
end
@@ -672,23 +895,11 @@ class RDoc::Context < RDoc::CodeObject
end
##
- # Yields Method and Attr entries matching the list of names in +methods+.
- # Attributes are only returned when +singleton+ is false.
+ # Yields AnyMethod and Attr entries matching the list of names in +methods+.
def methods_matching(methods, singleton = false)
- count = 0
-
- @method_list.each do |m|
- if methods.include? m.name and m.singleton == singleton then
- yield m
- count += 1
- end
- end
-
- return if count == methods.size || singleton
-
- @attributes.each do |a|
- yield a if methods.include? a.name
+ (@method_list + @attributes).each do |m|
+ yield m if methods.include?(m.name) and m.singleton == singleton
end
end
@@ -707,6 +918,14 @@ class RDoc::Context < RDoc::CodeObject
end
##
+ # Name to use to generate the url.
+ # <tt>#full_name</tt> by default.
+
+ def name_for_path
+ full_name
+ end
+
+ ##
# Changes the visibility for new methods to +visibility+
def ongoing_visibility=(visibility)
@@ -714,35 +933,63 @@ class RDoc::Context < RDoc::CodeObject
end
##
- # Record which file +top_level+ is in
+ # Record +top_level+ as a file +self+ is in.
def record_location(top_level)
@in_files << top_level unless @in_files.include?(top_level)
end
##
- # If a class's documentation is turned off after we've started collecting
- # methods etc., we need to remove the ones we have
-
- def remove_methods_etc
- initialize_methods_etc
+ # Should we remove this context from the documentation?
+ #
+ # The answer is yes if:
+ # * #received_nodoc is +true+
+ # * #any_content is +false+ (not counting includes)
+ # * All #includes are modules (not a string), and their module has
+ # <tt>#remove_from_documentation? == true</tt>
+ # * All classes and modules have <tt>#remove_from_documentation? == true</tt>
+
+ def remove_from_documentation?
+ @remove_from_documentation ||=
+ @received_nodoc &&
+ !any_content(false) &&
+ @includes.all? { |i| !i.module.is_a?(String) && i.module.remove_from_documentation? } &&
+ classes_and_modules.all? { |cm| cm.remove_from_documentation? }
end
##
- # Given an array +methods+ of method names, set the visibility of each to
- # +visibility+
+ # Removes methods and attributes with a visibility less than +min_visibility+.
+ #--
+ # TODO mark the visibility of attributes in the template (if not public?)
- def set_visibility_for(methods, visibility, singleton = false)
- methods_matching methods, singleton do |m|
- m.visibility = visibility
+ def remove_invisible(min_visibility)
+ return if min_visibility == :private
+ remove_invisible_in @method_list, min_visibility
+ remove_invisible_in @attributes, min_visibility
+ end
+
+ def remove_invisible_in(array, min_visibility) # :nodoc:
+ if min_visibility == :public
+ array.reject! { |e| e.visibility != :public }
+ else
+ array.reject! { |e| e.visibility == :private }
end
end
##
- # Removes classes and modules when we see a :nodoc: all
+ # Tries to resolve unmatched aliases when a method
+ # or attribute has just been added.
- def remove_classes_and_modules
- initialize_classes_and_modules
+ def resolve_aliases(added)
+ # resolve any pending unmatched aliases
+ key = added.pretty_name
+ unmatched_alias_list = @unmatched_alias_lists[key]
+ return unless unmatched_alias_list
+ unmatched_alias_list.each do |unmatched_alias|
+ added.add_alias unmatched_alias, self
+ @external_aliases.delete unmatched_alias
+ end
+ @unmatched_alias_lists.delete key
end
##
@@ -754,7 +1001,24 @@ class RDoc::Context < RDoc::CodeObject
end
##
+ # Given an array +methods+ of method names, set the visibility of each to
+ # +visibility+
+
+ def set_visibility_for(methods, visibility, singleton = false)
+ methods_matching methods, singleton do |m|
+ m.visibility = visibility
+ end
+ end
+
+ def to_s # :nodoc:
+ "#{self.class.name} #{self.full_name}"
+ end
+
+ ##
# Return the TopLevel that owns us
+ #--
+ # FIXME we can be 'owned' by several TopLevel (see #record_location &
+ # #in_files)
def top_level
return @top_level if defined? @top_level
@@ -763,5 +1027,20 @@ class RDoc::Context < RDoc::CodeObject
@top_level
end
+ ##
+ # Upgrades NormalModule +mod+ in +enclosing+ to a +class_type+
+
+ def upgrade_to_class mod, class_type, enclosing
+ enclosing.modules_hash.delete mod.name
+
+ klass = RDoc::ClassModule.from_module class_type, mod
+
+ # if it was there, then we keep it even if done_documenting
+ RDoc::TopLevel.classes_hash[mod.full_name] = klass
+ enclosing.classes_hash[mod.name] = klass
+
+ klass
+ end
+
end
diff --git a/lib/rdoc/encoding.rb b/lib/rdoc/encoding.rb
new file mode 100644
index 0000000000..4f0779881c
--- /dev/null
+++ b/lib/rdoc/encoding.rb
@@ -0,0 +1,79 @@
+require 'rdoc'
+
+##
+# This class is a wrapper around File IO and Encoding that helps RDoc load
+# files and convert them to the correct encoding.
+
+module RDoc::Encoding
+
+ ##
+ # Reads the contents of +filename+ and handles any encoding directives in
+ # the file.
+ #
+ # The content will be converted to the +encoding+. If the file cannot be
+ # converted a warning will be printed and nil will be returned.
+
+ def self.read_file filename, encoding
+ content = open filename, "rb" do |f| f.read end
+
+ utf8 = content.sub!(/\A\xef\xbb\xbf/, '')
+
+ RDoc::Encoding.set_encoding content
+
+ if Object.const_defined? :Encoding then
+ encoding ||= Encoding.default_external
+ orig_encoding = content.encoding
+
+ if utf8 then
+ content.force_encoding Encoding::UTF_8
+ content.encode! encoding
+ else
+ # assume the content is in our output encoding
+ content.force_encoding encoding
+ end
+
+ unless content.valid_encoding? then
+ # revert and try to transcode
+ content.force_encoding orig_encoding
+ content.encode! encoding
+ end
+
+ unless content.valid_encoding? then
+ warn "unable to convert #{filename} to #{encoding}, skipping"
+ content = nil
+ end
+ end
+
+ content
+ rescue ArgumentError => e
+ raise unless e.message =~ /unknown encoding name - (.*)/
+ warn "unknown encoding name \"#{$1}\" for #{filename}, skipping"
+ nil
+ rescue Encoding::UndefinedConversionError => e
+ warn "unable to convert #{e.message} for #{filename}, skipping"
+ nil
+ rescue Errno::EISDIR, Errno::ENOENT
+ nil
+ end
+
+ ##
+ # Sets the encoding of +string+ based on the magic comment
+
+ def self.set_encoding string
+ return unless Object.const_defined? :Encoding
+
+ first_line = string[/\A(?:#!.*\n)?.*\n/]
+
+ name = case first_line
+ when /^<\?xml[^?]*encoding=(["'])(.*?)\1/ then $2
+ when /\b(?:en)?coding[=:]\s*([^\s;]+)/i then $1
+ else return
+ end
+
+ enc = Encoding.find name
+ string.force_encoding enc if enc
+ end
+
+end
+
+
diff --git a/lib/rdoc/erbio.rb b/lib/rdoc/erbio.rb
new file mode 100644
index 0000000000..04a89fbd34
--- /dev/null
+++ b/lib/rdoc/erbio.rb
@@ -0,0 +1,37 @@
+require 'erb'
+
+##
+# A subclass of ERB that writes directly to an IO. Credit to Aaron Patterson
+# and Masatoshi SEKI.
+#
+# To use:
+#
+# erbio = RDoc::ERBIO.new '<%= "hello world" %>', nil, nil
+#
+# open 'hello.txt', 'w' do |io|
+# erbio.result binding
+# end
+#
+# Note that binding must enclose the io you wish to output on.
+
+class RDoc::ERBIO < ERB
+
+ ##
+ # Defaults +eoutvar+ to 'io', otherwise is identical to ERB's initialize
+
+ def initialize str, safe_level = nil, trim_mode = nil, eoutvar = 'io'
+ super
+ end
+
+ ##
+ # Instructs +compiler+ how to write to +io_variable+
+
+ def set_eoutvar compiler, io_variable
+ compiler.put_cmd = "#{io_variable}.write"
+ compiler.insert_cmd = "#{io_variable}.write"
+ compiler.pre_cmd = []
+ compiler.post_cmd = []
+ end
+
+end
+
diff --git a/lib/rdoc/generator.rb b/lib/rdoc/generator.rb
index b65002977a..d02a7538c0 100644
--- a/lib/rdoc/generator.rb
+++ b/lib/rdoc/generator.rb
@@ -1,7 +1,39 @@
require 'rdoc'
##
-# Namespace for generators
+# RDoc uses generators to turn parsed source code in the form of an
+# RDoc::CodeObject tree into some form of output. RDoc comes with the HTML
+# generator RDoc::Generator::Darkfish and an ri data generator
+# RDoc::Generator::RI.
+#
+# = Registering a Generator
+#
+# Generators are registered by calling RDoc::RDoc.add_generator with the class
+# of the generator:
+#
+# class My::Awesome::Generator
+# RDoc::RDoc.add_generator self
+# end
+#
+# = Adding Options to +rdoc+
+#
+# Before option processing in +rdoc+, RDoc::Options will call ::setup_options
+# on the generator class with an RDoc::Options instance. The generator can
+# use RDoc::Options#option_parser to add command-line options to the +rdoc+
+# tool. See OptionParser for details on how to add options.
+#
+# You can extend the RDoc::Options instance with additional accesors for your
+# generator.
+#
+# = Generator Instantiation
+#
+# After parsing, RDoc::RDoc will instantiate a generator by calling
+# #initialize with an RDoc::Options instance.
+#
+# RDoc will then call #generate on the generator instance and pass in an Array
+# of RDoc::TopLevel instances, each representing a parsed file. You can use
+# the various class methods on RDoc::TopLevel and in the RDoc::CodeObject tree
+# to create your desired output format.
module RDoc::Generator
end
diff --git a/lib/rdoc/generator/darkfish.rb b/lib/rdoc/generator/darkfish.rb
index f64641873e..e5a6e57424 100644
--- a/lib/rdoc/generator/darkfish.rb
+++ b/lib/rdoc/generator/darkfish.rb
@@ -1,15 +1,12 @@
# -*- mode: ruby; ruby-indent-level: 2; tab-width: 2 -*-
-# vim: noet ts=2 sts=8 sw=2
require 'pathname'
require 'fileutils'
-require 'erb'
+require 'rdoc/erbio'
require 'rdoc/generator/markup'
-$DARKFISH_DRYRUN = false # TODO make me non-global
-
-#
+##
# Darkfish RDoc HTML Generator
#
# $Id: darkfish.rb 52 2009-01-07 02:08:11Z deveiant $
@@ -52,401 +49,314 @@ $DARKFISH_DRYRUN = false # TODO make me non-global
#
class RDoc::Generator::Darkfish
- RDoc::RDoc.add_generator( self )
+ RDoc::RDoc.add_generator self
+
+ include ERB::Util
+
+ # Path to this file's parent directory. Used to find templates and other
+ # resources.
+
+ GENERATOR_DIR = File.join 'rdoc', 'generator'
+
+ ##
+ # Release Version
+
+ VERSION = '2'
+
+ ##
+ # Initialize a few instance variables before we start
- include ERB::Util
+ def initialize options
+ @options = options
- # Subversion rev
- SVNRev = %$Rev: 52 $
+ @template_dir = Pathname.new options.template_dir
+ @template_cache = {}
- # Subversion ID
- SVNId = %$Id: darkfish.rb 52 2009-01-07 02:08:11Z deveiant $
+ @files = nil
+ @classes = nil
- # Path to this file's parent directory. Used to find templates and other
- # resources.
- GENERATOR_DIR = File.join 'rdoc', 'generator'
+ @basedir = Pathname.pwd.expand_path
+ end
- # Release Version
- VERSION = '1.1.6'
+ ##
+ # The output directory
- # Directory where generated classes live relative to the root
- CLASS_DIR = nil
+ attr_reader :outputdir
- # Directory where generated files live relative to the root
- FILE_DIR = nil
+ ##
+ # Output progress information if debugging is enabled
+ def debug_msg *msg
+ return unless $DEBUG_RDOC
+ $stderr.puts(*msg)
+ end
- #################################################################
- ### C L A S S M E T H O D S
- #################################################################
+ ##
+ # Directory where generated class HTML files live relative to the output
+ # dir.
- ### Standard generator factory method
- def self::for( options )
- new( options )
- end
+ def class_dir
+ nil
+ end
+ ##
+ # Directory where generated class HTML files live relative to the output
+ # dir.
- #################################################################
- ### I N S T A N C E M E T H O D S
- #################################################################
+ def file_dir
+ nil
+ end
- ### Initialize a few instance variables before we start
- def initialize( options )
- @options = options
+ ##
+ # Create the directories the generated docs will live in if they don't
+ # already exist.
- template = @options.template || 'darkfish'
+ def gen_sub_directories
+ @outputdir.mkpath
+ end
- template_dir = $LOAD_PATH.map do |path|
- File.join File.expand_path(path), GENERATOR_DIR, 'template', template
- end.find do |dir|
- File.directory? dir
- end
+ ##
+ # Copy over the stylesheet into the appropriate place in the output
+ # directory.
- raise RDoc::Error, "could not find template #{template.inspect}" unless
- template_dir
+ def write_style_sheet
+ debug_msg "Copying static files"
+ options = { :verbose => $DEBUG_RDOC, :noop => @options.dry_run }
- @template_dir = Pathname.new File.expand_path(template_dir)
+ FileUtils.cp @template_dir + 'rdoc.css', '.', options
- @files = nil
- @classes = nil
+ Dir[(@template_dir + "{js,images}/**/*").to_s].each do |path|
+ next if File.directory? path
+ next if File.basename(path) =~ /^\./
- @basedir = Pathname.pwd.expand_path
- end
+ dst = Pathname.new(path).relative_path_from @template_dir
- ######
- public
- ######
+ # I suck at glob
+ dst_dir = dst.dirname
+ FileUtils.mkdir_p dst_dir, options unless File.exist? dst_dir
- # The output directory
- attr_reader :outputdir
+ FileUtils.cp @template_dir + path, dst, options
+ end
+ end
+ ##
+ # Build the initial indices and output objects based on an array of TopLevel
+ # objects containing the extracted information.
- ### Output progress information if debugging is enabled
- def debug_msg( *msg )
- return unless $DEBUG_RDOC
- $stderr.puts( *msg )
- end
+ def generate top_levels
+ @outputdir = Pathname.new(@options.op_dir).expand_path(@basedir)
- def class_dir
- CLASS_DIR
- end
+ @files = top_levels.sort
+ @classes = RDoc::TopLevel.all_classes_and_modules.sort
+ @methods = @classes.map { |m| m.method_list }.flatten.sort
+ @modsort = get_sorted_module_list(@classes)
- def file_dir
- FILE_DIR
- end
+ # Now actually write the output
+ write_style_sheet
+ generate_index
+ generate_class_files
+ generate_file_files
- ### Create the directories the generated docs will live in if
- ### they don't already exist.
- def gen_sub_directories
- @outputdir.mkpath
- end
+ rescue StandardError => err
+ debug_msg "%s: %s\n %s" % [
+ err.class.name, err.message, err.backtrace.join("\n ")
+ ]
+
+ raise
+ end
+
+ protected
+
+ ##
+ # Return a list of the documented modules sorted by salience first, then
+ # by name.
+
+ def get_sorted_module_list(classes)
+ nscounts = classes.inject({}) do |counthash, klass|
+ top_level = klass.full_name.gsub(/::.*/, '')
+ counthash[top_level] ||= 0
+ counthash[top_level] += 1
+
+ counthash
+ end
+
+ # Sort based on how often the top level namespace occurs, and then on the
+ # name of the module -- this works for projects that put their stuff into
+ # a namespace, of course, but doesn't hurt if they don't.
+ classes.sort_by do |klass|
+ top_level = klass.full_name.gsub( /::.*/, '' )
+ [nscounts[top_level] * -1, klass.full_name]
+ end.select do |klass|
+ klass.document_self
+ end
+ end
+
+ ##
+ # Generate an index page which lists all the classes which are documented.
+
+ def generate_index
+ template_file = @template_dir + 'index.rhtml'
+ return unless template_file.exist?
+
+ debug_msg "Rendering the index page..."
+
+ out_file = @basedir + @options.op_dir + 'index.html'
+
+ render_template template_file, out_file do |io| binding end
+ end
+
+ ##
+ # Generate a documentation file for each class
+
+ def generate_class_files
+ template_file = @template_dir + 'classpage.rhtml'
+ return unless template_file.exist?
+ debug_msg "Generating class documentation in #@outputdir"
+
+ @classes.each do |klass|
+ debug_msg " working on %s (%s)" % [klass.full_name, klass.path]
+ out_file = @outputdir + klass.path
+ # suppress 1.9.3 warning
+ rel_prefix = rel_prefix = @outputdir.relative_path_from(out_file.dirname)
+ svninfo = svninfo = self.get_svninfo(klass)
+
+ debug_msg " rendering #{out_file}"
+ render_template template_file, out_file do |io| binding end
+ end
+ end
+
+ ##
+ # Generate a documentation file for each file
+
+ def generate_file_files
+ template_file = @template_dir + 'filepage.rhtml'
+ return unless template_file.exist?
+ debug_msg "Generating file documentation in #@outputdir"
+
+ @files.each do |file|
+ out_file = @outputdir + file.path
+ debug_msg " working on %s (%s)" % [ file.full_name, out_file ]
+ # suppress 1.9.3 warning
+ rel_prefix = rel_prefix = @outputdir.relative_path_from(out_file.dirname)
+
+ debug_msg " rendering #{out_file}"
+ render_template template_file, out_file do |io| binding end
+ end
+ end
+
+ ##
+ # Return a string describing the amount of time in the given number of
+ # seconds in terms a human can understand easily.
+
+ def time_delta_string seconds
+ return 'less than a minute' if seconds < 60
+ return "#{seconds / 60} minute#{seconds / 60 == 1 ? '' : 's'}" if
+ seconds < 3000 # 50 minutes
+ return 'about one hour' if seconds < 5400 # 90 minutes
+ return "#{seconds / 3600} hours" if seconds < 64800 # 18 hours
+ return 'one day' if seconds < 86400 # 1 day
+ return 'about one day' if seconds < 172800 # 2 days
+ return "#{seconds / 86400} days" if seconds < 604800 # 1 week
+ return 'about one week' if seconds < 1209600 # 2 week
+ return "#{seconds / 604800} weeks" if seconds < 7257600 # 3 months
+ return "#{seconds / 2419200} months" if seconds < 31536000 # 1 year
+ return "#{seconds / 31536000} years"
+ end
+
+ # %q$Id: darkfish.rb 52 2009-01-07 02:08:11Z deveiant $"
+ SVNID_PATTERN = /
+ \$Id:\s
+ (\S+)\s # filename
+ (\d+)\s # rev
+ (\d{4}-\d{2}-\d{2})\s # Date (YYYY-MM-DD)
+ (\d{2}:\d{2}:\d{2}Z)\s # Time (HH:MM:SSZ)
+ (\w+)\s # committer
+ \$$
+ /x
+
+ ##
+ # Try to extract Subversion information out of the first constant whose
+ # value looks like a subversion Id tag. If no matching constant is found,
+ # and empty hash is returned.
+
+ def get_svninfo klass
+ constants = klass.constants or return {}
+
+ constants.find { |c| c.value =~ SVNID_PATTERN } or return {}
+
+ filename, rev, date, time, committer = $~.captures
+ commitdate = Time.parse "#{date} #{time}"
+
+ return {
+ :filename => filename,
+ :rev => Integer(rev),
+ :commitdate => commitdate,
+ :commitdelta => time_delta_string(Time.now - commitdate),
+ :committer => committer,
+ }
+ end
+
+ ##
+ # Load and render the erb template in the given +template_file+ and write
+ # it out to +out_file+.
+ #
+ # Both +template_file+ and +out_file+ should be Pathname-like objects.
+ #
+ # An io will be yielded which must be captured by binding in the caller.
+
+ def render_template template_file, out_file # :yield: io
+ template = template_for template_file
+
+ unless @options.dry_run then
+ debug_msg "Outputting to %s" % [out_file.expand_path]
+
+ out_file.dirname.mkpath
+ out_file.open 'w', 0644 do |io|
+ io.set_encoding @options.encoding if Object.const_defined? :Encoding
+
+ context = yield io
+
+ template_result template, context, template_file
+ end
+ else
+ context = yield nil
+
+ output = template_result template, context, template_file
+
+ debug_msg " would have written %d characters to %s" % [
+ output.length, out_file.expand_path
+ ]
+ end
+ end
+
+ ##
+ # Creates the result for +template+ with +context+. If an error is raised a
+ # Pathname +template_file+ will indicate the file where the error occurred.
+
+ def template_result template, context, template_file
+ template.filename = template_file.to_s
+ template.result context
+ rescue NoMethodError => e
+ raise RDoc::Error, "Error while evaluating %s: %s" % [
+ template_file.expand_path,
+ e.message,
+ ], e.backtrace
+ end
+
+ ##
+ # Retrieves a cache template for +file+, if present, or fills the cache.
+
+ def template_for file
+ template = @template_cache[file]
+
+ return template if template
+
+ klass = @options.dry_run ? ERB : RDoc::ERBIO
+
+ template = klass.new file.read, nil, '<>'
+ @template_cache[file] = template
+ template
+ end
- ### Copy over the stylesheet into the appropriate place in the output
- ### directory.
- def write_style_sheet
- debug_msg "Copying static files"
- options = { :verbose => $DEBUG_RDOC, :noop => $DARKFISH_DRYRUN }
-
- FileUtils.cp @template_dir + 'rdoc.css', '.', options
-
- Dir[(@template_dir + "{js,images}/**/*").to_s].each do |path|
- next if File.directory? path
- next if path =~ /#{File::SEPARATOR}\./
-
- dst = Pathname.new(path).relative_path_from @template_dir
-
- # I suck at glob
- dst_dir = dst.dirname
- FileUtils.mkdir_p dst_dir, options unless File.exist? dst_dir
-
- FileUtils.cp @template_dir + path, dst, options
- end
- end
-
- ### Build the initial indices and output objects
- ### based on an array of TopLevel objects containing
- ### the extracted information.
- def generate( top_levels )
- @outputdir = Pathname.new( @options.op_dir ).expand_path( @basedir )
-
- @files = top_levels.sort
- @classes = RDoc::TopLevel.all_classes_and_modules.sort
- @methods = @classes.map { |m| m.method_list }.flatten.sort
- @modsort = get_sorted_module_list( @classes )
-
- # Now actually write the output
- write_style_sheet
- generate_index
- generate_class_files
- generate_file_files
-
- rescue StandardError => err
- debug_msg "%s: %s\n %s" % [ err.class.name, err.message, err.backtrace.join("\n ") ]
- raise
- end
-
- #########
- protected
- #########
-
- ### Return a list of the documented modules sorted by salience first, then
- ### by name.
- def get_sorted_module_list( classes )
- nscounts = classes.inject({}) do |counthash, klass|
- top_level = klass.full_name.gsub( /::.*/, '' )
- counthash[top_level] ||= 0
- counthash[top_level] += 1
-
- counthash
- end
-
- # Sort based on how often the top level namespace occurs, and then on the
- # name of the module -- this works for projects that put their stuff into
- # a namespace, of course, but doesn't hurt if they don't.
- classes.sort_by do |klass|
- top_level = klass.full_name.gsub( /::.*/, '' )
- [
- nscounts[ top_level ] * -1,
- klass.full_name
- ]
- end.select do |klass|
- klass.document_self
- end
- end
-
- ### Generate an index page which lists all the classes which
- ### are documented.
- def generate_index
- template_file = @template_dir + 'index.rhtml'
- return unless template_file.exist?
-
- debug_msg "Rendering the index page..."
-
- template_src = template_file.read
- template = ERB.new( template_src, nil, '<>' )
- template.filename = template_file.to_s
- context = binding()
-
- output = nil
-
- begin
- output = template.result( context )
- rescue NoMethodError => err
- raise RDoc::Error, "Error while evaluating %s: %s (at %p)" % [
- template_file,
- err.message,
- eval( "_erbout[-50,50]", context )
- ], err.backtrace
- end
-
- outfile = @basedir + @options.op_dir + 'index.html'
- unless $DARKFISH_DRYRUN
- debug_msg "Outputting to %s" % [outfile.expand_path]
- outfile.open( 'w', 0644 ) do |fh|
- fh.print( output )
- end
- else
- debug_msg "Would have output to %s" % [outfile.expand_path]
- end
- end
-
- ### Generate a documentation file for each class
- def generate_class_files
- template_file = @template_dir + 'classpage.rhtml'
- return unless template_file.exist?
- debug_msg "Generating class documentation in #@outputdir"
-
- @classes.each do |klass|
- debug_msg " working on %s (%s)" % [ klass.full_name, klass.path ]
- outfile = @outputdir + klass.path
- rel_prefix = @outputdir.relative_path_from( outfile.dirname )
- svninfo = self.get_svninfo( klass )
-
- debug_msg " rendering #{outfile}"
- self.render_template( template_file, binding(), outfile )
- end
- end
-
- ### Generate a documentation file for each file
- def generate_file_files
- template_file = @template_dir + 'filepage.rhtml'
- return unless template_file.exist?
- debug_msg "Generating file documentation in #@outputdir"
-
- @files.each do |file|
- outfile = @outputdir + file.path
- debug_msg " working on %s (%s)" % [ file.full_name, outfile ]
- rel_prefix = @outputdir.relative_path_from( outfile.dirname )
-
- debug_msg " rendering #{outfile}"
- self.render_template( template_file, binding(), outfile )
- end
- end
-
-
- ### Return a string describing the amount of time in the given number of
- ### seconds in terms a human can understand easily.
- def time_delta_string( seconds )
- return 'less than a minute' if seconds < 1.minute
- return (seconds / 1.minute).to_s + ' minute' + (seconds/60 == 1 ? '' : 's') if seconds < 50.minutes
- return 'about one hour' if seconds < 90.minutes
- return (seconds / 1.hour).to_s + ' hours' if seconds < 18.hours
- return 'one day' if seconds < 1.day
- return 'about one day' if seconds < 2.days
- return (seconds / 1.day).to_s + ' days' if seconds < 1.week
- return 'about one week' if seconds < 2.week
- return (seconds / 1.week).to_s + ' weeks' if seconds < 3.months
- return (seconds / 1.month).to_s + ' months' if seconds < 1.year
- return (seconds / 1.year).to_s + ' years'
- end
-
-
- # %q$Id: darkfish.rb 52 2009-01-07 02:08:11Z deveiant $"
- SVNID_PATTERN = /
- \$Id:\s
- (\S+)\s # filename
- (\d+)\s # rev
- (\d{4}-\d{2}-\d{2})\s # Date (YYYY-MM-DD)
- (\d{2}:\d{2}:\d{2}Z)\s # Time (HH:MM:SSZ)
- (\w+)\s # committer
- \$$
- /x
-
- ### Try to extract Subversion information out of the first constant whose value looks like
- ### a subversion Id tag. If no matching constant is found, and empty hash is returned.
- def get_svninfo( klass )
- constants = klass.constants or return {}
-
- constants.find {|c| c.value =~ SVNID_PATTERN } or return {}
-
- filename, rev, date, time, committer = $~.captures
- commitdate = Time.parse( date + ' ' + time )
-
- return {
- :filename => filename,
- :rev => Integer( rev ),
- :commitdate => commitdate,
- :commitdelta => time_delta_string( Time.now.to_i - commitdate.to_i ),
- :committer => committer,
- }
- end
-
-
- ### Load and render the erb template in the given +template_file+ within the
- ### specified +context+ (a Binding object) and write it out to +outfile+.
- ### Both +template_file+ and +outfile+ should be Pathname-like objects.
-
- def render_template( template_file, context, outfile )
- template_src = template_file.read
- template = ERB.new( template_src, nil, '<>' )
- template.filename = template_file.to_s
-
- output = begin
- template.result( context )
- rescue NoMethodError => err
- raise RDoc::Error, "Error while evaluating %s: %s (at %p)" % [
- template_file.to_s,
- err.message,
- eval( "_erbout[-50,50]", context )
- ], err.backtrace
- end
-
- unless $DARKFISH_DRYRUN
- outfile.dirname.mkpath
- outfile.open( 'w', 0644 ) do |ofh|
- ofh.print( output )
- end
- else
- debug_msg " would have written %d bytes to %s" %
- [ output.length, outfile ]
- end
- end
-
-end # Roc::Generator::Darkfish
-
-# :stopdoc:
-
-### Time constants
-module TimeConstantMethods # :nodoc:
-
- ### Number of seconds (returns receiver unmodified)
- def seconds
- return self
- end
- alias_method :second, :seconds
-
- ### Returns number of seconds in <receiver> minutes
- def minutes
- return self * 60
- end
- alias_method :minute, :minutes
-
- ### Returns the number of seconds in <receiver> hours
- def hours
- return self * 60.minutes
- end
- alias_method :hour, :hours
-
- ### Returns the number of seconds in <receiver> days
- def days
- return self * 24.hours
- end
- alias_method :day, :days
-
- ### Return the number of seconds in <receiver> weeks
- def weeks
- return self * 7.days
- end
- alias_method :week, :weeks
-
- ### Returns the number of seconds in <receiver> fortnights
- def fortnights
- return self * 2.weeks
- end
- alias_method :fortnight, :fortnights
-
- ### Returns the number of seconds in <receiver> months (approximate)
- def months
- return self * 30.days
- end
- alias_method :month, :months
-
- ### Returns the number of seconds in <receiver> years (approximate)
- def years
- return (self * 365.25.days).to_i
- end
- alias_method :year, :years
-
-
- ### Returns the Time <receiver> number of seconds before the
- ### specified +time+. E.g., 2.hours.before( header.expiration )
- def before( time )
- return time - self
- end
-
-
- ### Returns the Time <receiver> number of seconds ago. (e.g.,
- ### expiration > 2.hours.ago )
- def ago
- return self.before( ::Time.now )
- end
-
-
- ### Returns the Time <receiver> number of seconds after the given +time+.
- ### E.g., 10.minutes.after( header.expiration )
- def after( time )
- return time + self
- end
-
- # Reads best without arguments: 10.minutes.from_now
- def from_now
- return self.after( ::Time.now )
- end
-end # module TimeConstantMethods
-
-
-# Extend Numeric with time constants
-class Numeric # :nodoc:
- include TimeConstantMethods
end
diff --git a/lib/rdoc/generator/markup.rb b/lib/rdoc/generator/markup.rb
index a90b15a1e7..482fd2b2a3 100644
--- a/lib/rdoc/generator/markup.rb
+++ b/lib/rdoc/generator/markup.rb
@@ -36,8 +36,9 @@ module RDoc::Generator::Markup
return @formatter if defined? @formatter
show_hash = RDoc::RDoc.current.options.show_hash
+ hyperlink_all = RDoc::RDoc.current.options.hyperlink_all
this = RDoc::Context === self ? self : @parent
- @formatter = RDoc::Markup::ToHtmlCrossref.new this.path, this, show_hash
+ @formatter = RDoc::Markup::ToHtmlCrossref.new this.path, this, show_hash, hyperlink_all
end
##
@@ -57,36 +58,65 @@ end
class RDoc::AnyMethod
+ ##
+ # Maps RDoc::RubyToken classes to CSS class names
+
+ STYLE_MAP = {
+ RDoc::RubyToken::TkCONSTANT => 'ruby-constant',
+ RDoc::RubyToken::TkKW => 'ruby-keyword',
+ RDoc::RubyToken::TkIVAR => 'ruby-ivar',
+ RDoc::RubyToken::TkOp => 'ruby-operator',
+ RDoc::RubyToken::TkId => 'ruby-identifier',
+ RDoc::RubyToken::TkNode => 'ruby-node',
+ RDoc::RubyToken::TkCOMMENT => 'ruby-comment',
+ RDoc::RubyToken::TkREGEXP => 'ruby-regexp',
+ RDoc::RubyToken::TkSTRING => 'ruby-string',
+ RDoc::RubyToken::TkVal => 'ruby-value',
+ }
+
include RDoc::Generator::Markup
+ @add_line_numbers = false
+
+ class << self
+ ##
+ # Allows controlling whether <tt>#markup_code</tt> adds line numbers to
+ # the source code.
+
+ attr_accessor :add_line_numbers
+ end
+
##
# Prepend +src+ with line numbers. Relies on the first line of a source
# code listing having:
#
- # # File xxxxx, line dddd
+ # # File xxxxx, line dddd
+ #
+ # If it has, line numbers are added an ', line dddd' is removed.
def add_line_numbers(src)
- if src =~ /\A.*, line (\d+)/ then
- first = $1.to_i - 1
- last = first + src.count("\n")
- size = last.to_s.length
-
- line = first
- src.gsub!(/^/) do
- res = if line == first then
- " " * (size + 2)
- else
- "%2$*1$d: " % [size, line]
- end
-
- line += 1
- res
- end
+ return unless src.sub!(/\A(.*)(, line (\d+))/, '\1')
+ first = $3.to_i - 1
+ last = first + src.count("\n")
+ size = last.to_s.length
+
+ line = first
+ src.gsub!(/^/) do
+ res = if line == first then
+ " " * (size + 1)
+ else
+ "<span class=\"line-num\">%2$*1$d</span> " % [size, line]
+ end
+
+ line += 1
+ res
end
end
##
- # Turns the method's token stream into HTML
+ # Turns the method's token stream into HTML.
+ #
+ # Prepends line numbers if +add_line_numbers+ is true.
def markup_code
return '' unless @token_stream
@@ -95,32 +125,32 @@ class RDoc::AnyMethod
@token_stream.each do |t|
next unless t
- # style = STYLE_MAP[t.class]
- style = case t
- 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
+
+ style = STYLE_MAP[t.class]
text = CGI.escapeHTML t.text
- if style
+ if style then
src << "<span class=\"#{style}\">#{text}</span>"
else
src << text
end
end
- add_line_numbers src
+ # dedent the source
+ indent = src.length
+ lines = src.lines.to_a
+ lines.shift if src =~ /\A.*#\ *File/i # remove '# File' comment
+ lines.each do |line|
+ if line =~ /^ *(?=\S)/
+ n = $&.length
+ indent = n if n < indent
+ break if n == 0
+ end
+ end
+ src.gsub!(/^#{' ' * indent}/, '') if indent > 0
+
+ add_line_numbers(src) if self.class.add_line_numbers
src
end
@@ -133,6 +163,12 @@ class RDoc::Attr
end
+class RDoc::Alias
+
+ include RDoc::Generator::Markup
+
+end
+
class RDoc::Constant
include RDoc::Generator::Markup
diff --git a/lib/rdoc/generator/ri.rb b/lib/rdoc/generator/ri.rb
index 819eb52d40..fb52997e89 100644
--- a/lib/rdoc/generator/ri.rb
+++ b/lib/rdoc/generator/ri.rb
@@ -8,10 +8,6 @@ class RDoc::Generator::RI
RDoc::RDoc.add_generator self
- def self.for options
- new options
- end
-
##
# Set up a new ri generator
@@ -20,6 +16,8 @@ class RDoc::Generator::RI
@store = RDoc::RI::Store.new '.'
@old_siginfo = nil
@current = nil
+
+ @store.dry_run = @options.dry_run
end
##
diff --git a/lib/rdoc/generator/template/darkfish/classpage.rhtml b/lib/rdoc/generator/template/darkfish/classpage.rhtml
index 7151087988..72b86ec6a7 100644
--- a/lib/rdoc/generator/template/darkfish/classpage.rhtml
+++ b/lib/rdoc/generator/template/darkfish/classpage.rhtml
@@ -1,296 +1,289 @@
<?xml version="1.0" encoding="<%= @options.charset %>"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
- <meta content="text/html; charset=<%= @options.charset %>" http-equiv="Content-Type" />
+ <meta content="text/html; charset=<%= @options.charset %>" http-equiv="Content-Type" />
- <title><%= klass.type.capitalize %>: <%= klass.full_name %></title>
+ <title><%= klass.type.capitalize %>: <%= klass.full_name %></title>
- <link rel="stylesheet" href="<%= rel_prefix %>/rdoc.css" type="text/css" media="screen" />
+ <link rel="stylesheet" href="<%= rel_prefix %>/rdoc.css" type="text/css" media="screen" />
- <script src="<%= rel_prefix %>/js/jquery.js" type="text/javascript"
- charset="utf-8"></script>
- <script src="<%= rel_prefix %>/js/thickbox-compressed.js" type="text/javascript"
- charset="utf-8"></script>
- <script src="<%= rel_prefix %>/js/quicksearch.js" type="text/javascript"
- charset="utf-8"></script>
- <script src="<%= rel_prefix %>/js/darkfish.js" type="text/javascript"
- charset="utf-8"></script>
+ <script src="<%= rel_prefix %>/js/jquery.js" type="text/javascript"
+ charset="utf-8"></script>
+ <script src="<%= rel_prefix %>/js/thickbox-compressed.js" type="text/javascript"
+ charset="utf-8"></script>
+ <script src="<%= rel_prefix %>/js/quicksearch.js" type="text/javascript"
+ charset="utf-8"></script>
+ <script src="<%= rel_prefix %>/js/darkfish.js" type="text/javascript"
+ charset="utf-8"></script>
</head>
<body class="<%= klass.type %>">
- <div id="metadata">
- <div id="home-metadata">
- <div id="home-section" class="section">
+ <div id="metadata">
+ <div id="home-metadata">
+ <div id="home-section" class="section">
<h3 class="section-header">
<a href="<%= rel_prefix %>/index.html">Home</a>
<a href="<%= rel_prefix %>/index.html#classes">Classes</a>
<a href="<%= rel_prefix %>/index.html#methods">Methods</a>
</h3>
- </div>
- </div>
-
- <div id="file-metadata">
- <div id="file-list-section" class="section">
- <h3 class="section-header">In Files</h3>
- <div class="section-body">
- <ul>
- <% klass.in_files.each do |tl| %>
- <li><a href="<%= rel_prefix %>/<%= h tl.path %>?TB_iframe=true&amp;height=550&amp;width=785"
- class="thickbox" title="<%= h tl.absolute_name %>"><%= h tl.absolute_name %></a></li>
- <% end %>
- </ul>
- </div>
- </div>
-
- <% if !svninfo.empty? %>
- <div id="file-svninfo-section" class="section">
- <h3 class="section-header">Subversion Info</h3>
- <div class="section-body">
- <dl class="svninfo">
- <dt>Rev</dt>
- <dd><%= svninfo[:rev] %></dd>
-
- <dt>Last Checked In</dt>
- <dd><%= svninfo[:commitdate].strftime('%Y-%m-%d %H:%M:%S') %>
- (<%= svninfo[:commitdelta] %> ago)</dd>
-
- <dt>Checked in by</dt>
- <dd><%= svninfo[:committer] %></dd>
- </dl>
- </div>
- </div>
- <% end %>
- </div>
-
- <div id="class-metadata">
-
- <!-- Parent Class -->
- <% if klass.type == 'class' %>
- <div id="parent-class-section" class="section">
- <h3 class="section-header">Parent</h3>
- <% unless String === klass.superclass %>
- <p class="link"><a href="<%= klass.aref_to klass.superclass.path %>"><%= klass.superclass.full_name %></a></p>
- <% else %>
- <p class="link"><%= klass.superclass %></p>
- <% end %>
- </div>
- <% end %>
-
- <!-- Namespace Contents -->
- <% unless klass.classes_and_modules.empty? %>
- <div id="namespace-list-section" class="section">
- <h3 class="section-header">Namespace</h3>
- <ul class="link-list">
- <% (klass.modules.sort + klass.classes.sort).each do |mod| %>
- <li><span class="type"><%= mod.type.upcase %></span> <a href="<%= klass.aref_to mod.path %>"><%= mod.full_name %></a></li>
- <% end %>
- </ul>
- </div>
- <% end %>
-
- <!-- Method Quickref -->
- <% unless klass.method_list.empty? %>
- <div id="method-list-section" class="section">
- <h3 class="section-header">Methods</h3>
- <ul class="link-list">
- <% klass.each_method do |meth| %>
- <li><a href="#<%= meth.aref %>"><%= meth.singleton ? '::' : '#' %><%= meth.name %></a></li>
- <% end %>
- </ul>
- </div>
- <% end %>
-
- <!-- Included Modules -->
- <% unless klass.includes.empty? %>
- <div id="includes-section" class="section">
- <h3 class="section-header">Included Modules</h3>
- <ul class="link-list">
- <% klass.each_include do |inc| %>
- <% unless String === inc.module %>
- <li><a class="include" href="<%= klass.aref_to inc.module.path %>"><%= inc.module.full_name %></a></li>
- <% else %>
- <li><span class="include"><%= inc.name %></span></li>
- <% end %>
- <% end %>
- </ul>
- </div>
- <% end %>
- </div>
-
- <div id="project-metadata">
- <% simple_files = @files.select {|tl| tl.parser == RDoc::Parser::Simple } %>
- <% unless simple_files.empty? then %>
- <div id="fileindex-section" class="section project-section">
- <h3 class="section-header">Files</h3>
- <ul>
- <% simple_files.each do |file| %>
- <li class="file"><a href="<%= rel_prefix %>/<%= file.path %>"><%= h file.base_name %></a></li>
- <% end %>
- </ul>
- </div>
- <% end %>
-
- <div id="classindex-section" class="section project-section">
- <h3 class="section-header">Class Index
- <span class="search-toggle"><img src="<%= rel_prefix %>/images/find.png"
- height="16" width="16" alt="[+]"
- title="show/hide quicksearch" /></span></h3>
- <form action="#" method="get" accept-charset="utf-8" class="initially-hidden">
- <fieldset>
- <legend>Quicksearch</legend>
- <input type="text" name="quicksearch" value=""
- class="quicksearch-field" />
- </fieldset>
- </form>
-
- <ul class="link-list">
- <% @modsort.each do |index_klass| %>
- <li><a href="<%= rel_prefix %>/<%= index_klass.path %>"><%= index_klass.full_name %></a></li>
- <% end %>
- </ul>
- <div id="no-class-search-results" style="display: none;">No matching classes.</div>
- </div>
-
- <% if $DEBUG_RDOC %>
- <div id="debugging-toggle"><img src="<%= rel_prefix %>/images/bug.png"
- alt="toggle debugging" height="16" width="16" /></div>
- <% end %>
- </div>
- </div>
-
- <div id="documentation">
- <h1 class="<%= klass.type %>"><%= klass.full_name %></h1>
-
- <div id="description">
- <%= klass.description %>
- </div>
-
- <!-- Constants -->
- <% unless klass.constants.empty? %>
- <div id="constants-list" class="section">
- <h3 class="section-header">Constants</h3>
- <dl>
- <% klass.each_constant do |const| %>
- <dt><a name="<%= const.name %>"><%= const.name %></a></dt>
- <% if const.comment %>
- <dd class="description"><%= const.description.strip %></dd>
- <% else %>
- <dd class="description missing-docs">(Not documented)</dd>
- <% end %>
- <% end %>
- </dl>
- </div>
- <% end %>
-
- <!-- Attributes -->
- <% unless klass.attributes.empty? %>
- <div id="attribute-method-details" class="method-section section">
- <h3 class="section-header">Attributes</h3>
-
- <% klass.each_attribute do |attrib| %>
- <div id="<%= attrib.html_name %>-attribute-method" class="method-detail">
- <a name="<%= h attrib.name %>"></a>
- <% if attrib.rw =~ /w/i %>
- <a name="<%= h attrib.name %>="></a>
- <% end %>
- <div class="method-heading attribute-method-heading">
- <span class="method-name"><%= h attrib.name %></span><span
- class="attribute-access-type">[<%= attrib.rw %>]</span>
- </div>
-
- <div class="method-description">
- <% if attrib.comment %>
- <%= attrib.description.strip %>
- <% else %>
- <p class="missing-docs">(Not documented)</p>
- <% end %>
- </div>
- </div>
- <% end %>
- </div>
- <% end %>
-
- <!-- Methods -->
- <% klass.methods_by_type.each do |type, visibilities|
- next if visibilities.empty?
- visibilities.each do |visibility, methods|
- next if methods.empty? %>
- <div id="<%= visibility %>-<%= type %>-method-details" class="method-section section">
- <h3 class="section-header"><%= visibility.to_s.capitalize %> <%= type.capitalize %> Methods</h3>
-
- <% methods.each do |method| %>
- <div id="<%= method.html_name %>-method" class="method-detail <%= method.is_alias_for ? "method-alias" : '' %>">
- <a name="<%= h method.aref %>"></a>
-
- <div class="method-heading">
- <% if method.call_seq %>
- <span class="method-callseq"><%= method.call_seq.strip.gsub(/->/, '&rarr;').gsub( /^\w.+\./m, '') %></span>
- <span class="method-click-advice">click to toggle source</span>
- <% else %>
- <span class="method-name"><%= h method.name %></span><span
- class="method-args"><%= method.params %></span>
- <span class="method-click-advice">click to toggle source</span>
- <% end %>
- </div>
-
- <div class="method-description">
- <% if method.comment %>
- <%= method.description.strip %>
- <% else %>
- <p class="missing-docs">(Not documented)</p>
- <% end %>
-
- <% if method.token_stream %>
- <div class="method-source-code"
- id="<%= method.html_name %>-source">
+ </div>
+ </div>
+
+ <div id="file-metadata">
+ <div id="file-list-section" class="section">
+ <h3 class="section-header">In Files</h3>
+ <div class="section-body">
+ <ul>
+ <% klass.in_files.each do |tl| %>
+ <li><a href="<%= rel_prefix %>/<%= h tl.path %>?TB_iframe=true&amp;height=550&amp;width=785"
+ class="thickbox" title="<%= h tl.absolute_name %>"><%= h tl.absolute_name %></a></li>
+ <% end %>
+ </ul>
+ </div>
+ </div>
+
+ <% if !svninfo.empty? %>
+ <div id="file-svninfo-section" class="section">
+ <h3 class="section-header">Subversion Info</h3>
+ <div class="section-body">
+ <dl class="svninfo">
+ <dt>Rev</dt>
+ <dd><%= svninfo[:rev] %></dd>
+
+ <dt>Last Checked In</dt>
+ <dd><%= svninfo[:commitdate].strftime('%Y-%m-%d %H:%M:%S') %>
+ (<%= svninfo[:commitdelta] %> ago)</dd>
+
+ <dt>Checked in by</dt>
+ <dd><%= svninfo[:committer] %></dd>
+ </dl>
+ </div>
+ </div>
+ <% end %>
+ </div>
+
+ <div id="class-metadata">
+
+ <!-- Parent Class -->
+ <% if klass.type == 'class' %>
+ <div id="parent-class-section" class="section">
+ <h3 class="section-header">Parent</h3>
+ <% if klass.superclass and not String === klass.superclass then %>
+ <p class="link"><a href="<%= klass.aref_to klass.superclass.path %>"><%= klass.superclass.full_name %></a></p>
+ <% else %>
+ <p class="link"><%= klass.superclass %></p>
+ <% end %>
+ </div>
+ <% end %>
+
+ <!-- Namespace Contents -->
+ <% unless klass.classes_and_modules.empty? %>
+ <div id="namespace-list-section" class="section">
+ <h3 class="section-header">Namespace</h3>
+ <ul class="link-list">
+ <% (klass.modules.sort + klass.classes.sort).each do |mod| %>
+ <li><span class="type"><%= mod.type.upcase %></span> <a href="<%= klass.aref_to mod.path %>"><%= mod.full_name %></a></li>
+ <% end %>
+ </ul>
+ </div>
+ <% end %>
+
+ <!-- Method Quickref -->
+ <% unless klass.method_list.empty? %>
+ <div id="method-list-section" class="section">
+ <h3 class="section-header">Methods</h3>
+ <ul class="link-list">
+ <% klass.each_method do |meth| %>
+ <li><a href="#<%= meth.aref %>"><%= meth.singleton ? '::' : '#' %><%= meth.name %></a></li>
+ <% end %>
+ </ul>
+ </div>
+ <% end %>
+
+ <!-- Included Modules -->
+ <% unless klass.includes.empty? %>
+ <div id="includes-section" class="section">
+ <h3 class="section-header">Included Modules</h3>
+ <ul class="link-list">
+ <% klass.each_include do |inc| %>
+ <% unless String === inc.module %>
+ <li><a class="include" href="<%= klass.aref_to inc.module.path %>"><%= inc.module.full_name %></a></li>
+ <% else %>
+ <li><span class="include"><%= inc.name %></span></li>
+ <% end %>
+ <% end %>
+ </ul>
+ </div>
+ <% end %>
+ </div>
+
+ <div id="project-metadata">
+ <% simple_files = @files.select {|tl| tl.parser == RDoc::Parser::Simple } %>
+ <% unless simple_files.empty? then %>
+ <div id="fileindex-section" class="section project-section">
+ <h3 class="section-header">Files</h3>
+ <ul>
+ <% simple_files.each do |file| %>
+ <li class="file"><a href="<%= rel_prefix %>/<%= file.path %>"><%= h file.base_name %></a></li>
+ <% end %>
+ </ul>
+ </div>
+ <% end %>
+
+ <div id="classindex-section" class="section project-section">
+ <h3 class="section-header">Class Index
+ <span class="search-toggle"><img src="<%= rel_prefix %>/images/find.png"
+ height="16" width="16" alt="[+]"
+ title="show/hide quicksearch" /></span></h3>
+ <form action="#" method="get" accept-charset="utf-8" class="initially-hidden">
+ <fieldset>
+ <legend>Quicksearch</legend>
+ <input type="text" name="quicksearch" value=""
+ class="quicksearch-field" />
+ </fieldset>
+ </form>
+
+ <ul class="link-list">
+ <% @modsort.each do |index_klass| %>
+ <li><a href="<%= rel_prefix %>/<%= index_klass.path %>"><%= index_klass.full_name %></a></li>
+ <% end %>
+ </ul>
+ <div id="no-class-search-results" style="display: none;">No matching classes.</div>
+ </div>
+
+ <% if $DEBUG_RDOC %>
+ <div id="debugging-toggle"><img src="<%= rel_prefix %>/images/bug.png"
+ alt="toggle debugging" height="16" width="16" /></div>
+ <% end %>
+ </div>
+ </div>
+
+ <div id="documentation">
+ <h1 class="<%= klass.type %>"><%= klass.full_name %></h1>
+
+ <div id="description">
+ <%= klass.description %>
+ </div>
+
+ <!-- Constants -->
+ <% unless klass.constants.empty? %>
+ <div id="constants-list" class="section">
+ <h3 class="section-header">Constants</h3>
+ <dl>
+ <% klass.each_constant do |const| %>
+ <dt><a name="<%= const.name %>"><%= const.name %></a></dt>
+ <% if const.comment %>
+ <dd class="description"><%= const.description.strip %></dd>
+ <% else %>
+ <dd class="description missing-docs">(Not documented)</dd>
+ <% end %>
+ <% end %>
+ </dl>
+ </div>
+ <% end %>
+
+ <!-- Attributes -->
+ <% unless klass.attributes.empty? %>
+ <div id="attribute-method-details" class="method-section section">
+ <h3 class="section-header">Attributes</h3>
+
+ <% klass.each_attribute do |attrib| %>
+ <div id="<%= attrib.html_name %>-attribute-method" class="method-detail">
+ <a name="<%= h attrib.name %>"></a>
+ <% if attrib.rw =~ /w/i %>
+ <a name="<%= h attrib.name %>="></a>
+ <% end %>
+ <div class="method-heading attribute-method-heading">
+ <span class="method-name"><%= h attrib.name %></span><span
+ class="attribute-access-type">[<%= attrib.rw %>]</span>
+ </div>
+
+ <div class="method-description">
+ <% if attrib.comment %>
+ <%= attrib.description.strip %>
+ <% else %>
+ <p class="missing-docs">(Not documented)</p>
+ <% end %>
+ </div>
+ </div>
+ <% end %>
+ </div>
+ <% end %>
+
+ <!-- Methods -->
+ <% klass.methods_by_type.each do |type, visibilities|
+ next if visibilities.empty?
+ visibilities.each do |visibility, methods|
+ next if methods.empty? %>
+ <div id="<%= visibility %>-<%= type %>-method-details" class="method-section section">
+ <h3 class="section-header"><%= visibility.to_s.capitalize %> <%= type.capitalize %> Methods</h3>
+
+ <% methods.each do |method| %>
+ <div id="<%= method.html_name %>-method" class="method-detail <%= method.is_alias_for ? "method-alias" : '' %>">
+ <a name="<%= h method.aref %>"></a>
+
+ <div class="method-heading">
+ <% if method.call_seq %>
+ <span class="method-callseq"><%= method.call_seq.strip.gsub(/->/, '&rarr;').gsub( /^\w.+\./m, '') %></span>
+ <span class="method-click-advice">click to toggle source</span>
+ <% else %>
+ <span class="method-name"><%= h method.name %></span><span
+ class="method-args"><%= method.params %></span>
+ <span class="method-click-advice">click to toggle source</span>
+ <% end %>
+ </div>
+
+ <div class="method-description">
+ <% if method.comment %>
+ <%= method.description.strip %>
+ <% else %>
+ <p class="missing-docs">(Not documented)</p>
+ <% end %>
+
+ <% if method.token_stream %>
+ <div class="method-source-code"
+ id="<%= method.html_name %>-source">
<pre>
<%= method.markup_code %>
</pre>
- </div>
- <% end %>
- </div>
-
- <% unless method.aliases.empty? %>
- <div class="aliases">
- Also aliased as: <%= method.aliases.map do |aka|
- %{<a href="#{ klass.aref_to aka.path}">#{h aka.name}</a>}
- end.join(", ") %>
- </div>
- <% end %>
-
- <% if method.is_alias_for then %>
- <div class="aliases">
+ </div>
+ <% end %>
+ </div>
+
+ <% unless method.aliases.empty? %>
+ <div class="aliases">
+ Also aliased as: <%= method.aliases.map do |aka|
+ if aka.parent then # HACK lib/rexml/encodings
+ %{<a href="#{klass.aref_to aka.path}">#{h aka.name}</a>}
+ else
+ h aka.name
+ end
+ end.join ", " %>
+ </div>
+ <% end %>
+
+ <% if method.is_alias_for then %>
+ <div class="aliases">
Alias for: <a href="<%= klass.aref_to method.is_alias_for.path %>"><%= h method.is_alias_for.name %></a>
- </div>
- <% end %>
- </div>
-
- <% end %>
- </div>
- <% end
- end %>
-
- </div>
-
-
- <div id="rdoc-debugging-section-dump" class="debugging-section">
- <% if $DEBUG_RDOC
- require 'pp' %>
-<pre><%= h PP.pp(klass, _erbout) %></pre>
- </div>
- <% else %>
- <p>Disabled; run with --debug to generate this.</p>
- <% end %>
- </div>
-
- <div id="validator-badges">
- <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
- <p><small>Generated with the <a href="http://deveiate.org/projects/Darkfish-Rdoc/">Darkfish
- Rdoc Generator</a> <%= RDoc::Generator::Darkfish::VERSION %></small>.</p>
- </div>
+ </div>
+ <% end %>
+ </div>
+
+ <% end %>
+ </div>
+ <% end
+ end %>
+
+ </div>
+
+ <div id="validator-badges">
+ <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
+ <p><small>Generated with the <a href="http://deveiate.org/projects/Darkfish-Rdoc/">Darkfish
+ Rdoc Generator</a> <%= RDoc::Generator::Darkfish::VERSION %></small>.</p>
+ </div>
</body>
</html>
diff --git a/lib/rdoc/generator/template/darkfish/filepage.rhtml b/lib/rdoc/generator/template/darkfish/filepage.rhtml
index 33216dc8f1..b230a456a3 100644
--- a/lib/rdoc/generator/template/darkfish/filepage.rhtml
+++ b/lib/rdoc/generator/template/darkfish/filepage.rhtml
@@ -1,123 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
- <meta content="text/html; charset=<%= @options.charset %>" http-equiv="Content-Type" />
+ <meta content="text/html; charset=<%= @options.charset %>" http-equiv="Content-Type" />
- <title>File: <%= file.base_name %> [<%= @options.title %>]</title>
+ <title>File: <%= file.base_name %> [<%= @options.title %>]</title>
- <link type="text/css" media="screen" href="<%= rel_prefix %>/rdoc.css" rel="stylesheet" />
+ <link type="text/css" media="screen" href="<%= rel_prefix %>/rdoc.css" rel="stylesheet" />
- <script src="<%= rel_prefix %>/js/jquery.js" type="text/javascript"
- charset="utf-8"></script>
- <script src="<%= rel_prefix %>/js/thickbox-compressed.js" type="text/javascript"
- charset="utf-8"></script>
- <script src="<%= rel_prefix %>/js/quicksearch.js" type="text/javascript"
- charset="utf-8"></script>
- <script src="<%= rel_prefix %>/js/darkfish.js" type="text/javascript"
- charset="utf-8"></script>
+ <script src="<%= rel_prefix %>/js/jquery.js" type="text/javascript"
+ charset="utf-8"></script>
+ <script src="<%= rel_prefix %>/js/thickbox-compressed.js" type="text/javascript"
+ charset="utf-8"></script>
+ <script src="<%= rel_prefix %>/js/quicksearch.js" type="text/javascript"
+ charset="utf-8"></script>
+ <script src="<%= rel_prefix %>/js/darkfish.js" type="text/javascript"
+ charset="utf-8"></script>
</head>
<% if file.parser == RDoc::Parser::Simple %>
<body class="file">
- <div id="metadata">
- <div id="home-metadata">
- <div id="home-section" class="section">
+ <div id="metadata">
+ <div id="home-metadata">
+ <div id="home-section" class="section">
<h3 class="section-header">
<a href="<%= rel_prefix %>/index.html">Home</a>
<a href="<%= rel_prefix %>/index.html#classes">Classes</a>
<a href="<%= rel_prefix %>/index.html#methods">Methods</a>
</h3>
- </div>
- </div>
+ </div>
+ </div>
- <div id="project-metadata">
- <% simple_files = @files.select { |f| f.parser == RDoc::Parser::Simple } %>
- <% unless simple_files.empty? then %>
- <div id="fileindex-section" class="section project-section">
- <h3 class="section-header">Files</h3>
- <ul>
- <% simple_files.each do |f| %>
- <li class="file"><a href="<%= rel_prefix %>/<%= f.path %>"><%= h f.base_name %></a></li>
- <% end %>
- </ul>
- </div>
- <% end %>
+ <div id="project-metadata">
+ <% simple_files = @files.select { |f| f.parser == RDoc::Parser::Simple } %>
+ <% unless simple_files.empty? then %>
+ <div id="fileindex-section" class="section project-section">
+ <h3 class="section-header">Files</h3>
+ <ul>
+ <% simple_files.each do |f| %>
+ <li class="file"><a href="<%= rel_prefix %>/<%= f.path %>"><%= h f.base_name %></a></li>
+ <% end %>
+ </ul>
+ </div>
+ <% end %>
- <div id="classindex-section" class="section project-section">
- <h3 class="section-header">Class Index
- <span class="search-toggle"><img src="<%= rel_prefix %>/images/find.png"
- height="16" width="16" alt="[+]"
- title="show/hide quicksearch" /></span></h3>
- <form action="#" method="get" accept-charset="utf-8" class="initially-hidden">
- <fieldset>
- <legend>Quicksearch</legend>
- <input type="text" name="quicksearch" value=""
- class="quicksearch-field" />
- </fieldset>
- </form>
+ <div id="classindex-section" class="section project-section">
+ <h3 class="section-header">Class Index
+ <span class="search-toggle"><img src="<%= rel_prefix %>/images/find.png"
+ height="16" width="16" alt="[+]"
+ title="show/hide quicksearch" /></span></h3>
+ <form action="#" method="get" accept-charset="utf-8" class="initially-hidden">
+ <fieldset>
+ <legend>Quicksearch</legend>
+ <input type="text" name="quicksearch" value=""
+ class="quicksearch-field" />
+ </fieldset>
+ </form>
- <ul class="link-list">
- <% @modsort.each do |index_klass| %>
- <li><a href="<%= rel_prefix %>/<%= index_klass.path %>"><%= index_klass.full_name %></a></li>
- <% end %>
- </ul>
- <div id="no-class-search-results" style="display: none;">No matching classes.</div>
- </div>
+ <ul class="link-list">
+ <% @modsort.each do |index_klass| %>
+ <li><a href="<%= rel_prefix %>/<%= index_klass.path %>"><%= index_klass.full_name %></a></li>
+ <% end %>
+ </ul>
+ <div id="no-class-search-results" style="display: none;">No matching classes.</div>
+ </div>
- <% if $DEBUG_RDOC %>
- <div id="debugging-toggle"><img src="<%= rel_prefix %>/images/bug.png"
- alt="toggle debugging" height="16" width="16" /></div>
- <% end %>
- </div>
- </div>
+ <% if $DEBUG_RDOC %>
+ <div id="debugging-toggle"><img src="<%= rel_prefix %>/images/bug.png"
+ alt="toggle debugging" height="16" width="16" /></div>
+ <% end %>
+ </div>
+ </div>
- <div id="documentation">
- <%= file.description %>
- </div>
+ <div id="documentation">
+ <%= file.description %>
+ </div>
- <div id="validator-badges">
- <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
- <p><small>Generated with the <a href="http://deveiate.org/projects/Darkfish-Rdoc/">Darkfish
- Rdoc Generator</a> <%= RDoc::Generator::Darkfish::VERSION %></small>.</p>
- </div>
+ <div id="validator-badges">
+ <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
+ <p><small>Generated with the <a href="http://deveiate.org/projects/Darkfish-Rdoc/">Darkfish
+ Rdoc Generator</a> <%= RDoc::Generator::Darkfish::VERSION %></small>.</p>
+ </div>
</body>
<% else %>
<body class="file file-popup">
- <div id="metadata">
- <dl>
- <dt class="modified-date">Last Modified</dt>
- <dd class="modified-date"><%= file.last_modified %></dd>
+ <div id="metadata">
+ <dl>
+ <dt class="modified-date">Last Modified</dt>
+ <dd class="modified-date"><%= file.last_modified %></dd>
- <% if file.requires %>
- <dt class="requires">Requires</dt>
- <dd class="requires">
- <ul>
- <% file.requires.each do |require| %>
- <li><%= require.name %></li>
- <% end %>
- </ul>
- </dd>
- <% end %>
+ <% if file.requires %>
+ <dt class="requires">Requires</dt>
+ <dd class="requires">
+ <ul>
+ <% file.requires.each do |require| %>
+ <li><%= require.name %></li>
+ <% end %>
+ </ul>
+ </dd>
+ <% end %>
- <% if @options.webcvs %>
- <dt class="scs-url">Trac URL</dt>
- <dd class="scs-url"><a target="_top"
- href="<%= file.cvs_url %>"><%= file.cvs_url %></a></dd>
- <% end %>
- </dl>
- </div>
+ <% if @options.webcvs %>
+ <dt class="scs-url">Trac URL</dt>
+ <dd class="scs-url"><a target="_top"
+ href="<%= file.cvs_url %>"><%= file.cvs_url %></a></dd>
+ <% end %>
+ </dl>
+ </div>
- <div id="documentation">
- <% if file.comment %>
- <div class="description">
- <h2>Description</h2>
- <%= file.description %>
- </div>
- <% end %>
- </div>
+ <div id="documentation">
+ <% if file.comment %>
+ <div class="description">
+ <h2>Description</h2>
+ <%= file.description %>
+ </div>
+ <% end %>
+ </div>
</body>
<% end %>
</html>
diff --git a/lib/rdoc/generator/template/darkfish/index.rhtml b/lib/rdoc/generator/template/darkfish/index.rhtml
index e853235ddb..3198246f8a 100644
--- a/lib/rdoc/generator/template/darkfish/index.rhtml
+++ b/lib/rdoc/generator/template/darkfish/index.rhtml
@@ -1,64 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
- "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
- <meta content="text/html; charset=<%= @options.charset %>" http-equiv="Content-Type" />
+ <meta content="text/html; charset=<%= @options.charset %>" http-equiv="Content-Type" />
- <title><%= h @options.title %></title>
+ <title><%= h @options.title %></title>
- <link type="text/css" media="screen" href="rdoc.css" rel="stylesheet" />
+ <link type="text/css" media="screen" href="rdoc.css" rel="stylesheet" />
- <script src="js/jquery.js" type="text/javascript" charset="utf-8"></script>
- <script src="js/thickbox-compressed.js" type="text/javascript" charset="utf-8"></script>
- <script src="js/quicksearch.js" type="text/javascript" charset="utf-8"></script>
- <script src="js/darkfish.js" type="text/javascript" charset="utf-8"></script>
+ <script src="js/jquery.js" type="text/javascript" charset="utf-8"></script>
+ <script src="js/thickbox-compressed.js" type="text/javascript" charset="utf-8"></script>
+ <script src="js/quicksearch.js" type="text/javascript" charset="utf-8"></script>
+ <script src="js/darkfish.js" type="text/javascript" charset="utf-8"></script>
</head>
<body class="indexpage">
- <% $stderr.sync = true %>
- <h1><%= h @options.title %></h1>
+ <% $stderr.sync = true %>
+ <h1><%= h @options.title %></h1>
- <% if @options.main_page && main_page = @files.find { |f| f.full_name == @options.main_page } %>
- <div id="main">
- <%= main_page.description.sub(%r{^\s*<h1.*?/h1>}i, '') %>
- </div>
- <% else %>
- <p>This is the API documentation for '<%= @options.title %>'.</p>
- <% end %>
+ <% if @options.main_page && main_page = @files.find { |f| f.full_name == @options.main_page } then %>
+ <div id="main">
+ <%= main_page.description.sub(%r{^\s*<h1.*?/h1>}i, '') %>
+ </div>
+ <% else %>
+ <p>This is the API documentation for '<%= @options.title %>'.</p>
+ <% end %>
- <% simple_files = @files.select {|tl| tl.parser == RDoc::Parser::Simple } %>
- <% unless simple_files.empty? then %>
- <h2>Files</h2>
- <ul>
- <% simple_files.sort.each do |file| %>
- <li class="file"><a href="<%= file.path %>"><%= h file.base_name %></a></li>
- <% end %>
- </ul>
- <% end %>
+ <% simple_files = @files.select {|tl| tl.parser == RDoc::Parser::Simple } %>
+ <% unless simple_files.empty? then %>
+ <h2>Files</h2>
+ <ul>
+ <% simple_files.sort.each do |file| %>
+ <li class="file"><a href="<%= file.path %>"><%= h file.base_name %></a></li>
+ <% end %>
+ </ul>
+ <% end %>
- <h2 id="classes">Classes/Modules</h2>
- <ul>
- <% @modsort.each do |klass| %>
- <li class="<%= klass.type %>"><a href="<%= klass.path %>"><%= klass.full_name %></a></li>
- <% end %>
- </ul>
+ <h2 id="classes">Classes/Modules</h2>
+ <ul>
+ <% @modsort.each do |klass| %>
+ <li class="<%= klass.type %>"><a href="<%= klass.path %>"><%= klass.full_name %></a></li>
+ <% end %>
+ </ul>
- <h2 id="methods">Methods</h2>
- <ul>
- <% RDoc::TopLevel.all_classes_and_modules.map do |mod|
- mod.method_list
- end.flatten.sort.each do |method| %>
- <li><a href="<%= method.path %>"><%= method.pretty_name %> &mdash; <%= method.parent.full_name %></a></li>
- <% end %>
- </ul>
+ <h2 id="methods">Methods</h2>
+ <ul>
+ <% RDoc::TopLevel.all_classes_and_modules.map do |mod|
+ mod.method_list
+ end.flatten.sort.each do |method| %>
+ <li><a href="<%= method.path %>"><%= method.pretty_name %> &mdash; <%= method.parent.full_name %></a></li>
+ <% end %>
+ </ul>
- <div id="validator-badges">
- <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
- <p><small>Generated with the <a href="http://deveiate.org/projects/Darkfish-Rdoc/">Darkfish
- Rdoc Generator</a> <%= RDoc::Generator::Darkfish::VERSION %></small>.</p>
- </div>
+ <div id="validator-badges">
+ <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
+ <p><small>Generated with the <a href="http://deveiate.org/projects/Darkfish-Rdoc/">Darkfish
+ Rdoc Generator</a> <%= RDoc::Generator::Darkfish::VERSION %></small>.</p>
+ </div>
</body>
</html>
diff --git a/lib/rdoc/generator/template/darkfish/rdoc.css b/lib/rdoc/generator/template/darkfish/rdoc.css
index ffe996001a..231f9b7f04 100644
--- a/lib/rdoc/generator/template/darkfish/rdoc.css
+++ b/lib/rdoc/generator/template/darkfish/rdoc.css
@@ -12,76 +12,76 @@
body {
background: #efefef;
- font: 14px "Helvetica Neue", Helvetica, Tahoma, sans-serif;
+ font: 14px "Helvetica Neue", Helvetica, Tahoma, sans-serif;
}
body.class, body.module, body.file {
- margin-left: 40px;
+ margin-left: 40px;
}
body.file-popup {
- font-size: 90%;
- margin-left: 0;
+ font-size: 90%;
+ margin-left: 0;
}
h1 {
- font-size: 300%;
- text-shadow: rgba(135,145,135,0.65) 2px 2px 3px;
- color: #6C8C22;
+ font-size: 300%;
+ text-shadow: rgba(135,145,135,0.65) 2px 2px 3px;
+ color: #6C8C22;
}
h2,h3,h4 { margin-top: 1.5em; }
:link,
:visited {
- color: #6C8C22;
- text-decoration: none;
+ color: #6C8C22;
+ text-decoration: none;
}
:link:hover,
:visited:hover {
- border-bottom: 1px dotted #6C8C22;
+ border-bottom: 1px dotted #6C8C22;
}
pre {
- background: #ddd;
- padding: 0.5em 0;
+ background: #ddd;
+ padding: 0.5em 0;
}
/* @group Generic Classes */
.initially-hidden {
- display: none;
+ display: none;
}
.quicksearch-field {
- width: 98%;
- background: #ddd;
- border: 1px solid #aaa;
- height: 1.5em;
- -webkit-border-radius: 4px;
+ width: 98%;
+ background: #ddd;
+ border: 1px solid #aaa;
+ height: 1.5em;
+ -webkit-border-radius: 4px;
}
.quicksearch-field:focus {
- background: #f1edba;
+ background: #f1edba;
}
.missing-docs {
- font-size: 120%;
- background: white url(images/wrench_orange.png) no-repeat 4px center;
- color: #ccc;
- line-height: 2em;
- border: 1px solid #d00;
- opacity: 1;
- padding-left: 20px;
- text-indent: 24px;
- letter-spacing: 3px;
- font-weight: bold;
- -webkit-border-radius: 5px;
- -moz-border-radius: 5px;
+ font-size: 120%;
+ background: white url(images/wrench_orange.png) no-repeat 4px center;
+ color: #ccc;
+ line-height: 2em;
+ border: 1px solid #d00;
+ opacity: 1;
+ padding-left: 20px;
+ text-indent: 24px;
+ letter-spacing: 3px;
+ font-weight: bold;
+ -webkit-border-radius: 5px;
+ -moz-border-radius: 5px;
}
.target-section {
- border: 2px solid #dcce90;
- border-left-width: 8px;
- padding: 0 1em;
- background: #fff3c2;
+ border: 2px solid #dcce90;
+ border-left-width: 8px;
+ padding: 0 1em;
+ background: #fff3c2;
}
/* @end */
@@ -89,37 +89,37 @@ pre {
/* @group Index Page, Standalone file pages */
body.indexpage {
- margin: 1em 3em;
+ margin: 1em 3em;
}
body.indexpage p,
body.indexpage div,
body.file p {
- margin: 1em 0;
+ margin: 1em 0;
}
.indexpage ul,
.file #documentation ul {
- line-height: 160%;
- list-style: none;
+ line-height: 160%;
+ list-style: none;
}
.indexpage ul :link,
.indexpage ul :visited {
- font-size: 16px;
+ font-size: 16px;
}
.indexpage li,
.file #documentation li {
- padding-left: 20px;
- background: url(images/bullet_black.png) no-repeat left 4px;
+ padding-left: 20px;
+ background: url(images/bullet_black.png) no-repeat left 4px;
}
.indexpage li.module {
- background: url(images/package.png) no-repeat left 4px;
+ background: url(images/package.png) no-repeat left 4px;
}
.indexpage li.class {
- background: url(images/ruby.png) no-repeat left 4px;
+ background: url(images/ruby.png) no-repeat left 4px;
}
.indexpage li.file {
- background: url(images/page_white_text.png) no-repeat left 4px;
+ background: url(images/page_white_text.png) no-repeat left 4px;
}
.file li p,
.indexpage li p {
@@ -133,48 +133,48 @@ body.file p {
.class #metadata,
.file #metadata,
.module #metadata {
- float: left;
- width: 260px;
+ float: left;
+ width: 260px;
}
.class #documentation,
.file #documentation,
.module #documentation {
- margin: 2em 1em 5em 300px;
- min-width: 340px;
+ margin: 2em 1em 5em 300px;
+ min-width: 340px;
}
.file #metadata {
- margin: 0.8em;
+ margin: 0.8em;
}
#validator-badges {
- clear: both;
- margin: 1em 1em 2em;
+ clear: both;
+ margin: 1em 1em 2em;
}
/* @end */
/* @group Metadata Section */
#metadata .section {
- background-color: #dedede;
- -moz-border-radius: 5px;
- -webkit-border-radius: 5px;
- border: 1px solid #aaa;
- margin: 0 8px 16px;
- font-size: 90%;
- overflow: hidden;
+ background-color: #dedede;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ border: 1px solid #aaa;
+ margin: 0 8px 16px;
+ font-size: 90%;
+ overflow: hidden;
}
#metadata h3.section-header {
- margin: 0;
- padding: 2px 8px;
- background: #ccc;
- color: #666;
- -moz-border-radius-topleft: 4px;
- -moz-border-radius-topright: 4px;
- -webkit-border-top-left-radius: 4px;
- -webkit-border-top-right-radius: 4px;
- border-bottom: 1px solid #aaa;
+ margin: 0;
+ padding: 2px 8px;
+ background: #ccc;
+ color: #666;
+ -moz-border-radius-topleft: 4px;
+ -moz-border-radius-topright: 4px;
+ -webkit-border-top-left-radius: 4px;
+ -webkit-border-top-right-radius: 4px;
+ border-bottom: 1px solid #aaa;
}
#metadata #home-section h3.section-header {
border-bottom: 0;
@@ -183,33 +183,33 @@ body.file p {
#metadata ul,
#metadata dl,
#metadata p {
- padding: 8px;
- list-style: none;
+ padding: 8px;
+ list-style: none;
}
#file-metadata ul {
- padding-left: 28px;
- list-style-image: url(images/page_green.png);
+ padding-left: 28px;
+ list-style-image: url(images/page_green.png);
}
dl.svninfo {
- color: #666;
- margin: 0;
+ color: #666;
+ margin: 0;
}
dl.svninfo dt {
- font-weight: bold;
+ font-weight: bold;
}
ul.link-list li {
- white-space: nowrap;
+ white-space: nowrap;
}
ul.link-list .type {
- font-size: 8px;
- text-transform: uppercase;
- color: white;
- background: #969696;
- padding: 2px 4px;
- -webkit-border-radius: 5px;
+ font-size: 8px;
+ text-transform: uppercase;
+ color: white;
+ background: #969696;
+ padding: 2px 4px;
+ -webkit-border-radius: 5px;
}
/* @end */
@@ -217,7 +217,7 @@ ul.link-list .type {
/* @group Project Metadata Section */
#project-metadata {
- margin-top: 3em;
+ margin-top: 3em;
}
.file #project-metadata {
@@ -225,34 +225,34 @@ ul.link-list .type {
}
#project-metadata .section {
- border: 1px solid #aaa;
+ border: 1px solid #aaa;
}
#project-metadata h3.section-header {
- border-bottom: 1px solid #aaa;
- position: relative;
+ border-bottom: 1px solid #aaa;
+ position: relative;
}
#project-metadata h3.section-header .search-toggle {
- position: absolute;
- right: 5px;
+ position: absolute;
+ right: 5px;
}
#project-metadata form {
- color: #777;
- background: #ccc;
- padding: 8px 8px 16px;
- border-bottom: 1px solid #bbb;
+ color: #777;
+ background: #ccc;
+ padding: 8px 8px 16px;
+ border-bottom: 1px solid #bbb;
}
#project-metadata fieldset {
- border: 0;
+ border: 0;
}
#no-class-search-results {
- margin: 0 auto 1em;
- text-align: center;
- font-size: 14px;
- font-weight: bold;
- color: #aaa;
+ margin: 0 auto 1em;
+ text-align: center;
+ font-size: 14px;
+ font-weight: bold;
+ color: #aaa;
}
/* @end */
@@ -260,12 +260,12 @@ ul.link-list .type {
/* @group Documentation Section */
#description {
- font-size: 100%;
- color: #333;
+ font-size: 100%;
+ color: #333;
}
#description p {
- margin: 1em 0.4em;
+ margin: 1em 0.4em;
}
#description li p {
@@ -273,152 +273,152 @@ ul.link-list .type {
}
#description ul {
- margin-left: 1.5em;
+ margin-left: 1.5em;
}
#description ul li {
- line-height: 1.4em;
+ line-height: 1.4em;
}
#description dl,
#documentation dl {
- margin: 8px 1.5em;
- border: 1px solid #ccc;
+ margin: 8px 1.5em;
+ border: 1px solid #ccc;
}
#description dl {
- font-size: 14px;
+ font-size: 14px;
}
#description dt,
#documentation dt {
- padding: 2px 4px;
- font-weight: bold;
- background: #ddd;
+ padding: 2px 4px;
+ font-weight: bold;
+ background: #ddd;
}
#description dd,
#documentation dd {
- padding: 2px 12px;
+ padding: 2px 12px;
}
#description dd + dt,
#documentation dd + dt {
- margin-top: 0.7em;
+ margin-top: 0.7em;
}
#documentation .section {
- font-size: 90%;
+ font-size: 90%;
}
#documentation h3.section-header {
- margin-top: 2em;
- padding: 0.75em 0.5em;
- background-color: #dedede;
- color: #333;
- font-size: 150%;
- border: 1px solid #bbb;
- -moz-border-radius: 3px;
- -webkit-border-radius: 3px;
+ margin-top: 2em;
+ padding: 0.75em 0.5em;
+ background-color: #dedede;
+ color: #333;
+ font-size: 150%;
+ border: 1px solid #bbb;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
}
#constants-list > dl,
#attributes-list > dl {
- margin: 1em 0 2em;
- border: 0;
+ margin: 1em 0 2em;
+ border: 0;
}
#constants-list > dl dt,
#attributes-list > dl dt {
- padding-left: 0;
- font-weight: bold;
- font-family: Monaco, "Andale Mono";
- background: inherit;
+ padding-left: 0;
+ font-weight: bold;
+ font-family: Monaco, "Andale Mono";
+ background: inherit;
}
#constants-list > dl dt a,
#attributes-list > dl dt a {
- color: inherit;
+ color: inherit;
}
#constants-list > dl dd,
#attributes-list > dl dd {
- margin: 0 0 1em 0;
- padding: 0;
- color: #666;
+ margin: 0 0 1em 0;
+ padding: 0;
+ color: #666;
}
/* @group Method Details */
#documentation .method-source-code {
- display: none;
+ display: none;
}
#documentation .method-detail {
- margin: 0.5em 0;
- padding: 0.5em 0;
- cursor: pointer;
+ margin: 0.5em 0;
+ padding: 0.5em 0;
+ cursor: pointer;
}
#documentation .method-detail:hover {
- background-color: #f1edba;
+ background-color: #f1edba;
}
#documentation .method-heading {
- position: relative;
- padding: 2px 4px 0 20px;
- font-size: 125%;
- font-weight: bold;
- color: #333;
- background: url(images/brick.png) no-repeat left bottom;
+ position: relative;
+ padding: 2px 4px 0 20px;
+ font-size: 125%;
+ font-weight: bold;
+ color: #333;
+ background: url(images/brick.png) no-repeat left bottom;
}
#documentation .method-heading :link,
#documentation .method-heading :visited {
- color: inherit;
+ color: inherit;
}
#documentation .method-click-advice {
- position: absolute;
- top: 2px;
- right: 5px;
- font-size: 10px;
- color: #9b9877;
- visibility: hidden;
- padding-right: 20px;
- line-height: 20px;
- background: url(images/zoom.png) no-repeat right top;
+ position: absolute;
+ top: 2px;
+ right: 5px;
+ font-size: 10px;
+ color: #9b9877;
+ visibility: hidden;
+ padding-right: 20px;
+ line-height: 20px;
+ background: url(images/zoom.png) no-repeat right top;
}
#documentation .method-detail:hover .method-click-advice {
- visibility: visible;
+ visibility: visible;
}
#documentation .method-alias .method-heading {
- color: #666;
- background: url(images/brick_link.png) no-repeat left bottom;
+ color: #666;
+ background: url(images/brick_link.png) no-repeat left bottom;
}
#documentation .method-description,
#documentation .aliases {
- margin: 0 20px;
- line-height: 1.2em;
- color: #666;
+ margin: 0 20px;
+ line-height: 1.2em;
+ color: #666;
}
#documentation .aliases {
- padding-top: 4px;
- font-style: italic;
- cursor: default;
+ padding-top: 4px;
+ font-style: italic;
+ cursor: default;
}
#documentation .method-description p {
- padding: 0;
+ padding: 0;
}
#documentation .method-description p + p {
- margin-bottom: 0.5em;
+ margin-bottom: 0.5em;
}
#documentation .method-description ul {
margin-left: 1.5em;
}
#documentation .attribute-method-heading {
- background: url(images/tag_green.png) no-repeat left bottom;
+ background: url(images/tag_green.png) no-repeat left bottom;
}
#documentation #attribute-method-details .method-detail:hover {
- background-color: transparent;
- cursor: default;
+ background-color: transparent;
+ cursor: default;
}
#documentation .attribute-access-type {
- font-size: 60%;
- text-transform: uppercase;
- vertical-align: super;
- padding: 0 2px;
+ font-size: 60%;
+ text-transform: uppercase;
+ vertical-align: super;
+ padding: 0 2px;
}
/* @end */
@@ -429,19 +429,19 @@ ul.link-list .type {
/* @group Source Code */
div.method-source-code {
- background: #262626;
- color: #efefef;
- margin: 1em;
- padding: 0.5em;
- border: 1px dashed #999;
- overflow: hidden;
+ background: #262626;
+ color: #efefef;
+ margin: 1em;
+ padding: 0.5em;
+ border: 1px dashed #999;
+ overflow: hidden;
}
div.method-source-code pre {
- background: inherit;
- padding: 0;
- color: white;
- overflow: auto;
+ background: inherit;
+ padding: 0;
+ color: white;
+ overflow: auto;
}
/* @group Ruby keyword styles */
@@ -467,51 +467,51 @@ div.method-source-code pre {
}
.file-popup dl {
- font-size: 80%;
- padding: 0.75em;
- background-color: #dedede;
- color: #333;
- border: 1px solid #bbb;
- -moz-border-radius: 3px;
- -webkit-border-radius: 3px;
+ font-size: 80%;
+ padding: 0.75em;
+ background-color: #dedede;
+ color: #333;
+ border: 1px solid #bbb;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
}
.file dt {
- font-weight: bold;
- padding-left: 22px;
- line-height: 20px;
- background: url(images/page_white_width.png) no-repeat left top;
+ font-weight: bold;
+ padding-left: 22px;
+ line-height: 20px;
+ background: url(images/page_white_width.png) no-repeat left top;
}
.file dt.modified-date {
- background: url(images/date.png) no-repeat left top;
+ background: url(images/date.png) no-repeat left top;
}
.file dt.requires {
- background: url(images/plugin.png) no-repeat left top;
+ background: url(images/plugin.png) no-repeat left top;
}
.file dt.scs-url {
- background: url(images/wrench.png) no-repeat left top;
+ background: url(images/wrench.png) no-repeat left top;
}
.file dl dd {
- margin: 0 0 1em 0;
+ margin: 0 0 1em 0;
}
.file #metadata dl dd ul {
- list-style: circle;
- margin-left: 20px;
- padding-top: 0;
+ list-style: circle;
+ margin-left: 20px;
+ padding-top: 0;
}
.file #metadata dl dd ul li {
}
.file h2 {
- margin-top: 2em;
- padding: 0.75em 0.5em;
- background-color: #dedede;
- color: #333;
- font-size: 120%;
- border: 1px solid #bbb;
- -moz-border-radius: 3px;
- -webkit-border-radius: 3px;
+ margin-top: 2em;
+ padding: 0.75em 0.5em;
+ background-color: #dedede;
+ color: #333;
+ font-size: 120%;
+ border: 1px solid #bbb;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
}
/* @end */
@@ -521,13 +521,13 @@ div.method-source-code pre {
/* @group ThickBox Styles */
#TB_window {
- font: 12px Arial, Helvetica, sans-serif;
- color: #333333;
+ font: 12px Arial, Helvetica, sans-serif;
+ color: #333333;
}
#TB_secondLine {
- font: 10px Arial, Helvetica, sans-serif;
- color:#666666;
+ font: 10px Arial, Helvetica, sans-serif;
+ color:#666666;
}
#TB_window :link,
@@ -540,147 +540,147 @@ div.method-source-code pre {
#TB_window :visited:focus { color: #666666; }
#TB_overlay {
- position: fixed;
- z-index:100;
- top: 0px;
- left: 0px;
- height:100%;
- width:100%;
+ position: fixed;
+ z-index:100;
+ top: 0px;
+ left: 0px;
+ height:100%;
+ width:100%;
}
.TB_overlayMacFFBGHack {background: url(images/macFFBgHack.png) repeat;}
.TB_overlayBG {
- background-color:#000;
- filter:alpha(opacity=75);
- -moz-opacity: 0.75;
- opacity: 0.75;
+ background-color:#000;
+ filter:alpha(opacity=75);
+ -moz-opacity: 0.75;
+ opacity: 0.75;
}
* html #TB_overlay { /* ie6 hack */
- position: absolute;
- height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px');
+ position: absolute;
+ height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px');
}
#TB_window {
- position: fixed;
- background: #ffffff;
- z-index: 102;
- color:#000000;
- display:none;
- border: 4px solid #525252;
- text-align:left;
- top:50%;
- left:50%;
+ position: fixed;
+ background: #ffffff;
+ z-index: 102;
+ color:#000000;
+ display:none;
+ border: 4px solid #525252;
+ text-align:left;
+ top:50%;
+ left:50%;
}
* html #TB_window { /* ie6 hack */
-position: absolute;
-margin-top: expression(0 - parseInt(this.offsetHeight / 2) + (TBWindowMargin = document.documentElement && document.documentElement.scrollTop || document.body.scrollTop) + 'px');
+ position: absolute;
+ margin-top: expression(0 - parseInt(this.offsetHeight / 2) + (TBWindowMargin = document.documentElement && document.documentElement.scrollTop || document.body.scrollTop) + 'px');
}
#TB_window img#TB_Image {
- display:block;
- margin: 15px 0 0 15px;
- border-right: 1px solid #ccc;
- border-bottom: 1px solid #ccc;
- border-top: 1px solid #666;
- border-left: 1px solid #666;
+ display:block;
+ margin: 15px 0 0 15px;
+ border-right: 1px solid #ccc;
+ border-bottom: 1px solid #ccc;
+ border-top: 1px solid #666;
+ border-left: 1px solid #666;
}
#TB_caption{
- height:25px;
- padding:7px 30px 10px 25px;
- float:left;
+ height:25px;
+ padding:7px 30px 10px 25px;
+ float:left;
}
#TB_closeWindow{
- height:25px;
- padding:11px 25px 10px 0;
- float:right;
+ height:25px;
+ padding:11px 25px 10px 0;
+ float:right;
}
#TB_closeAjaxWindow{
- padding:7px 10px 5px 0;
- margin-bottom:1px;
- text-align:right;
- float:right;
+ padding:7px 10px 5px 0;
+ margin-bottom:1px;
+ text-align:right;
+ float:right;
}
#TB_ajaxWindowTitle{
- float:left;
- padding:7px 0 5px 10px;
- margin-bottom:1px;
- font-size: 22px;
+ float:left;
+ padding:7px 0 5px 10px;
+ margin-bottom:1px;
+ font-size: 22px;
}
#TB_title{
- background-color: #6C8C22;
- color: #dedede;
- height:40px;
+ background-color: #6C8C22;
+ color: #dedede;
+ height:40px;
}
#TB_title :link,
#TB_title :visited {
- color: white !important;
- border-bottom: 1px dotted #dedede;
+ color: white !important;
+ border-bottom: 1px dotted #dedede;
}
#TB_ajaxContent{
- clear:both;
- padding:2px 15px 15px 15px;
- overflow:auto;
- text-align:left;
- line-height:1.4em;
+ clear:both;
+ padding:2px 15px 15px 15px;
+ overflow:auto;
+ text-align:left;
+ line-height:1.4em;
}
#TB_ajaxContent.TB_modal{
- padding:15px;
+ padding:15px;
}
#TB_ajaxContent p{
- padding:5px 0px 5px 0px;
+ padding:5px 0px 5px 0px;
}
#TB_load{
- position: fixed;
- display:none;
- height:13px;
- width:208px;
- z-index:103;
- top: 50%;
- left: 50%;
- margin: -6px 0 0 -104px; /* -height/2 0 0 -width/2 */
+ position: fixed;
+ display:none;
+ height:13px;
+ width:208px;
+ z-index:103;
+ top: 50%;
+ left: 50%;
+ margin: -6px 0 0 -104px; /* -height/2 0 0 -width/2 */
}
* html #TB_load { /* ie6 hack */
-position: absolute;
-margin-top: expression(0 - parseInt(this.offsetHeight / 2) + (TBWindowMargin = document.documentElement && document.documentElement.scrollTop || document.body.scrollTop) + 'px');
+ position: absolute;
+ margin-top: expression(0 - parseInt(this.offsetHeight / 2) + (TBWindowMargin = document.documentElement && document.documentElement.scrollTop || document.body.scrollTop) + 'px');
}
#TB_HideSelect{
- z-index:99;
- position:fixed;
- top: 0;
- left: 0;
- background-color:#fff;
- border:none;
- filter:alpha(opacity=0);
- -moz-opacity: 0;
- opacity: 0;
- height:100%;
- width:100%;
+ z-index:99;
+ position:fixed;
+ top: 0;
+ left: 0;
+ background-color:#fff;
+ border:none;
+ filter:alpha(opacity=0);
+ -moz-opacity: 0;
+ opacity: 0;
+ height:100%;
+ width:100%;
}
* html #TB_HideSelect { /* ie6 hack */
- position: absolute;
- height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px');
+ position: absolute;
+ height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px');
}
#TB_iframeContent{
- clear:both;
- border:none;
- margin-bottom:-1px;
- margin-top:1px;
- _margin-bottom:1px;
+ clear:both;
+ border:none;
+ margin-bottom:-1px;
+ margin-top:1px;
+ _margin-bottom:1px;
}
/* @end */
@@ -688,17 +688,17 @@ margin-top: expression(0 - parseInt(this.offsetHeight / 2) + (TBWindowMargin = d
/* @group Debugging Section */
#debugging-toggle {
- text-align: center;
+ text-align: center;
}
#debugging-toggle img {
- cursor: pointer;
+ cursor: pointer;
}
#rdoc-debugging-section-dump {
- display: none;
- margin: 0 2em 2em;
- background: #ccc;
- border: 1px solid #999;
+ display: none;
+ margin: 0 2em 2em;
+ background: #ccc;
+ border: 1px solid #999;
}
diff --git a/lib/rdoc/include.rb b/lib/rdoc/include.rb
index 11a9bdc7ef..9cebd3d8ef 100644
--- a/lib/rdoc/include.rb
+++ b/lib/rdoc/include.rb
@@ -17,6 +17,7 @@ class RDoc::Include < RDoc::CodeObject
super()
@name = name
self.comment = comment
+ @module = nil # cache for module if found
end
##
@@ -52,9 +53,47 @@ class RDoc::Include < RDoc::CodeObject
##
# Attempts to locate the included module object. Returns the name if not
# known.
+ #
+ # The scoping rules of Ruby to resolve the name of an included module are:
+ # - first look into the children of the current context;
+ # - if not found, look into the children of included modules,
+ # in reverse inclusion order;
+ # - if still not found, go up the hierarchy of names.
def module
- RDoc::TopLevel.find_module_named(@name) || @name
+ return @module if @module
+
+ # search the current context
+ return @name unless parent
+ full_name = parent.child_name(@name)
+ @module = RDoc::TopLevel.modules_hash[full_name]
+ return @module if @module
+ return @name if @name =~ /^::/
+
+ # search the includes before this one, in reverse order
+ searched = parent.includes.take_while { |i| i != self }.reverse
+ searched.each do |i|
+ inc = i.module
+ next if String === inc
+ full_name = inc.child_name(@name)
+ @module = RDoc::TopLevel.modules_hash[full_name]
+ return @module if @module
+ end
+
+ # go up the hierarchy of names
+ p = parent.parent
+ while p
+ full_name = p.child_name(@name)
+ @module = RDoc::TopLevel.modules_hash[full_name]
+ return @module if @module
+ p = p.parent
+ end
+
+ @name
+ end
+
+ def to_s # :nodoc:
+ "include #@name in: #{parent}"
end
end
diff --git a/lib/rdoc/markup.rb b/lib/rdoc/markup.rb
index 32c8179e0d..c8914fea11 100644
--- a/lib/rdoc/markup.rb
+++ b/lib/rdoc/markup.rb
@@ -59,12 +59,477 @@ require 'rdoc'
#
# puts "<body>#{wh.convert ARGF.read}</body>"
#
+# == Encoding
+#
+# Where Encoding support is available RDoc will automatically convert all
+# documents to the same output encoding. The output encoding can be set via
+# RDoc::Options#encoding and defaults to Encoding.default_external.
+#
+# = \RDoc Markup Reference
+#
+# == Block Markup
+#
+# === Paragraphs and Verbatim
+#
+# The markup engine looks for a document's natural left margin. This is
+# used as the initial margin for the document.
+#
+# Consecutive lines starting at this margin are considered to be a
+# paragraph. Empty lines separate paragraphs.
+#
+# Any line that starts to the right of the current margin is treated
+# as verbatim text. This is useful for code listings:
+#
+# 3.times { puts "Ruby" }
+#
+# In verbatim text, two or more blank lines are collapsed into one,
+# and trailing blank lines are removed:
+#
+# This is the first line
+#
+#
+# This is the second non-blank line,
+# after 2 blank lines in the source markup.
+#
+#
+# There were two trailing blank lines right above this paragraph, that
+# have been removed. In addition, the verbatim text has been shifted
+# left, so the amount of indentation of verbatim text is unimportant.
+#
+# === Headers and Rules
+#
+# A line starting with an equal sign (=) is treated as a
+# heading. Level one headings have one equals sign, level two headings
+# have two, and so on until level six, which is the maximum
+# (seven hyphens or more result in a level six heading).
+#
+# For example, the above header was obtained with:
+# == Headers and Rules
+#
+# A line starting with three or more hyphens (at the current indent)
+# generates a horizontal rule. The more hyphens, the thicker the rule
+# (within reason, and if supported by the output device).
+#
+# In the case of HTML output, three dashes generate a 1-pixel high rule,
+# four dashes result in 2 pixels, and so on. The actual height is limited
+# to 10 pixels:
+#
+# ---
+# -----
+# -----------------------------------------------------
+#
+# produces:
+#
+# ---
+# -----
+# -----------------------------------------------------
+#
+# === Simple Lists
+#
+# If a paragraph starts with a "*", "-", "<digit>." or "<letter>.",
+# then it is taken to be the start of a list. The margin in increased to be
+# the first non-space following the list start flag. Subsequent lines
+# should be indented to this new margin until the list ends. For example:
+#
+# * this is a list with three paragraphs in
+# the first item. This is the first paragraph.
+#
+# And this is the second paragraph.
+#
+# 1. This is an indented, numbered list.
+# 2. This is the second item in that list
+#
+# This is the third conventional paragraph in the
+# first list item.
+#
+# * This is the second item in the original list
+#
+# produces:
+#
+# * this is a list with three paragraphs in
+# the first item. This is the first paragraph.
+#
+# And this is the second paragraph.
+#
+# 1. This is an indented, numbered list.
+# 2. This is the second item in that list
+#
+# This is the third conventional paragraph in the
+# first list item.
+#
+# * This is the second item in the original list
+#
+# === Labeled Lists
+#
+# You can also construct labeled lists, sometimes called description
+# or definition lists. Do this by putting the label in square brackets
+# and indenting the list body:
+#
+# [cat] a small furry mammal
+# that seems to sleep a lot
+#
+# [ant] a little insect that is known
+# to enjoy picnics
+#
+# produces:
+#
+# [cat] a small furry mammal
+# that seems to sleep a lot
+#
+# [ant] a little insect that is known
+# to enjoy picnics
+#
+# If you want the list bodies to line up to the left of the labels,
+# use two colons:
+#
+# cat:: a small furry mammal
+# that seems to sleep a lot
+#
+# ant:: a little insect that is known
+# to enjoy picnics
+#
+# produces:
+#
+# cat:: a small furry mammal
+# that seems to sleep a lot
+#
+# ant:: a little insect that is known
+# to enjoy picnics
+#
+# Notice that blank lines right after the label are ignored in labeled lists:
+#
+# [one]
+#
+# definition 1
+#
+# [two]
+#
+# definition 2
+#
+# produces the same output as
+#
+# [one] definition 1
+# [two] definition 2
+#
+#
+# === Lists and Verbatim
+#
+# If you want to introduce a verbatim section right after a list, it has to be
+# less indented than the list item bodies, but more indented than the list
+# label, letter, digit or bullet. For instance:
+#
+# * point 1
+#
+# * point 2, first paragraph
+#
+# point 2, second paragraph
+# verbatim text inside point 2
+# point 2, third paragraph
+# verbatim text outside of the list (the list is therefore closed)
+# regular paragraph after the list
+#
+# produces:
+#
+# * point 1
+#
+# * point 2, first paragraph
+#
+# point 2, second paragraph
+# verbatim text inside point 2
+# point 2, third paragraph
+# verbatim text outside of the list (the list is therefore closed)
+# regular paragraph after the list
+#
+#
+# == Text Markup
+#
+# === Bold, Italic, Typewriter Text
+#
+# You can use markup within text (except verbatim) to change the
+# appearance of parts of that text. Out of the box, RDoc::Markup
+# supports word-based and general markup.
+#
+# Word-based markup uses flag characters around individual words:
+#
+# <tt>\*_word_\*</tt>:: displays _word_ in a *bold* font
+# <tt>\__word_\_</tt>:: displays _word_ in an _emphasized_ font
+# <tt>\+_word_\+</tt>:: displays _word_ in a +code+ font
+#
+# General markup affects text between a start delimiter and an end
+# delimiter. Not surprisingly, these delimiters look like HTML markup.
+#
+# <tt>\<b>_text_</b></tt>:: displays _text_ in a *bold* font
+# <tt>\<em>_text_</em></tt>:: displays _text_ in an _emphasized_ font
+# (alternate tag: <tt>\<i></tt>)
+# <tt>\<tt>_text_\</tt></tt>:: displays _text_ in a +code+ font
+# (alternate tag: <tt>\<code></tt>)
+#
+# Unlike conventional Wiki markup, general markup can cross line
+# boundaries. You can turn off the interpretation of markup by
+# preceding the first character with a backslash (see <i>Escaping
+# Text Markup</i>, below).
+#
+# === Hyperlinks
+#
+# Hyperlinks to the web starting with +http:+, +mailto:+, +ftp:+ or +www.+
+# are recognized. An HTTP url that references an external image file is
+# converted into an inline <img...>. Hyperlinks starting with +link:+ are
+# assumed to refer to local files whose path is relative to the <tt>--op</tt>
+# directory.
+#
+# Hyperlinks can also be of the form _label_[_url_], in which
+# case _label_ is used in the displayed text, and _url_ is
+# used as the target. If _label_ contains multiple words,
+# put it in braces: {<em>multi word label</em>}[url].
+#
+# Example hyperlinks:
+#
+# link:RDoc.html
+# http://rdoc.rubyforge.org
+# mailto:user@example.com
+# {RDoc Documentation}[http://rdoc.rubyforge.org]
+# {RDoc Markup}[link:RDoc/Markup.html]
+#
+# === Escaping Text Markup
+#
+# Text markup can be escaped with a backslash, as in \<tt>, which was obtained
+# with "<tt>\\<tt></tt>". Except in verbatim sections and between \<tt> tags,
+# to produce a backslash, you have to double it unless it is followed by a
+# space, tab or newline. Otherwise, the HTML formatter will discard it, as it
+# is used to escape potential hyperlinks:
+#
+# * The \ must be doubled if not followed by white space: \\.
+# * But not in \<tt> tags: in a Regexp, <tt>\S</tt> matches non-space.
+# * This is a link to {ruby-lang}[www.ruby-lang.org].
+# * This is not a link, however: \{ruby-lang.org}[www.ruby-lang.org].
+# * This will not be hyperlinked to \RDoc::RDoc#document
+#
+# generates:
+#
+# * The \ must be doubled if not followed by white space: \\.
+# * But not in \<tt> tags: in a Regexp, <tt>\S</tt> matches non-space.
+# * This is a link to {ruby-lang}[www.ruby-lang.org]
+# * This is not a link, however: \{ruby-lang.org}[www.ruby-lang.org]
+# * This will not be hyperlinked to \RDoc::RDoc#document
+#
+# Inside \<tt> tags, more precisely, leading backslashes are removed
+# only if followed by a markup character (<tt><*_+</tt>), a backslash,
+# or a known hyperlink reference (a known class or method). So in the
+# example above, the backslash of <tt>\S</tt> would be removed
+# if there was a class or module named +S+ in the current context.
+#
+# This behavior is inherited from RDoc version 1, and has been kept
+# for compatibility with existing RDoc documentation.
+#
+# === Conversion of characters
+#
+# HTML will convert two/three dashes to an em-dash. Other common characters are
+# converted as well:
+#
+# em-dash:: -- or ---
+# ellipsis:: ...
+#
+# single quotes:: 'text' or `text'
+# double quotes:: "text" or ``text''
+#
+# copyright:: (c)
+# registered trademark:: (r)
+#
+# produces:
+#
+# em-dash:: -- or ---
+# ellipsis:: ...
+#
+# single quotes:: 'text' or `text'
+# double quotes:: "text" or ``text''
+#
+# copyright:: (c)
+# registered trademark:: (r)
+#
+#
+# == Documenting Source Code
+#
+# Comment blocks can be written fairly naturally, either using <tt>#</tt> on
+# successive lines of the comment, or by including the comment in
+# a <tt>=begin</tt>/<tt>=end</tt> block. If you use the latter form,
+# the <tt>=begin</tt> 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 starting
+# with <tt>--</tt> right after the <tt>#</tt> character (otherwise,
+# it will be treated as a rule if it has three dashes or more).
+# 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 <tt>++</tt>.
+#
+# ##
+# # Extract the age and calculate the date-of-birth.
+# #--
+# # FIXME: fails if the birthday falls on February 29th
+# #++
+# # The DOB is returned as a Time object.
+#
+# def get_dob(person)
+# # ...
+# end
+#
+# Names of classes, files, and any method names containing an
+# underscore or preceded by a hash character are automatically hyperlinked
+# from comment text to their description. This hyperlinking works inside
+# the current class or module, and with ancestor methods (in included modules
+# or in the superclass).
+#
+# 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.
+#
+# RDoc automatically cross-references words with underscores or camel-case.
+# To suppress cross-references, prefix the word with a \ character. To
+# include special characters like "<tt>\n</tt>", you'll need to use
+# two \ characters in normal text, but only one in \<tt> text:
+#
+# "\\n" or "<tt>\n</tt>"
+#
+# produces:
+#
+# "\\n" or "<tt>\n</tt>"
+#
+# == Directives
+#
+# Directives are keywords surrounded by ":" characters.
+#
+# === Controlling what is documented
+#
+# [+:nodoc:+ / <tt>:nodoc: all</tt>]
+# This directive prevents documentation for the element from
+# being generated. For classes and modules, the methods, aliases,
+# constants, and attributes directly within the affected class or
+# module also will be omitted. By default, though, modules and
+# classes within that class of module _will_ be documented. This is
+# turned off by adding the +all+ modifier.
+#
+# module MyModule # :nodoc:
+# class Input
+# end
+# end
+#
+# module OtherModule # :nodoc: all
+# class Output
+# end
+# end
+#
+# In the above code, only class <tt>MyModule::Input</tt> will be documented.
+#
+# The +:nodoc:+ directive, like +:enddoc:+, +:stopdoc:+ and +:startdoc:+
+# presented below, is local to the current file: if you do not want to
+# document a module that appears in several files, specify +:nodoc:+ on each
+# appearance, at least once per file.
+#
+# [+: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 rest of the current file.
+#
+# [+:doc:+]
+# Forces a method or attribute to be documented even if it wouldn't be
+# otherwise. Useful if, for example, you want to include documentation of a
+# particular private method.
+#
+# [+:enddoc:+]
+# Document nothing further at the current level: directives +:startdoc:+ and
+# +:doc:+ that appear after this will not be honored for the current container
+# (file, class or module), in the current file.
+#
+# [+:notnew:+ / +:not_new:+ / +:not-new:+ ]
+# Only applicable to the +initialize+ instance method. Normally RDoc
+# assumes that the documentation and parameters for +initialize+ are
+# actually for the +new+ method, and so fakes out a +new+ for the class.
+# The +:notnew:+ directive stops this. Remember that +initialize+ is private,
+# so you won't see the documentation unless you use the +-a+ command line
+# option.
+#
+# === Other directives
+#
+# [+:include:+ _filename_]
+# Include the contents of the named file at this point. This directive
+# must appear alone on one line, possibly preceded by spaces. In this
+# position, it can be escapd with a \ in front of the first colon.
+#
+# 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.
+#
+# [+: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).
+#
+# [+:main:+ _name_]
+# Equivalent to the <tt>--main</tt> command line parameter.
+#
+# [<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
+# the section is used as introductory text. Subsequent methods, aliases,
+# attributes, and classes will be documented in this section. A :section:
+# comment block may have one or more lines before the :section: directive.
+# These will be removed, and any identical lines at the end of the block are
+# also removed. This allows you to add visual cues such as:
+#
+# # ----------------------------------------
+# # :section: My Section
+# # This is the section that I wrote.
+# # See it glisten in the noon-day sun.
+# # ----------------------------------------
+#
+# <i>Note: Current formatters to not take sections into account.</i>
+#
+# [+: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.
+#
+# Further directives can be found in RDoc::Parser::Ruby and RDoc::Parser::C.
#--
-# Author:: Dave Thomas, dave@pragmaticprogrammer.com
-# License:: Ruby license
+# Original Author:: Dave Thomas, dave@pragmaticprogrammer.com
+# License:: Ruby license
class RDoc::Markup
+ ##
+ # An AttributeManager which handles inline markup.
+
attr_reader :attribute_manager
##
diff --git a/lib/rdoc/markup/attribute_manager.rb b/lib/rdoc/markup/attribute_manager.rb
index e86e7f6812..2ee243ab0b 100644
--- a/lib/rdoc/markup/attribute_manager.rb
+++ b/lib/rdoc/markup/attribute_manager.rb
@@ -15,9 +15,12 @@ class RDoc::Markup::AttributeManager
# optimistic
#++
- A_PROTECT = 004 # :nodoc:
+ A_PROTECT = 004 # :nodoc:
- PROTECT_ATTR = A_PROTECT.chr # :nodoc:
+ ##
+ # Special mask character to prevent inline markup handling
+
+ PROTECT_ATTR = A_PROTECT.chr # :nodoc:
##
# This maps delimiters that occur around words (such as *bold* or +tt+)
@@ -56,7 +59,7 @@ class RDoc::Markup::AttributeManager
def initialize
@html_tags = {}
@matching_word_pairs = {}
- @protectable = %w[<\\]
+ @protectable = %w[<]
@special = {}
@word_pair_map = {}
@@ -79,12 +82,19 @@ class RDoc::Markup::AttributeManager
RDoc::Markup::AttrChanger.new turn_on, turn_off
end
- def change_attribute(current, new)
+ ##
+ # Changes the current attribute from +current+ to +new+
+
+ def change_attribute current, new
diff = current ^ new
attribute(new & diff, current & diff)
end
- def changed_attribute_by_name(current_set, new_set)
+ ##
+ # Used by the tests to change attributes by name from +current_set+ to
+ # +new_set+
+
+ def changed_attribute_by_name current_set, new_set
current = new = 0
current_set.each do |name|
current |= RDoc::Markup::Attribute.bitmap_for(name)
@@ -97,6 +107,9 @@ class RDoc::Markup::AttributeManager
change_attribute(current, new)
end
+ ##
+ # Copies +start_pos+ to +end_pos+ from the current string
+
def copy_string(start_pos, end_pos)
res = @str[start_pos...end_pos]
res.gsub!(/\000/, '')
@@ -112,7 +125,7 @@ class RDoc::Markup::AttributeManager
# first do matching ones
tags = @matching_word_pairs.keys.join("")
- re = /(^|[^\w#{NULL}])([#{tags}])([#:\\]?[\w.\/-]+?\S?)\2(\W|$)/
+ re = /(^|\W)([#{tags}])([#:\\]?[\w.\/-]+?\S?)\2(\W|$)/
1 while str.gsub!(re) do
attr = @matching_word_pairs[$2]
@@ -164,6 +177,9 @@ class RDoc::Markup::AttributeManager
# Escapes special sequences of text to prevent conversion to RDoc
def mask_protected_sequences
+ # protect __send__, __FILE__, etc.
+ @str.gsub!(/__([a-z]+)__/i,
+ "_#{PROTECT_ATTR}_#{PROTECT_ATTR}\\1_#{PROTECT_ATTR}_#{PROTECT_ATTR}")
@str.gsub!(/\\([#{Regexp.escape @protectable.join('')}])/,
"\\1#{PROTECT_ATTR}")
end
@@ -228,8 +244,8 @@ class RDoc::Markup::AttributeManager
@attrs = RDoc::Markup::AttrSpan.new @str.length
- convert_html @str, @attrs
convert_attrs @str, @attrs
+ convert_html @str, @attrs
convert_specials @str, @attrs
unmask_protected_sequences
@@ -262,6 +278,9 @@ class RDoc::Markup::AttributeManager
end
end
+ ##
+ # Splits the string into chunks by attribute change
+
def split_into_flow
res = []
current_attr = 0
diff --git a/lib/rdoc/markup/blank_line.rb b/lib/rdoc/markup/blank_line.rb
index a8c07c8e57..5da0ac8d81 100644
--- a/lib/rdoc/markup/blank_line.rb
+++ b/lib/rdoc/markup/blank_line.rb
@@ -1,12 +1,20 @@
##
-# An empty line
+# An empty line. This class is a singleton.
class RDoc::Markup::BlankLine
- def == other # :nodoc:
- self.class == other.class
+ @instance = new
+
+ ##
+ # RDoc::Markup::BlankLine is a singleton
+
+ def self.new
+ @instance
end
+ ##
+ # Calls #accept_blank_line on +visitor+
+
def accept visitor
visitor.accept_blank_line self
end
diff --git a/lib/rdoc/markup/document.rb b/lib/rdoc/markup/document.rb
index 7963e9afe1..688e8e822e 100644
--- a/lib/rdoc/markup/document.rb
+++ b/lib/rdoc/markup/document.rb
@@ -39,6 +39,9 @@ class RDoc::Markup::Document
self.class == other.class and @parts == other.parts
end
+ ##
+ # Runs this document and all its #items through +visitor+
+
def accept visitor
visitor.start_accepting
@@ -49,6 +52,9 @@ class RDoc::Markup::Document
visitor.end_accepting
end
+ ##
+ # Does this document have no parts?
+
def empty?
@parts.empty?
end
diff --git a/lib/rdoc/markup/formatter.rb b/lib/rdoc/markup/formatter.rb
index 993e523f0c..9308954de1 100644
--- a/lib/rdoc/markup/formatter.rb
+++ b/lib/rdoc/markup/formatter.rb
@@ -7,6 +7,10 @@ require 'rdoc/markup'
class RDoc::Markup::Formatter
+ ##
+ # Tag for inline markup containing a +bit+ for the bitmask and the +on+ and
+ # +off+ triggers.
+
InlineTag = Struct.new(:bit, :on, :off)
##
@@ -101,6 +105,9 @@ class RDoc::Markup::Formatter
@in_tt > 0
end
+ ##
+ # Turns on tags for +item+ on +res+
+
def on_tags res, item
attr_mask = item.turn_on
return if attr_mask.zero?
@@ -113,6 +120,9 @@ class RDoc::Markup::Formatter
end
end
+ ##
+ # Turns off tags for +item+ on +res+
+
def off_tags res, item
attr_mask = item.turn_off
return if attr_mask.zero?
diff --git a/lib/rdoc/markup/formatter_test_case.rb b/lib/rdoc/markup/formatter_test_case.rb
index 26c8d63332..dd755c55d1 100644
--- a/lib/rdoc/markup/formatter_test_case.rb
+++ b/lib/rdoc/markup/formatter_test_case.rb
@@ -4,14 +4,57 @@ require 'rdoc/markup/formatter'
##
# Test case for creating new RDoc::Markup formatters. See
# test/test_rdoc_markup_to_*.rb for examples.
+#
+# This test case adds a variety of tests to your subclass when
+# #add_visitor_tests is called. Most tests set up a scenario then call a
+# method you will provide to perform the assertion on the output.
+#
+# Your subclass must instantiate a visitor and assign it to <tt>@to</tt>.
+#
+# For example, test_accept_blank_line sets up a RDoc::Markup::BlockLine then
+# calls accept_blank_line on your visitor. You are responsible for asserting
+# that the output is correct.
+#
+# Example:
+#
+# class TestRDocMarkupToNewFormat < RDoc::Markup::FormatterTestCase
+#
+# add_visitor_tests
+#
+# def setup
+# super
+#
+# @to = RDoc::Markup::ToNewFormat.new
+# end
+#
+# def accept_blank_line
+# assert_equal :junk, @to.res.join
+# end
+#
+# # ...
+#
+# end
class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
+ ##
+ # Call #setup when inheriting from this test case.
+ #
+ # Provides the following instance variables:
+ #
+ # +@m+:: RDoc::Markup.new
+ # +@RM+:: RDoc::Markup # to reduce typing
+ # +@bullet_list+:: @RM::List.new :BULLET, # ...
+ # +@label_list+:: @RM::List.new :LABEL, # ...
+ # +@lalpha_list+:: @RM::List.new :LALPHA, # ...
+ # +@note_list+:: @RM::List.new :NOTE, # ...
+ # +@number_list+:: @RM::List.new :NUMBER, # ...
+ # +@ualpha_list+:: @RM::List.new :UALPHA, # ...
+
def setup
super
@m = RDoc::Markup.new
- @am = RDoc::Markup::AttributeManager.new
@RM = RDoc::Markup
@bullet_list = @RM::List.new(:BULLET,
@@ -39,14 +82,25 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
@RM::ListItem.new(nil, @RM::Paragraph.new('l2')))
end
+ ##
+ # Call to add the visitor tests to your test case
+
def self.add_visitor_tests
self.class_eval do
+
+ ##
+ # Calls start_accepting which needs to verify startup state
+
def test_start_accepting
@to.start_accepting
start_accepting
end
+ ##
+ # Calls end_accepting on your test case which needs to call
+ # <tt>@to.end_accepting</tt> and verify document generation
+
def test_end_accepting
@to.start_accepting
@to.res << 'hi'
@@ -54,6 +108,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
end_accepting
end
+ ##
+ # Calls accept_blank_line
+
def test_accept_blank_line
@to.start_accepting
@@ -62,6 +119,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_blank_line
end
+ ##
+ # Calls accept_heading with a level 5 RDoc::Markup::Heading
+
def test_accept_heading
@to.start_accepting
@@ -70,6 +130,79 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_heading
end
+ ##
+ # Calls accept_heading_1 with a level 1 RDoc::Markup::Heading
+
+ def test_accept_heading_1
+ @to.start_accepting
+
+ @to.accept_heading @RM::Heading.new(1, 'Hello')
+
+ accept_heading_1
+ end
+
+ ##
+ # Calls accept_heading_2 with a level 2 RDoc::Markup::Heading
+
+ def test_accept_heading_2
+ @to.start_accepting
+
+ @to.accept_heading @RM::Heading.new(2, 'Hello')
+
+ accept_heading_2
+ end
+
+ ##
+ # Calls accept_heading_3 with a level 3 RDoc::Markup::Heading
+
+ def test_accept_heading_3
+ # HACK this doesn't belong here
+ skip "No String#chars, upgrade your ruby" unless ''.respond_to? :chars
+
+ @to.start_accepting
+
+ @to.accept_heading @RM::Heading.new(3, 'Hello')
+
+ accept_heading_3
+ end
+
+ ##
+ # Calls accept_heading_4 with a level 4 RDoc::Markup::Heading
+
+ def test_accept_heading_4
+ @to.start_accepting
+
+ @to.accept_heading @RM::Heading.new(4, 'Hello')
+
+ accept_heading_4
+ end
+
+ ##
+ # Calls accept_heading_b with a bold level 1 RDoc::Markup::Heading
+
+ def test_accept_heading_b
+ @to.start_accepting
+
+ @to.accept_heading @RM::Heading.new(1, '*Hello*')
+
+ accept_heading_b
+ end
+
+ ##
+ # Calls accept_heading_suppressed_crossref with a level 1
+ # RDoc::Markup::Heading containing a suppressed crossref
+
+ def test_accept_heading_suppressed_crossref # HACK to_html_crossref test
+ @to.start_accepting
+
+ @to.accept_heading @RM::Heading.new(1, '\\Hello')
+
+ accept_heading_suppressed_crossref
+ end
+
+ ##
+ # Calls accept_paragraph
+
def test_accept_paragraph
@to.start_accepting
@@ -78,15 +211,80 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_paragraph
end
+ ##
+ # Calls accept_paragraph_b with a RDoc::Markup::Paragraph containing
+ # bold words
+
+ def test_accept_paragraph_b
+ @to.start_accepting
+
+ @to.accept_paragraph @RM::Paragraph.new('reg <b>bold words</b> reg')
+
+ accept_paragraph_b
+ end
+
+ ##
+ # Calls accept_paragraph_i with a RDoc::Markup::Paragraph containing
+ # emphasized words
+
+ def test_accept_paragraph_i
+ @to.start_accepting
+
+ @to.accept_paragraph @RM::Paragraph.new('reg <em>italic words</em> reg')
+
+ accept_paragraph_i
+ end
+
+ ##
+ # Calls accept_paragraph_plus with a RDoc::Markup::Paragraph containing
+ # teletype words
+
+ def test_accept_paragraph_plus
+ @to.start_accepting
+
+ @to.accept_paragraph @RM::Paragraph.new('reg +teletype+ reg')
+
+ accept_paragraph_plus
+ end
+
+ ##
+ # Calls accept_paragraph_star with a RDoc::Markup::Paragraph containing
+ # bold words
+
+ def test_accept_paragraph_star
+ @to.start_accepting
+
+ @to.accept_paragraph @RM::Paragraph.new('reg *bold* reg')
+
+ accept_paragraph_star
+ end
+
+ ##
+ # Calls accept_paragraph_underscore with a RDoc::Markup::Paragraph
+ # containing emphasized words
+
+ def test_accept_paragraph_underscore
+ @to.start_accepting
+
+ @to.accept_paragraph @RM::Paragraph.new('reg _italic_ reg')
+
+ accept_paragraph_underscore
+ end
+
+ ##
+ # Calls accept_verbatim with a RDoc::Markup::Verbatim
+
def test_accept_verbatim
@to.start_accepting
- @to.accept_verbatim @RM::Verbatim.new(' ', 'hi', "\n",
- ' ', 'world', "\n")
+ @to.accept_verbatim @RM::Verbatim.new("hi\n", " world\n")
accept_verbatim
end
+ ##
+ # Calls accept_raw with a RDoc::Markup::Raw
+
def test_accept_raw
@to.start_accepting
@@ -99,6 +297,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_raw
end
+ ##
+ # Calls accept_rule with a RDoc::Markup::Rule
+
def test_accept_rule
@to.start_accepting
@@ -107,6 +308,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_rule
end
+ ##
+ # Calls accept_list_item_start_bullet
+
def test_accept_list_item_start_bullet
@to.start_accepting
@@ -117,6 +321,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_item_start_bullet
end
+ ##
+ # Calls accept_list_item_start_label
+
def test_accept_list_item_start_label
@to.start_accepting
@@ -127,6 +334,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_item_start_label
end
+ ##
+ # Calls accept_list_item_start_lalpha
+
def test_accept_list_item_start_lalpha
@to.start_accepting
@@ -137,6 +347,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_item_start_lalpha
end
+ ##
+ # Calls accept_list_item_start_note
+
def test_accept_list_item_start_note
@to.start_accepting
@@ -147,6 +360,26 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_item_start_note
end
+ ##
+ # Calls accept_list_item_start_note_2
+
+ def test_accept_list_item_start_note_2
+ list = @RM::List.new(:NOTE,
+ @RM::ListItem.new('<tt>teletype</tt>',
+ @RM::Paragraph.new('teletype description')))
+
+ @to.start_accepting
+
+ list.accept @to
+
+ @to.end_accepting
+
+ accept_list_item_start_note_2
+ end
+
+ ##
+ # Calls accept_list_item_start_number
+
def test_accept_list_item_start_number
@to.start_accepting
@@ -157,6 +390,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_item_start_number
end
+ ##
+ # Calls accept_list_item_start_ualpha
+
def test_accept_list_item_start_ualpha
@to.start_accepting
@@ -167,6 +403,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_item_start_ualpha
end
+ ##
+ # Calls accept_list_item_end_bullet
+
def test_accept_list_item_end_bullet
@to.start_accepting
@@ -179,6 +418,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_item_end_bullet
end
+ ##
+ # Calls accept_list_item_end_label
+
def test_accept_list_item_end_label
@to.start_accepting
@@ -191,6 +433,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_item_end_label
end
+ ##
+ # Calls accept_list_item_end_lalpha
+
def test_accept_list_item_end_lalpha
@to.start_accepting
@@ -203,6 +448,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_item_end_lalpha
end
+ ##
+ # Calls accept_list_item_end_note
+
def test_accept_list_item_end_note
@to.start_accepting
@@ -215,6 +463,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_item_end_note
end
+ ##
+ # Calls accept_list_item_end_number
+
def test_accept_list_item_end_number
@to.start_accepting
@@ -227,6 +478,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_item_end_number
end
+ ##
+ # Calls accept_list_item_end_ualpha
+
def test_accept_list_item_end_ualpha
@to.start_accepting
@@ -239,6 +493,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_item_end_ualpha
end
+ ##
+ # Calls accept_list_start_bullet
+
def test_accept_list_start_bullet
@to.start_accepting
@@ -247,6 +504,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_start_bullet
end
+ ##
+ # Calls accept_list_start_label
+
def test_accept_list_start_label
@to.start_accepting
@@ -255,6 +515,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_start_label
end
+ ##
+ # Calls accept_list_start_lalpha
+
def test_accept_list_start_lalpha
@to.start_accepting
@@ -263,6 +526,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_start_lalpha
end
+ ##
+ # Calls accept_list_start_note
+
def test_accept_list_start_note
@to.start_accepting
@@ -271,6 +537,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_start_note
end
+ ##
+ # Calls accept_list_start_number
+
def test_accept_list_start_number
@to.start_accepting
@@ -279,6 +548,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_start_number
end
+ ##
+ # Calls accept_list_start_ualpha
+
def test_accept_list_start_ualpha
@to.start_accepting
@@ -287,6 +559,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_start_ualpha
end
+ ##
+ # Calls accept_list_end_bullet
+
def test_accept_list_end_bullet
@to.start_accepting
@@ -297,6 +572,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_end_bullet
end
+ ##
+ # Calls accept_list_end_label
+
def test_accept_list_end_label
@to.start_accepting
@@ -307,6 +585,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_end_label
end
+ ##
+ # Calls accept_list_end_lalpha
+
def test_accept_list_end_lalpha
@to.start_accepting
@@ -317,6 +598,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_end_lalpha
end
+ ##
+ # Calls accept_list_end_number
+
def test_accept_list_end_number
@to.start_accepting
@@ -327,6 +611,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_end_number
end
+ ##
+ # Calls accept_list_end_note
+
def test_accept_list_end_note
@to.start_accepting
@@ -337,6 +624,9 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_end_note
end
+ ##
+ # Calls accept_list_end_ulpha
+
def test_accept_list_end_ualpha
@to.start_accepting
@@ -346,6 +636,52 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase
accept_list_end_ualpha
end
+
+ ##
+ # Calls list_nested with a two-level list
+
+ def test_list_nested
+ doc = @RM::Document.new(
+ @RM::List.new(:BULLET,
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('l1'),
+ @RM::List.new(:BULLET,
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('l1.1')))),
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('l2'))))
+
+ doc.accept @to
+
+ list_nested
+ end
+
+ ##
+ # Calls list_verbatim with a list containing a verbatim block
+
+ def test_list_verbatim # HACK overblown
+ doc = @RM::Document.new(
+ @RM::List.new(:BULLET,
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('list', 'stuff'),
+ @RM::BlankLine.new,
+ @RM::Verbatim.new("* list\n",
+ " with\n",
+ "\n",
+ " second\n",
+ "\n",
+ " 1. indented\n",
+ " 2. numbered\n",
+ "\n",
+ " third\n",
+ "\n",
+ "* second\n"))))
+
+ doc.accept @to
+
+ list_verbatim
+ end
+
end
end
diff --git a/lib/rdoc/markup/heading.rb b/lib/rdoc/markup/heading.rb
index 21e2574d68..081d637729 100644
--- a/lib/rdoc/markup/heading.rb
+++ b/lib/rdoc/markup/heading.rb
@@ -3,6 +3,9 @@
class RDoc::Markup::Heading < Struct.new :level, :text
+ ##
+ # Calls #accept_heading on +wisitor+
+
def accept visitor
visitor.accept_heading self
end
diff --git a/lib/rdoc/markup/inline.rb b/lib/rdoc/markup/inline.rb
index 1b5eac45ae..f5bf98a071 100644
--- a/lib/rdoc/markup/inline.rb
+++ b/lib/rdoc/markup/inline.rb
@@ -1,3 +1,4 @@
+require 'rdoc'
class RDoc::Markup
##
@@ -14,6 +15,9 @@ class RDoc::Markup
@@name_to_bitmap = { :_SPECIAL_ => SPECIAL }
@@next_bitmap = 2
+ ##
+ # Returns a unique bit for +name+
+
def self.bitmap_for(name)
bitmap = @@name_to_bitmap[name]
unless bitmap then
@@ -24,6 +28,9 @@ class RDoc::Markup
bitmap
end
+ ##
+ # Returns a string reperesentation of +bitmap+
+
def self.as_string(bitmap)
return "none" if bitmap.zero?
res = []
@@ -33,6 +40,9 @@ class RDoc::Markup
res.join(",")
end
+ ##
+ # yields each attribute name in +bitmap+
+
def self.each_name_of(bitmap)
@@name_to_bitmap.each do |name, bit|
next if bit == SPECIAL
@@ -75,7 +85,7 @@ class RDoc::Markup
end
##
- # Acccesses flags for character +n+
+ # Accesses flags for character +n+
def [](n)
@attrs[n]
diff --git a/lib/rdoc/markup/list.rb b/lib/rdoc/markup/list.rb
index 75326ed836..820b4c9645 100644
--- a/lib/rdoc/markup/list.rb
+++ b/lib/rdoc/markup/list.rb
@@ -35,6 +35,9 @@ class RDoc::Markup::List
@items == other.items
end
+ ##
+ # Runs this list and all its #items through +visitor+
+
def accept visitor
visitor.accept_list_start self
diff --git a/lib/rdoc/markup/list_item.rb b/lib/rdoc/markup/list_item.rb
index 500e814fe1..d719c352ec 100644
--- a/lib/rdoc/markup/list_item.rb
+++ b/lib/rdoc/markup/list_item.rb
@@ -35,6 +35,9 @@ class RDoc::Markup::ListItem
@parts == other.parts
end
+ ##
+ # Runs this list item and all its #parts through +visitor+
+
def accept visitor
visitor.accept_list_item_start self
diff --git a/lib/rdoc/markup/paragraph.rb b/lib/rdoc/markup/paragraph.rb
index a9923ed24d..808430d576 100644
--- a/lib/rdoc/markup/paragraph.rb
+++ b/lib/rdoc/markup/paragraph.rb
@@ -3,6 +3,9 @@
class RDoc::Markup::Paragraph < RDoc::Markup::Raw
+ ##
+ # Calls #accept_paragraph on +visitor+
+
def accept visitor
visitor.accept_paragraph self
end
diff --git a/lib/rdoc/markup/parser.rb b/lib/rdoc/markup/parser.rb
index 9fba69dc29..ea02ee3c5b 100644
--- a/lib/rdoc/markup/parser.rb
+++ b/lib/rdoc/markup/parser.rb
@@ -52,13 +52,13 @@ class RDoc::Markup::Parser
attr_reader :tokens
##
- # Parsers +str+ into a Document
+ # Parses +str+ into a Document
def self.parse str
parser = new
- #parser.debug = true
parser.tokenize str
- RDoc::Markup::Document.new(*parser.parse)
+ doc = RDoc::Markup::Document.new
+ parser.parse doc
end
##
@@ -86,6 +86,7 @@ class RDoc::Markup::Parser
# Builds a Heading of +level+
def build_heading level
+ _, text, = get # TEXT
heading = RDoc::Markup::Heading.new level, text
skip :NEWLINE
@@ -105,38 +106,69 @@ class RDoc::Markup::Parser
case type
when :BULLET, :LABEL, :LALPHA, :NOTE, :NUMBER, :UALPHA then
- list_type = type
- if column < margin then
+ if column < margin || (list.type && list.type != type) then
unget
break
end
- if list.type and list.type != list_type then
- unget
- break
- end
-
- list.type = list_type
+ list.type = type
+ peek_type, _, column, = peek_token
case type
when :NOTE, :LABEL then
- _, indent, = get # SPACE
- if :NEWLINE == peek_token.first then
- get
- peek_type, new_indent, peek_column, = peek_token
- indent = new_indent if
- peek_type == :INDENT and peek_column >= column
- unget
+ if peek_type == :NEWLINE then
+ # description not on the same line as LABEL/NOTE
+ # skip the trailing newline & any blank lines below
+ while peek_type == :NEWLINE
+ get
+ peek_type, _, column, = peek_token
+ end
+
+ # we may be:
+ # - at end of stream
+ # - at a column < margin:
+ # [text]
+ # blah blah blah
+ # - at the same column, but with a different type of list item
+ # [text]
+ # * blah blah
+ # - at the same column, with the same type of list item
+ # [one]
+ # [two]
+ # In all cases, we have an empty description.
+ # In the last case only, we continue.
+ if peek_type.nil? || column < margin then
+ empty = 1
+ elsif column == margin then
+ case peek_type
+ when type
+ empty = 2 # continue
+ when *LIST_TOKENS
+ empty = 1
+ else
+ empty = 0
+ end
+ else
+ empty = 0
+ end
+
+ if empty > 0 then
+ item = RDoc::Markup::ListItem.new(data)
+ item << RDoc::Markup::BlankLine.new
+ list << item
+ break if empty == 1
+ next
+ end
end
else
data = nil
- _, indent, = get
end
- list_item = build_list_item(margin + indent, data)
+ list_item = RDoc::Markup::ListItem.new data
+ parse list_item, column
+ list << list_item
- list << list_item if list_item
else
unget
break
@@ -151,54 +183,6 @@ class RDoc::Markup::Parser
end
##
- # Builds a ListItem that is flush to +indent+ with type +item_type+
-
- def build_list_item indent, item_type = nil
- p :list_item_start => [indent, item_type] if @debug
-
- list_item = RDoc::Markup::ListItem.new item_type
-
- until @tokens.empty? do
- type, data, column = get
-
- if column < indent and
- not type == :NEWLINE and
- (type != :INDENT or data < indent) then
- unget
- break
- end
-
- case type
- when :INDENT then
- unget
- list_item.push(*parse(indent))
- when :TEXT then
- unget
- list_item << build_paragraph(indent)
- when :HEADER then
- list_item << build_heading(data)
- when :NEWLINE then
- list_item << RDoc::Markup::BlankLine.new
- when *LIST_TOKENS then
- unget
- list_item << build_list(column)
- else
- raise ParseError, "Unhandled token #{@current_token.inspect}"
- end
- end
-
- p :list_item_end => [indent, item_type] if @debug
-
- return nil if list_item.empty?
-
- list_item.parts.shift if
- RDoc::Markup::BlankLine === list_item.parts.first and
- list_item.length > 1
-
- list_item
- end
-
- ##
# Builds a Paragraph that is flush to +margin+
def build_paragraph margin
@@ -209,18 +193,7 @@ class RDoc::Markup::Parser
until @tokens.empty? do
type, data, column, = get
- case type
- when :INDENT then
- next if data == margin and peek_token[0] == :TEXT
-
- unget
- break
- when :TEXT then
- if column != margin then
- unget
- break
- end
-
+ if type == :TEXT && column == margin then
paragraph << data
skip :NEWLINE
else
@@ -235,67 +208,81 @@ class RDoc::Markup::Parser
end
##
- # Builds a Verbatim that is flush to +margin+
+ # Builds a Verbatim that is indented from +margin+.
+ #
+ # The verbatim block is shifted left (the least indented lines start in
+ # column 0). Each part of the verbatim is one line of text, always
+ # terminated by a newline. Blank lines always consist of a single newline
+ # character, and there is never a single newline at the end of the verbatim.
def build_verbatim margin
p :verbatim_begin => margin if @debug
verbatim = RDoc::Markup::Verbatim.new
+ min_indent = nil
+ generate_leading_spaces = true
+ line = ''
+
until @tokens.empty? do
type, data, column, = get
- case type
- when :INDENT then
- if margin >= data then
- unget
- break
- end
+ if type == :NEWLINE then
+ line << data
+ verbatim << line
+ line = ''
+ generate_leading_spaces = true
+ next
+ end
- indent = data - margin
+ if column <= margin
+ unget
+ break
+ end
- verbatim << ' ' * indent
- when :HEADER then
- verbatim << '=' * data
+ if generate_leading_spaces then
+ indent = column - margin
+ line << ' ' * indent
+ min_indent = indent if min_indent.nil? || indent < min_indent
+ generate_leading_spaces = false
+ end
+ case type
+ when :HEADER then
+ line << '=' * data
_, _, peek_column, = peek_token
peek_column ||= column + data
- verbatim << ' ' * (peek_column - column - data)
+ indent = peek_column - column - data
+ line << ' ' * indent
when :RULE then
width = 2 + data
- verbatim << '-' * width
-
+ line << '-' * width
_, _, peek_column, = peek_token
- peek_column ||= column + data + 2
- verbatim << ' ' * (peek_column - column - width)
+ peek_column ||= column + width
+ indent = peek_column - column - width
+ line << ' ' * indent
when :TEXT then
- verbatim << data
- when *LIST_TOKENS then
- if column <= margin then
- unget
- break
- end
-
+ line << data
+ else # *LIST_TOKENS
list_marker = case type
- when :BULLET then '*'
- when :LABEL then "[#{data}]"
- when :LALPHA, :NUMBER, :UALPHA then "#{data}."
- when :NOTE then "#{data}::"
+ when :BULLET then data
+ when :LABEL then "[#{data}]"
+ when :NOTE then "#{data}::"
+ else # :LALPHA, :NUMBER, :UALPHA
+ "#{data}."
end
-
- verbatim << list_marker
-
- _, data, = get
-
- verbatim << ' ' * (data - list_marker.length)
- when :NEWLINE then
- verbatim << data
- break unless [:INDENT, :NEWLINE].include? peek_token[0]
- else
- unget
- break
+ line << list_marker
+ peek_type, _, peek_column = peek_token
+ unless peek_type == :NEWLINE then
+ peek_column ||= column + list_marker.length
+ indent = peek_column - column - list_marker.length
+ line << ' ' * indent
+ end
end
+
end
+ verbatim << line << "\n" unless line.empty?
+ verbatim.parts.each { |p| p.slice!(0, min_indent) unless p == "\n" } if min_indent > 0
verbatim.normalize
p :verbatim_end => margin if @debug
@@ -313,65 +300,60 @@ class RDoc::Markup::Parser
end
##
- # Parses the tokens into a Document
-
- def parse indent = 0
+ # Parses the tokens into an array of RDoc::Markup::XXX objects,
+ # and appends them to the passed +parent+ RDoc::Markup::YYY object.
+ #
+ # Exits at the end of the token stream, or when it encounters a token
+ # in a column less than +indent+ (unless it is a NEWLINE).
+ #
+ # Returns +parent+.
+
+ def parse parent, indent = 0
p :parse_start => indent if @debug
- document = []
-
until @tokens.empty? do
type, data, column, = get
- if type != :INDENT and column < indent then
+ if type == :NEWLINE then
+ # trailing newlines are skipped below, so this is a blank line
+ parent << RDoc::Markup::BlankLine.new
+ skip :NEWLINE, false
+ next
+ end
+
+ # indentation change: break or verbattim
+ if column < indent then
unget
break
+ elsif column > indent then
+ unget
+ parent << build_verbatim(indent)
+ next
end
+ # indentation is the same
case type
when :HEADER then
- document << build_heading(data)
- when :INDENT then
- if indent > data then
- unget
- break
- elsif indent == data then
- next
- end
-
- unget
- document << build_verbatim(indent)
- when :NEWLINE then
- document << RDoc::Markup::BlankLine.new
- skip :NEWLINE, false
+ parent << build_heading(data)
when :RULE then
- document << RDoc::Markup::Rule.new(data)
+ parent << RDoc::Markup::Rule.new(data)
skip :NEWLINE
when :TEXT then
unget
- document << build_paragraph(indent)
-
- # we're done with this paragraph (indent mismatch)
- break if peek_token[0] == :TEXT
+ parent << build_paragraph(indent)
when *LIST_TOKENS then
unget
-
- list = build_list(indent)
-
- document << list if list
-
- # we're done with this list (indent mismatch)
- break if LIST_TOKENS.include? peek_token.first and indent > 0
+ parent << build_list(indent)
else
type, data, column, line = @current_token
- raise ParseError,
- "Unhandled token #{type} (#{data.inspect}) at #{line}:#{column}"
+ raise ParseError, "Unhandled token #{type} (#{data.inspect}) at #{line}:#{column}"
end
end
p :parse_end => indent if @debug
- document
+ parent
+
end
##
@@ -384,63 +366,16 @@ class RDoc::Markup::Parser
end
##
- # Skips a token of +token_type+, optionally raising an error.
+ # Skips the next token if its type is +token_type+.
+ #
+ # Optionally raises an error if the next token is not of the expected type.
def skip token_type, error = true
type, = get
-
return unless type # end of stream
-
return @current_token if token_type == type
-
unget
-
- raise ParseError, "expected #{token_type} got #{@current_token.inspect}" if
- error
- end
-
- ##
- # Consumes tokens until NEWLINE and turns them back into text
-
- def text
- text = ''
-
- loop do
- type, data, = get
-
- text << case type
- when :BULLET then
- _, space, = get # SPACE
- "*#{' ' * (space - 1)}"
- when :LABEL then
- _, space, = get # SPACE
- "[#{data}]#{' ' * (space - data.length - 2)}"
- when :LALPHA, :NUMBER, :UALPHA then
- _, space, = get # SPACE
- "#{data}.#{' ' * (space - 2)}"
- when :NOTE then
- _, space = get # SPACE
- "#{data}::#{' ' * (space - data.length - 2)}"
- when :TEXT then
- data
- when :NEWLINE then
- unget
- break
- when nil then
- break
- else
- raise ParseError, "unhandled token #{@current_token.inspect}"
- end
- end
-
- text
- end
-
- ##
- # Calculates the column and line of the current token based on +offset+.
-
- def token_pos offset
- [offset - @line_pos, @line]
+ raise ParseError, "expected #{token_type} got #{@current_token.inspect}" if error
end
##
@@ -455,51 +390,62 @@ class RDoc::Markup::Parser
until s.eos? do
pos = s.pos
+ # leading spaces will be reflected by the column of the next token
+ # the only thing we loose are trailing spaces at the end of the file
+ next if s.scan(/ +/)
+
+ # note: after BULLET, LABEL, etc.,
+ # indent will be the column of the next non-newline token
+
@tokens << case
+ # [CR]LF => :NEWLINE
when s.scan(/\r?\n/) then
token = [:NEWLINE, s.matched, *token_pos(pos)]
@line_pos = s.pos
@line += 1
token
- when s.scan(/ +/) then
- [:INDENT, s.matched_size, *token_pos(pos)]
+ # === text => :HEADER then :TEXT
when s.scan(/(=+)\s*/) then
level = s[1].length
level = 6 if level > 6
@tokens << [:HEADER, level, *token_pos(pos)]
-
pos = s.pos
s.scan(/.*/)
- [:TEXT, s.matched, *token_pos(pos)]
- when s.scan(/^(-{3,}) *$/) then
+ [:TEXT, s.matched.sub(/\r$/, ''), *token_pos(pos)]
+ # --- (at least 3) and nothing else on the line => :RULE
+ when s.scan(/(-{3,}) *$/) then
[:RULE, s[1].length - 2, *token_pos(pos)]
- when s.scan(/([*-])\s+/) then
- @tokens << [:BULLET, :BULLET, *token_pos(pos)]
- [:SPACE, s.matched_size, *token_pos(pos)]
- when s.scan(/([a-z]|\d+)\.[ \t]+\S/i) then
+ # * or - followed by white space and text => :BULLET
+ when s.scan(/([*-]) +(\S)/) then
+ s.pos -= s[2].bytesize # unget \S
+ [:BULLET, s[1], *token_pos(pos)]
+ # A. text, a. text, 12. text => :UALPHA, :LALPHA, :NUMBER
+ when s.scan(/([a-z]|\d+)\. +(\S)/i) then
+ # FIXME if tab(s), the column will be wrong
+ # either support tabs everywhere by first expanding them to
+ # spaces, or assume that they will have been replaced
+ # before (and provide a check for that at least in debug
+ # mode)
list_label = s[1]
- width = s.matched_size - 1
-
- s.pos -= 1 # unget \S
-
- list_type = case list_label
- when /[a-z]/ then :LALPHA
- when /[A-Z]/ then :UALPHA
- when /\d/ then :NUMBER
- else
- raise ParseError, "BUG token #{list_label}"
- end
-
- @tokens << [list_type, list_label, *token_pos(pos)]
- [:SPACE, width, *token_pos(pos)]
+ s.pos -= s[2].bytesize # unget \S
+ list_type =
+ case list_label
+ when /[a-z]/ then :LALPHA
+ when /[A-Z]/ then :UALPHA
+ when /\d/ then :NUMBER
+ else
+ raise ParseError, "BUG token #{list_label}"
+ end
+ [list_type, list_label, *token_pos(pos)]
+ # [text] followed by spaces or end of line => :LABEL
when s.scan(/\[(.*?)\]( +|$)/) then
- @tokens << [:LABEL, s[1], *token_pos(pos)]
- [:SPACE, s.matched_size, *token_pos(pos)]
+ [:LABEL, s[1], *token_pos(pos)]
+ # text:: followed by spaces or end of line => :NOTE
when s.scan(/(.*?)::( +|$)/) then
- @tokens << [:NOTE, s[1], *token_pos(pos)]
- [:SPACE, s.matched_size, *token_pos(pos)]
+ [:NOTE, s[1], *token_pos(pos)]
+ # anything else: :TEXT
else s.scan(/.*/)
- [:TEXT, s.matched, *token_pos(pos)]
+ [:TEXT, s.matched.sub(/\r$/, ''), *token_pos(pos)]
end
end
@@ -507,9 +453,17 @@ class RDoc::Markup::Parser
end
##
- # Returns the current token or +token+ to the token stream
+ # Calculates the column and line of the current token based on +offset+.
+
+ def token_pos offset
+ [offset - @line_pos, @line]
+ end
+
+ ##
+ # Returns the current token to the token stream
- def unget token = @current_token
+ def unget
+ token = @current_token
p :unget => token if @debug
raise Error, 'too many #ungets' if token == @tokens.first
@tokens.unshift token if token
diff --git a/lib/rdoc/markup/preprocess.rb b/lib/rdoc/markup/pre_process.rb
index cefb498916..e59bd227b7 100644
--- a/lib/rdoc/markup/preprocess.rb
+++ b/lib/rdoc/markup/pre_process.rb
@@ -1,12 +1,15 @@
require 'rdoc/markup'
+require 'rdoc/encoding'
##
# Handle common directives that can occur in a block of text:
#
-# : include : filename
+# \:include: filename
#
-# RDoc plugin authors can register additional directives to be handled through
-# RDoc::Markup::PreProcess::register
+# Directives can be escaped by preceding them with a backslash.
+#
+# RDoc plugin authors can register additional directives to be handled by
+# using RDoc::Markup::PreProcess::register
class RDoc::Markup::PreProcess
@@ -52,18 +55,25 @@ class RDoc::Markup::PreProcess
# +code_object+. See RDoc::CodeObject#metadata
def handle text, code_object = nil
- text.gsub!(/^([ \t]*#?[ \t]*):(\w+):([ \t]*)(.+)?\n/) do
- next $& if $3.empty? and $4 and $4[0, 1] == ':'
+ # regexp helper (square brackets for optional)
+ # $1 $2 $3 $4 $5
+ # [prefix][\]:directive:[spaces][param]newline
+ text.gsub!(/^([ \t]*#?[ \t]*)(\\?):(\w+):([ \t]*)(.+)?\n/) do
+ # skip something like ':toto::'
+ next $& if $4.empty? and $5 and $5[0, 1] == ':'
+
+ # skip if escaped
+ next "#$1:#$3:#$4#$5\n" unless $2.empty?
prefix = $1
- directive = $2.downcase
- param = $4
+ directive = $3.downcase
+ param = $5
case directive
when 'include' then
filename = param.split[0]
- include_file filename, prefix
-
+ encoding = if defined?(Encoding) then text.encoding else nil end
+ include_file filename, prefix, encoding
else
result = yield directive, param if block_given?
@@ -88,27 +98,38 @@ class RDoc::Markup::PreProcess
end
##
- # Include a file, indenting it correctly.
-
- def include_file(name, indent)
- if full_name = find_include_file(name) then
- content = if defined?(Encoding) then
- File.binread full_name
- else
- File.read full_name
- end
- # HACK determine content type and force encoding
- content = content.sub(/\A# .*coding[=:].*$/, '').lstrip
-
- # strip leading '#'s, but only if all lines start with them
- if content =~ /^[^#]/ then
- content.gsub(/^/, indent)
- else
- content.gsub(/^#?/, indent)
- end
- else
+ # Handles the <tt>:include: _filename_</tt> directive.
+ #
+ # If the first line of the included file starts with '#', and contains
+ # an encoding information in the form 'coding:' or 'coding=', it is
+ # removed.
+ #
+ # If all lines in the included file start with a '#', this leading '#'
+ # is removed before inclusion. The included content is indented like
+ # the <tt>:include:</tt> directive.
+ #--
+ # so all content will be verbatim because of the likely space after '#'?
+ # TODO shift left the whole file content in that case
+ # TODO comment stop/start #-- and #++ in included file must be processed here
+
+ def include_file name, indent, encoding
+ full_name = find_include_file name
+
+ unless full_name then
warn "Couldn't find file to include '#{name}' from #{@input_file_name}"
- ''
+ return ''
+ end
+
+ content = RDoc::Encoding.read_file full_name, encoding
+
+ # strip magic comment
+ content = content.sub(/\A# .*coding[=:].*$/, '').lstrip
+
+ # strip leading '#'s, but only if all lines start with them
+ if content =~ /^[^#]/ then
+ content.gsub(/^/, indent)
+ else
+ content.gsub(/^#?/, indent)
end
end
diff --git a/lib/rdoc/markup/raw.rb b/lib/rdoc/markup/raw.rb
index 1124be7cc8..ca877c79af 100644
--- a/lib/rdoc/markup/raw.rb
+++ b/lib/rdoc/markup/raw.rb
@@ -27,6 +27,9 @@ class RDoc::Markup::Raw
self.class == other.class and text == other.text
end
+ ##
+ # Calls #accept_raw+ on +visitor+
+
def accept visitor
visitor.accept_raw self
end
@@ -63,3 +66,4 @@ class RDoc::Markup::Raw
end
end
+
diff --git a/lib/rdoc/markup/rule.rb b/lib/rdoc/markup/rule.rb
index 4fcd040d2b..b778f2bc09 100644
--- a/lib/rdoc/markup/rule.rb
+++ b/lib/rdoc/markup/rule.rb
@@ -3,6 +3,9 @@
class RDoc::Markup::Rule < Struct.new :weight
+ ##
+ # Calls #accept_rule on +visitor+
+
def accept visitor
visitor.accept_rule self
end
diff --git a/lib/rdoc/markup/text_formatter_test_case.rb b/lib/rdoc/markup/text_formatter_test_case.rb
new file mode 100644
index 0000000000..ba9e7c6187
--- /dev/null
+++ b/lib/rdoc/markup/text_formatter_test_case.rb
@@ -0,0 +1,116 @@
+require 'rdoc/markup/formatter_test_case'
+
+##
+# Test case for creating new plain-text RDoc::Markup formatters. See also
+# RDoc::Markup::FormatterTestCase
+#
+# See test_rdoc_markup_to_rdoc.rb for a complete example.
+#
+# Example:
+#
+# class TestRDocMarkupToNewTextFormat < RDoc::Markup::TextFormatterTestCase
+#
+# add_visitor_tests
+# add_text_tests
+#
+# def setup
+# super
+#
+# @to = RDoc::Markup::ToNewTextFormat.new
+# end
+#
+# def accept_blank_line
+# assert_equal :junk, @to.res.join
+# end
+#
+# # ...
+#
+# end
+
+class RDoc::Markup::TextFormatterTestCase < RDoc::Markup::FormatterTestCase
+
+ ##
+ # Adds test cases to the calling TestCase.
+
+ def self.add_text_tests
+ self.class_eval do
+
+ ##
+ # Test case that calls <tt>@to.accept_heading</tt>
+
+ def test_accept_heading_indent
+ @to.start_accepting
+ @to.indent = 3
+ @to.accept_heading @RM::Heading.new(1, 'Hello')
+
+ accept_heading_indent
+ end
+
+ ##
+ # Test case that calls <tt>@to.accept_rule</tt>
+
+ def test_accept_rule_indent
+ @to.start_accepting
+ @to.indent = 3
+ @to.accept_rule @RM::Rule.new(1)
+
+ accept_rule_indent
+ end
+
+ ##
+ # Test case that calls <tt>@to.accept_verbatim</tt>
+
+ def test_accept_verbatim_indent
+ @to.start_accepting
+ @to.indent = 2
+ @to.accept_verbatim @RM::Verbatim.new("hi\n", " world\n")
+
+ accept_verbatim_indent
+ end
+
+ ##
+ # Test case that calls <tt>@to.accept_verbatim</tt> with a big indent
+
+ def test_accept_verbatim_big_indent
+ @to.start_accepting
+ @to.indent = 2
+ @to.accept_verbatim @RM::Verbatim.new("hi\n", "world\n")
+
+ accept_verbatim_big_indent
+ end
+
+ ##
+ # Test case that calls <tt>@to.accept_paragraph</tt> with an indent
+
+ def test_accept_paragraph_indent
+ @to.start_accepting
+ @to.indent = 3
+ @to.accept_paragraph @RM::Paragraph.new(('words ' * 30).strip)
+
+ accept_paragraph_indent
+ end
+
+ ##
+ # Test case that calls <tt>@to.accept_paragraph</tt> with a long line
+
+ def test_accept_paragraph_wrap
+ @to.start_accepting
+ @to.accept_paragraph @RM::Paragraph.new(('words ' * 30).strip)
+
+ accept_paragraph_wrap
+ end
+
+ ##
+ # Test case that calls <tt>@to.attributes</tt> with an escaped
+ # cross-reference. If this test doesn't pass something may be very
+ # wrong.
+
+ def test_attributes
+ assert_equal 'Dog', @to.attributes("\\Dog")
+ end
+
+ end
+ end
+
+end
+
diff --git a/lib/rdoc/markup/to_ansi.rb b/lib/rdoc/markup/to_ansi.rb
index 9a5be8babb..c9f874ea3c 100644
--- a/lib/rdoc/markup/to_ansi.rb
+++ b/lib/rdoc/markup/to_ansi.rb
@@ -1,10 +1,13 @@
-require 'rdoc/markup/inline'
+require 'rdoc/markup/to_rdoc'
##
# Outputs RDoc markup with vibrant ANSI color!
class RDoc::Markup::ToAnsi < RDoc::Markup::ToRdoc
+ ##
+ # Creates a new ToAnsi visitor that is ready to output vibrant ANSI color!
+
def initialize
super
@@ -23,12 +26,15 @@ class RDoc::Markup::ToAnsi < RDoc::Markup::ToRdoc
add_tag :EM, "\e[4m", "\e[m"
end
+ ##
+ # Overrides indent width to ensure output lines up correctly.
+
def accept_list_item_end list_item
width = case @list_type.last
when :BULLET then
2
when :NOTE, :LABEL then
- @res << "\n"
+ @res << "\n" unless res.length == 1
2
else
bullet = @list_index.last.to_s
@@ -39,6 +45,9 @@ class RDoc::Markup::ToAnsi < RDoc::Markup::ToRdoc
@indent -= width
end
+ ##
+ # Adds coloring to note and label list items
+
def accept_list_item_start list_item
bullet = case @list_type.last
when :BULLET then
@@ -62,6 +71,9 @@ class RDoc::Markup::ToAnsi < RDoc::Markup::ToRdoc
end
end
+ ##
+ # Starts accepting with a reset screen
+
def start_accepting
super
diff --git a/lib/rdoc/markup/to_bs.rb b/lib/rdoc/markup/to_bs.rb
index e7af129824..931edd81ea 100644
--- a/lib/rdoc/markup/to_bs.rb
+++ b/lib/rdoc/markup/to_bs.rb
@@ -1,4 +1,4 @@
-require 'rdoc/markup/inline'
+require 'rdoc/markup/to_rdoc'
##
# Outputs RDoc markup with hot backspace action! You will probably need a
@@ -8,6 +8,9 @@ require 'rdoc/markup/inline'
class RDoc::Markup::ToBs < RDoc::Markup::ToRdoc
+ ##
+ # Returns a new ToBs that is ready for hot backspace action!
+
def initialize
super
@@ -22,8 +25,12 @@ class RDoc::Markup::ToBs < RDoc::Markup::ToRdoc
def init_tags
add_tag :BOLD, '+b', '-b'
add_tag :EM, '+_', '-_'
+ add_tag :TT, '' , '' # we need in_tt information maintained
end
+ ##
+ # Makes heading text bold.
+
def accept_heading heading
use_prefix or @res << ' ' * @indent
@res << @headings[heading.level][0]
@@ -44,7 +51,6 @@ class RDoc::Markup::ToBs < RDoc::Markup::ToRdoc
when '+_' then @in_em = true
when '-_' then @in_em = false
end
-
''
end
diff --git a/lib/rdoc/markup/to_html.rb b/lib/rdoc/markup/to_html.rb
index 74e3137eb2..de723921e9 100644
--- a/lib/rdoc/markup/to_html.rb
+++ b/lib/rdoc/markup/to_html.rb
@@ -8,6 +8,8 @@ require 'cgi'
class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
+ include RDoc::Text
+
##
# Maps RDoc::Markup::Parser::LIST_TOKENS types to HTML tags
@@ -15,7 +17,7 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
:BULLET => ['<ul>', '</ul>'],
:LABEL => ['<dl>', '</dl>'],
:LALPHA => ['<ol style="display: lower-alpha">', '</ol>'],
- :NOTE => ['<table>', '</table>'],
+ :NOTE => ['<table class="rdoc-list">', '</table>'],
:NUMBER => ['<ol>', '</ol>'],
:UALPHA => ['<ol style="display: upper-alpha">', '</ol>'],
}
@@ -48,6 +50,9 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
File.join(*from)
end
+ ##
+ # Creates a new formatter that will output HTML
+
def initialize
super
@@ -103,13 +108,15 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
end
end
+ # :section: Special handling
+
##
- # And we're invoked with a potential external hyperlink mailto:
- # just gets inserted. http: links are checked to see if they
+ # And we're invoked with a potential external hyperlink. <tt>mailto:</tt>
+ # just gets inserted. <tt>http:</tt> links are checked to see if they
# reference an image. If so, that image gets inserted using an
- # <img> tag. Otherwise a conventional <a href> is used. We also
- # support a special type of hyperlink, link:, which is a reference
- # to a local file whose path is relative to the --op directory.
+ # <tt><img></tt> tag. Otherwise a conventional <tt><a href></tt> is used.
+ # We also support a special type of hyperlink, <tt>link:</tt>, which is a
+ # reference to a local file whose path is relative to the --op directory.
def handle_special_HYPERLINK(special)
url = special.text
@@ -118,7 +125,7 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
##
# Here's a hypedlink where the label is different to the URL
- # <label>[url] or {long label}[url]
+ # <label>[url] or {long label}[url]
def handle_special_TIDYLINK(special)
text = special.text
@@ -130,8 +137,10 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
gen_url url, label
end
+ # :section: Utilities
+
##
- # This is a higher speed (if messier) version of wrap
+ # Wraps +txt+ to +line_len+
def wrap(txt, line_len = 76)
res = []
@@ -159,173 +168,150 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
sp += 1 while sp < ep and txt[sp] == ?\s
end
- res.join
+ res.join.strip
end
- ##
# :section: Visitor
+ ##
+ # Prepares the visitor for HTML generation
+
def start_accepting
@res = []
@in_list_entry = []
@list = []
end
+ ##
+ # Returns the generated output
+
def end_accepting
@res.join
end
+ ##
+ # Adds +paragraph+ to the output
+
def accept_paragraph(paragraph)
- @res << annotate("<p>") + "\n"
- @res << wrap(convert_flow(@am.flow(paragraph.text)))
- @res << annotate("</p>") + "\n"
+ @res << "\n<p>"
+ @res << wrap(to_html(paragraph.text))
+ @res << "</p>\n"
end
+ ##
+ # Adds +verbatim+ to the output
+
def accept_verbatim(verbatim)
- @res << annotate("<pre>") << "\n"
- @res << CGI.escapeHTML(verbatim.text)
- @res << annotate("</pre>") << "\n"
+ @res << "\n<pre>"
+ @res << CGI.escapeHTML(verbatim.text.rstrip)
+ @res << "</pre>\n"
end
+ ##
+ # Adds +rule+ to the output
+
def accept_rule(rule)
size = rule.weight
size = 10 if size > 10
- @res << "<hr style=\"height: #{size}px\"></hr>"
+ @res << "<hr style=\"height: #{size}px\">\n"
end
+ ##
+ # Prepares the visitor for consuming +list+
+
def accept_list_start(list)
@list << list.type
- @res << html_list_name(list.type, true) << "\n"
+ @res << html_list_name(list.type, true)
@in_list_entry.push false
end
+ ##
+ # Finishes consumption of +list+
+
def accept_list_end(list)
@list.pop
if tag = @in_list_entry.pop
- @res << annotate(tag) << "\n"
+ @res << tag
end
@res << html_list_name(list.type, false) << "\n"
end
+ ##
+ # Prepares the visitor for consuming +list_item+
+
def accept_list_item_start(list_item)
if tag = @in_list_entry.last
- @res << annotate(tag) << "\n"
+ @res << tag
end
@res << list_item_start(list_item, @list.last)
end
+ ##
+ # Finishes consumption of +list_item+
+
def accept_list_item_end(list_item)
@in_list_entry[-1] = list_end_for(@list.last)
end
- def accept_blank_line(blank_line)
- # @res << annotate("<p />") << "\n"
- end
-
- def accept_heading(heading)
- @res << convert_heading(heading.level, @am.flow(heading.text))
- end
-
- def accept_raw raw
- @res << raw.parts.join("\n")
- end
-
- private
-
##
- # Converts string +item+
+ # Adds +blank_line+ to the output
- def convert_string(item)
- in_tt? ? convert_string_simple(item) : convert_string_fancy(item)
+ def accept_blank_line(blank_line)
+ # @res << annotate("<p />") << "\n"
end
##
- # Escapes HTML in +item+
+ # Adds +heading+ to the output
- def convert_string_simple(item)
- CGI.escapeHTML item
+ def accept_heading(heading)
+ @res << "\n<h#{heading.level}>"
+ @res << to_html(heading.text)
+ @res << "</h#{heading.level}>\n"
end
##
- # Converts ampersand, dashes, elipsis, quotes, copyright and registered
- # trademark symbols to HTML escaped Unicode.
-
- def convert_string_fancy(item)
- # convert ampersand before doing anything else
- item.gsub(/&/, '&amp;').
-
- # convert -- to em-dash, (-- to en-dash)
- gsub(/---?/, '&#8212;'). #gsub(/--/, '&#8211;').
-
- # convert ... to elipsis (and make sure .... becomes .<elipsis>)
- gsub(/\.\.\.\./, '.&#8230;').gsub(/\.\.\./, '&#8230;').
+ # Adds +raw+ to the output
- # convert single closing quote
- 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;'). # }
-
- # convert double opening quote
- gsub(/"/, '&#8220;').
-
- # convert copyright
- gsub(/\(c\)/, '&#169;').
-
- # convert registered trademark
- gsub(/\(r\)/, '&#174;')
+ def accept_raw raw
+ @res << raw.parts.join("\n")
end
##
- # Converts headings to hN elements
+ # CGI escapes +text+
- def convert_heading(level, flow)
- [annotate("<h#{level}>"),
- convert_flow(flow),
- annotate("</h#{level}>\n")].join
+ def convert_string(text)
+ CGI.escapeHTML text
end
##
- # Determins the HTML list element for +list_type+ and +open_tag+
+ # Determines the HTML list element for +list_type+ and +open_tag+
def html_list_name(list_type, open_tag)
tags = LIST_TYPE_TO_HTML[list_type]
raise RDoc::Error, "Invalid list type: #{list_type.inspect}" unless tags
- annotate tags[open_tag ? 0 : 1]
+ tags[open_tag ? 0 : 1]
end
##
- # Starts a list item
+ # Returns the HTML tag for +list_type+, possible using a label from
+ # +list_item+
def list_item_start(list_item, list_type)
case list_type
when :BULLET, :LALPHA, :NUMBER, :UALPHA then
- annotate("<li>")
-
+ "<li>"
when :LABEL then
- annotate("<dt>") +
- convert_flow(@am.flow(list_item.label)) +
- annotate("</dt>") +
- annotate("<dd>")
-
+ "<dt>#{to_html list_item.label}</dt>\n<dd>"
when :NOTE then
- annotate("<tr>") +
- annotate("<td valign=\"top\">") +
- convert_flow(@am.flow(list_item.label)) +
- annotate("</td>") +
- annotate("<td>")
+ "<tr><td class=\"rdoc-term\"><p>#{to_html list_item.label}</p></td>\n<td>"
else
raise RDoc::Error, "Invalid list type: #{list_type.inspect}"
end
end
##
- # Ends a list item
+ # Returns the HTML end-tag for +list_type+
def list_end_for(list_type)
case list_type
@@ -340,5 +326,12 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
end
end
+ ##
+ # Converts +item+ to HTML using RDoc::Text#to_html
+
+ def to_html item
+ super convert_flow @am.flow item
+ end
+
end
diff --git a/lib/rdoc/markup/to_html_crossref.rb b/lib/rdoc/markup/to_html_crossref.rb
index 44e71486fb..a3feb848a2 100644
--- a/lib/rdoc/markup/to_html_crossref.rb
+++ b/lib/rdoc/markup/to_html_crossref.rb
@@ -9,10 +9,10 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
##
# Regular expression to match class references
#
- # 1) There can be a '\' in front of text to suppress any cross-references
- # 2) There can be a '::' in front of class names to reference from the
+ # 1. There can be a '\\' in front of text to suppress the cross-reference
+ # 2. There can be a '::' in front of class names to reference from the
# top-level namespace.
- # 3) The method can be followed by parenthesis
+ # 3. The method can be followed by parenthesis (not recommended)
CLASS_REGEXP_STR = '\\\\?((?:\:{2})?[A-Z]\w*(?:\:\:\w+)*)'
@@ -34,10 +34,10 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
# A::B::C.meth
#{CLASS_REGEXP_STR}(?:[.#]|::)#{METHOD_REGEXP_STR}
- # Stand-alone method (proceeded by a #)
+ # Stand-alone method (preceeded by a #)
| \\?\##{METHOD_REGEXP_STR}
- # Stand-alone method (proceeded by ::)
+ # Stand-alone method (preceeded by ::)
| ::#{METHOD_REGEXP_STR}
# A::B::C
@@ -51,9 +51,10 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
# In order that words like "can't" not
# be flagged as potential cross-references, only
# flag potential class cross-references if the character
- # after the cross-referece is a space or sentence
- # punctuation.
- | #{CLASS_REGEXP_STR}(?=[\s\)\.\?\!\,\;]|\z)
+ # after the cross-referece is a space, sentence
+ # punctuation, tag start character, or attribute
+ # marker.
+ | #{CLASS_REGEXP_STR}(?=[\s\)\.\?\!\,\;<\000]|\z)
# Things that look like filenames
# The key thing is that there must be at least
@@ -62,7 +63,29 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
| (?:\.\.\/)*[-\/\w]+[_\/\.][-\w\/\.]+
# Things that have markup suppressed
- | \\[^\s]
+ # Don't process things like '\<' in \<tt>, though.
+ # TODO: including < is a hack, not very satisfying.
+ | \\[^\s<]
+ )/x
+
+ ##
+ # Version of CROSSREF_REGEXP used when <tt>--hyperlink-all</tt> is specified.
+
+ ALL_CROSSREF_REGEXP = /(
+ # A::B::C.meth
+ #{CLASS_REGEXP_STR}(?:[.#]|::)#{METHOD_REGEXP_STR}
+
+ # Stand-alone method
+ | \\?#{METHOD_REGEXP_STR}
+
+ # A::B::C
+ | #{CLASS_REGEXP_STR}(?=[\s\)\.\?\!\,\;<\000]|\z)
+
+ # Things that look like filenames
+ | (?:\.\.\/)*[-\/\w]+[_\/\.][-\w\/\.]+
+
+ # Things that have markup suppressed
+ | \\[^\s<]
)/x
##
@@ -71,19 +94,28 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
attr_accessor :context
##
+ # Should we show '#' characters on method references?
+
+ attr_accessor :show_hash
+
+ ##
# Creates a new crossref resolver that generates links relative to +context+
# which lives at +from_path+ in the generated files. '#' characters on
- # references are removed unless +show_hash+ is true.
+ # references are removed unless +show_hash+ is true. Only method names
+ # preceded by '#' or '::' are hyperlinked, unless +hyperlink_all+ is true.
- def initialize(from_path, context, show_hash)
+ def initialize(from_path, context, show_hash, hyperlink_all = false)
raise ArgumentError, 'from_path cannot be nil' if from_path.nil?
super()
- @markup.add_special(CROSSREF_REGEXP, :CROSSREF)
+ crossref_re = hyperlink_all ? ALL_CROSSREF_REGEXP : CROSSREF_REGEXP
+
+ @markup.add_special crossref_re, :CROSSREF
@from_path = from_path
@context = context
@show_hash = show_hash
+ @hyperlink_all = hyperlink_all
@seen = {}
end
@@ -92,22 +124,24 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
# We're invoked when any text matches the CROSSREF pattern. If we find the
# corresponding reference, generate a hyperlink. If the name we're looking
# for contains no punctuation, we look for it up the module/class chain.
- # For example, HyperlinkHtml is found, even without the Generator:: prefix,
- # because we look for it in module Generator first.
+ # For example, ToHtml is found, even without the <tt>RDoc::Markup::</tt>
+ # prefix, because we look for it in module Markup first.
def handle_special_CROSSREF(special)
name = special.text
- # This ensures that words entirely consisting of lowercase letters will
- # not have cross-references generated (to suppress lots of erroneous
- # cross-references to "new" in text, for instance)
- return name if name =~ /\A[a-z]*\z/
+ unless @hyperlink_all then
+ # This ensures that words entirely consisting of lowercase letters will
+ # not have cross-references generated (to suppress lots of erroneous
+ # cross-references to "new" in text, for instance)
+ return name if name =~ /\A[a-z]*\z/
+ end
return @seen[name] if @seen.include? name
lookup = name
- name = name[0, 1] unless @show_hash if name[0, 1] == '#'
+ name = name[1..-1] unless @show_hash if name[0, 1] == '#'
# Find class, module, or method in class or module.
#
@@ -120,26 +154,47 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
# whether the string as a whole is a known symbol).
if /#{CLASS_REGEXP_STR}([.#]|::)#{METHOD_REGEXP_STR}/ =~ lookup then
- container = $1
type = $2
- type = '#' if type == '.'
+ type = '' if type == '.' # will find either #method or ::method
method = "#{type}#{$3}"
- ref = @context.find_symbol container, method
+ container = @context.find_symbol_module($1)
+ elsif /^([.#]|::)#{METHOD_REGEXP_STR}/ =~ lookup then
+ type = $1
+ type = '' if type == '.'
+ method = "#{type}#{$2}"
+ container = @context
+ else
+ container = nil
+ end
+
+ if container then
+ ref = container.find_local_symbol method
+
+ unless ref || RDoc::TopLevel === container then
+ ref = container.find_ancestor_local_symbol method
+ end
end
ref = @context.find_symbol lookup unless ref
+ ref = nil if RDoc::Alias === ref # external alias: can't link to it
out = if lookup == '\\' then
lookup
elsif lookup =~ /^\\/ then
- $'
- elsif ref and ref.document_self then
- "<a href=\"#{ref.as_href @from_path}\">#{name}</a>"
+ # we remove the \ only in front of what we know:
+ # other backslashes are treated later, only outside of <tt>
+ ref ? $' : lookup
+ elsif ref then
+ if ref.document_self then
+ "<a href=\"#{ref.as_href @from_path}\">#{name}</a>"
+ else
+ name
+ end
else
- name
+ lookup
end
- @seen[name] = out
+ @seen[lookup] = out
out
end
diff --git a/lib/rdoc/markup/to_rdoc.rb b/lib/rdoc/markup/to_rdoc.rb
index 867715bb1e..b1ac59e5b0 100644
--- a/lib/rdoc/markup/to_rdoc.rb
+++ b/lib/rdoc/markup/to_rdoc.rb
@@ -1,3 +1,4 @@
+require 'rdoc/markup/formatter'
require 'rdoc/markup/inline'
##
@@ -5,21 +6,49 @@ require 'rdoc/markup/inline'
class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
+ ##
+ # Current indent amount for output in characters
+
attr_accessor :indent
+
+ ##
+ # Output width in characters
+
+ attr_accessor :width
+
+ ##
+ # Stack of current list indexes for alphabetic and numeric lists
+
attr_reader :list_index
+
+ ##
+ # Stack of list types
+
attr_reader :list_type
+
+ ##
+ # Stack of list widths for indentation
+
attr_reader :list_width
+
+ ##
+ # Prefix for the next list item. See #use_prefix
+
attr_reader :prefix
+
+ ##
+ # Output accumulator
+
attr_reader :res
+ ##
+ # Creates a new formatter that will output (mostly) \RDoc markup
+
def initialize
super
- @markup.add_special(/\\[^\s]/, :SUPPRESSED_CROSSREF)
-
+ @markup.add_special(/\\\S/, :SUPPRESSED_CROSSREF)
@width = 78
- @prefix = ''
-
init_tags
@headings = {}
@@ -34,7 +63,7 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
end
##
- # Maps attributes to ANSI sequences
+ # Maps attributes to HTML sequences
def init_tags
add_tag :BOLD, "<b>", "</b>"
@@ -42,10 +71,16 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
add_tag :EM, "<em>", "</em>"
end
+ ##
+ # Adds +blank_line+ to the output
+
def accept_blank_line blank_line
@res << "\n"
end
+ ##
+ # Adds +heading+ to the output
+
def accept_heading heading
use_prefix or @res << ' ' * @indent
@res << @headings[heading.level][0]
@@ -54,12 +89,18 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
@res << "\n"
end
+ ##
+ # Finishes consumption of +list+
+
def accept_list_end list
@list_index.pop
@list_type.pop
@list_width.pop
end
+ ##
+ # Finishes consumption of +list_item+
+
def accept_list_item_end list_item
width = case @list_type.last
when :BULLET then
@@ -76,29 +117,29 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
@indent -= width
end
+ ##
+ # Prepares the visitor for consuming +list_item+
+
def accept_list_item_start list_item
- bullet = case @list_type.last
- when :BULLET then
- '*'
- when :NOTE, :LABEL then
- attributes(list_item.label) + ":\n"
- else
- @list_index.last.to_s + '.'
- end
-
- case @list_type.last
+ type = @list_type.last
+
+ case type
when :NOTE, :LABEL then
+ bullet = attributes(list_item.label) + ":\n"
+ @prefix = ' ' * @indent
@indent += 2
- @prefix = bullet + (' ' * @indent)
+ @prefix << bullet + (' ' * @indent)
else
+ bullet = type == :BULLET ? '*' : @list_index.last.to_s + '.'
@prefix = (' ' * @indent) + bullet.ljust(bullet.length + 1)
-
width = bullet.length + 1
-
@indent += width
end
end
+ ##
+ # Prepares the visitor for consuming +list+
+
def accept_list_start list
case list.type
when :BULLET then
@@ -123,14 +164,23 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
@list_type << list.type
end
+ ##
+ # Adds +paragraph+ to the output
+
def accept_paragraph paragraph
wrap attributes(paragraph.text)
end
+ ##
+ # Adds +raw+ to the output
+
def accept_raw raw
@res << raw.parts.join("\n")
end
+ ##
+ # Adds +rule+ to the output
+
def accept_rule rule
use_prefix or @res << ' ' * @indent
@res << '-' * (@width - @indent)
@@ -138,58 +188,46 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
end
##
- # Outputs +verbatim+ flush left and indented 2 columns
+ # Outputs +verbatim+ indented 2 columns
def accept_verbatim verbatim
indent = ' ' * (@indent + 2)
- lines = []
- current_line = []
-
- # split into lines
verbatim.parts.each do |part|
- current_line << part
-
- if part == "\n" then
- lines << current_line
- current_line = []
- end
+ @res << indent unless part == "\n"
+ @res << part
end
- lines << current_line unless current_line.empty?
-
- # calculate margin
- indented = lines.select { |line| line != ["\n"] }
- margin = indented.map { |line| line.first.length }.min
-
- # flush left
- indented.each { |line| line[0][0...margin] = '' }
-
- # output
- use_prefix or @res << indent # verbatim is unlikely to have prefix
- @res << lines.shift.join
-
- lines.each do |line|
- @res << indent unless line == ["\n"]
- @res << line.join
- end
-
- @res << "\n"
+ @res << "\n" unless @res =~ /\n\z/
end
+ ##
+ # Applies attribute-specific markup to +text+ using RDoc::AttributeManager
+
def attributes text
flow = @am.flow text.dup
convert_flow flow
end
+ ##
+ # Returns the generated output
+
def end_accepting
@res.join
end
+ ##
+ # Removes preceeding \\ from the suppressed crossref +special+
+
def handle_special_SUPPRESSED_CROSSREF special
- special.text.sub(/\\/, '')
+ text = special.text
+ text = text.sub('\\', '') unless in_tt?
+ text
end
+ ##
+ # Prepares the visitor for text generation
+
def start_accepting
@res = [""]
@indent = 0
@@ -200,6 +238,10 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
@list_width = []
end
+ ##
+ # Adds the stored #prefix to the output and clears it. Lists generate a
+ # prefix for later consumption.
+
def use_prefix
prefix = @prefix
@prefix = nil
@@ -208,6 +250,9 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
prefix
end
+ ##
+ # Wraps +text+ to #width
+
def wrap text
return unless text && !text.empty?
diff --git a/lib/rdoc/markup/to_test.rb b/lib/rdoc/markup/to_test.rb
index 0afdb96a18..f79f9475f1 100644
--- a/lib/rdoc/markup/to_test.rb
+++ b/lib/rdoc/markup/to_test.rb
@@ -6,6 +6,8 @@ require 'rdoc/markup/formatter'
class RDoc::Markup::ToTest < RDoc::Markup::Formatter
+ # :stopdoc:
+
##
# :section: Visitor
@@ -22,8 +24,12 @@ class RDoc::Markup::ToTest < RDoc::Markup::Formatter
@res << paragraph.text
end
+ def accept_raw raw
+ @res << raw.parts.join
+ end
+
def accept_verbatim(verbatim)
- @res << verbatim.text
+ @res << verbatim.text.gsub(/^(\S)/, ' \1')
end
def accept_list_start(list)
@@ -60,5 +66,7 @@ class RDoc::Markup::ToTest < RDoc::Markup::Formatter
@res << '-' * rule.weight
end
+ # :startdoc:
+
end
diff --git a/lib/rdoc/markup/verbatim.rb b/lib/rdoc/markup/verbatim.rb
index c684d78765..8fe2184699 100644
--- a/lib/rdoc/markup/verbatim.rb
+++ b/lib/rdoc/markup/verbatim.rb
@@ -3,6 +3,9 @@
class RDoc::Markup::Verbatim < RDoc::Markup::Raw
+ ##
+ # Calls #accept_verbatim on +visitor+
+
def accept visitor
visitor.accept_verbatim self
end
@@ -17,16 +20,16 @@ class RDoc::Markup::Verbatim < RDoc::Markup::Raw
@parts.each do |part|
case part
- when /\n/ then
+ when /^\s*\n/ then
newlines += 1
- parts << part if newlines <= 2
+ parts << part if newlines == 1
else
newlines = 0
parts << part
end
end
- parts.slice!(-1) if parts[-2..-1] == ["\n", "\n"]
+ parts.pop if parts.last =~ /\A\r?\n\z/
@parts = parts
end
diff --git a/lib/rdoc/method_attr.rb b/lib/rdoc/method_attr.rb
new file mode 100644
index 0000000000..15924d9ed0
--- /dev/null
+++ b/lib/rdoc/method_attr.rb
@@ -0,0 +1,353 @@
+require 'rdoc/code_object'
+
+##
+# Abstract class representing either a method or an attribute.
+
+class RDoc::MethodAttr < RDoc::CodeObject
+
+ include Comparable
+
+ ##
+ # Name of this method/attribute.
+
+ attr_accessor :name
+
+ ##
+ # public, protected, private
+
+ attr_accessor :visibility
+
+ ##
+ # Is this a singleton method/attribute?
+
+ attr_accessor :singleton
+
+ ##
+ # Source file token stream
+
+ attr_reader :text
+
+ ##
+ # Array of other names for this method/attribute
+
+ attr_reader :aliases
+
+ ##
+ # The method/attribute we're aliasing
+
+ attr_accessor :is_alias_for
+
+ #--
+ # The attributes below are for AnyMethod only.
+ # They are left here for the time being to
+ # allow ri to operate.
+ # TODO modify ri to avoid calling these on attributes.
+ #++
+
+ ##
+ # Parameters yielded by the called block
+
+ attr_reader :block_params
+
+ ##
+ # Parameters for this method
+
+ attr_accessor :params
+
+ ##
+ # Different ways to call this method
+
+ attr_accessor :call_seq
+
+ ##
+ # The call_seq or the param_seq with method name, if there is no call_seq.
+
+ attr_reader :arglists
+
+ ##
+ # Pretty parameter list for this method
+
+ attr_reader :param_seq
+
+
+ ##
+ # Creates a new MethodAttr from token stream +text+ and method or attribute
+ # name +name+.
+ #
+ # Usually this is called by super from a subclass.
+
+ def initialize text, name
+ super()
+
+ @text = text
+ @name = name
+
+ @aliases = []
+ @is_alias_for = nil
+ @parent_name = nil
+ @singleton = nil
+ @visibility = :public
+ @see = false
+
+ @arglists = nil
+ @block_params = nil
+ @call_seq = nil
+ @param_seq = nil
+ @params = nil
+ end
+
+ ##
+ # Order by #singleton then #name
+
+ def <=>(other)
+ [@singleton ? 0 : 1, name] <=> [other.singleton ? 0 : 1, other.name]
+ end
+
+ ##
+ # A method/attribute is documented if any of the following is true:
+ # - it was marked with :nodoc:;
+ # - it has a comment;
+ # - it is an alias for a documented method;
+ # - it has a +#see+ method that is documented.
+
+ def documented?
+ super or
+ (is_alias_for and is_alias_for.documented?) or
+ (see and see.documented?)
+ end
+
+ ##
+ # A method/attribute to look at,
+ # in particular if this method/attribute has no documentation.
+ #
+ # It can be a method/attribute of the superclass or of an included module,
+ # including the Kernel module, which is always appended to the included
+ # modules.
+ #
+ # Returns +nil+ if there is no such method/attribute.
+ # The +#is_alias_for+ method/attribute, if any, is not included.
+ #
+ # Templates may generate a "see also ..." if this method/attribute
+ # has documentation, and "see ..." if it does not.
+
+ def see
+ @see = find_see if @see == false
+ @see
+ end
+
+ def find_see # :nodoc:
+ return nil if singleton || is_alias_for
+
+ # look for the method
+ other = find_method_or_attribute name
+ return other if other
+
+ # if it is a setter, look for a getter
+ return nil unless name =~ /[a-z_]=$/i # avoid == or ===
+ return find_method_or_attribute name[0..-2]
+ end
+
+ def find_method_or_attribute name # :nodoc:
+ return nil unless parent.respond_to? :ancestors
+
+ searched = parent.ancestors
+ kernel = RDoc::TopLevel.all_modules_hash['Kernel']
+
+ searched << kernel if kernel &&
+ parent != kernel && !searched.include?(kernel)
+
+ searched.each do |ancestor|
+ next if parent == ancestor
+ next if String === ancestor
+
+ other = ancestor.find_method_named('#' << name) ||
+ ancestor.find_attribute_named(name)
+
+ return other if other
+ end
+
+ nil
+ end
+
+ ##
+ # Abstract method. Contexts in their building phase call this
+ # to register a new alias for this known method/attribute.
+ #
+ # - creates a new AnyMethod/Attribute +newa+ named an_alias.new_name;
+ # - adds +self+ as +newa.is_alias_for+;
+ # - adds +newa+ to #aliases
+ # - adds +newa+ to the methods/attributes of +context+.
+
+ def add_alias(an_alias, context)
+ raise NotImplementedError
+ end
+
+ ##
+ # HTML fragment reference for this method
+
+ def aref
+ type = singleton ? 'c' : 'i'
+ # % characters are not allowed in html names => dash instead
+ "#{aref_prefix}-#{type}-#{html_name}"
+ end
+
+ ##
+ # Prefix for +aref+, defined by subclasses.
+
+ def aref_prefix
+ raise NotImplementedError
+ end
+
+ ##
+ # Attempts to sanitize the content passed by the ruby parser:
+ # remove outer parentheses, etc.
+
+ def block_params=(value)
+ # 'yield.to_s' or 'assert yield, msg'
+ return @block_params = '' if value =~ /^[\.,]/
+
+ # remove trailing 'if/unless ...'
+ return @block_params = '' if value =~ /^(if|unless)\s/
+
+ value = $1.strip if value =~ /^(.+)\s(if|unless)\s/
+
+ # outer parentheses
+ value = $1 if value =~ /^\s*\((.*)\)\s*$/
+ value = value.strip
+
+ # proc/lambda
+ return @block_params = $1 if value =~ /^(proc|lambda)(\s*\{|\sdo)/
+
+ # surrounding +...+ or [...]
+ value = $1.strip if value =~ /^\+(.*)\+$/
+ value = $1.strip if value =~ /^\[(.*)\]$/
+
+ return @block_params = '' if value.empty?
+
+ # global variable
+ return @block_params = 'str' if value =~ /^\$[&0-9]$/
+
+ # wipe out array/hash indices
+ value.gsub!(/(\w)\[[^\[]+\]/, '\1')
+
+ # remove @ from class/instance variables
+ value.gsub!(/@@?([a-z0-9_]+)/, '\1')
+
+ # method calls => method name
+ value.gsub!(/([A-Z:a-z0-9_]+)\.([a-z0-9_]+)(\s*\(\s*[a-z0-9_.,\s]*\s*\)\s*)?/) do
+ case $2
+ when 'to_s' then $1
+ when 'const_get' then 'const'
+ when 'new' then
+ $1.split('::').last. # ClassName => class_name
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
+ downcase
+ else
+ $2
+ end
+ end
+
+ # class prefixes
+ value.gsub!(/[A-Za-z0-9_:]+::/, '')
+
+ # simple expressions
+ value = $1 if value =~ /^([a-z0-9_]+)\s*[-*+\/]/
+
+ @block_params = value.strip
+ end
+
+ ##
+ # HTML id-friendly method/attribute name
+
+ def html_name
+ CGI.escape(@name.gsub('-', '-2D')).gsub('%','-').sub(/^-/, '')
+ end
+
+ ##
+ # Full method/attribute name including namespace
+
+ def full_name
+ @full_name || "#{parent_name}#{pretty_name}"
+ end
+
+ ##
+ # '::' for a class method/attribute, '#' for an instance method.
+
+ def name_prefix
+ singleton ? '::' : '#'
+ end
+
+ ##
+ # Method/attribute name with class/instance indicator
+
+ def pretty_name
+ "#{name_prefix}#{@name}"
+ end
+
+ ##
+ # Type of method/attribute (class or instance)
+
+ def type
+ singleton ? 'class' : 'instance'
+ end
+
+ ##
+ # Path to this method
+
+ def path
+ "#{@parent.path}##{aref}"
+ end
+
+ ##
+ # Name of our parent with special handling for un-marshaled methods
+
+ def parent_name
+ @parent_name || super
+ end
+
+ def pretty_print q # :nodoc:
+ alias_for = @is_alias_for ? "alias for #{@is_alias_for.name}" : nil
+
+ q.group 2, "[#{self.class.name} #{full_name} #{visibility}", "]" do
+ if alias_for then
+ q.breakable
+ q.text alias_for
+ end
+
+ if text then
+ q.breakable
+ q.text "text:"
+ q.breakable
+ q.pp @text
+ end
+
+ unless comment.empty? then
+ q.breakable
+ q.text "comment:"
+ q.breakable
+ q.pp @comment
+ end
+ end
+ end
+
+ def inspect # :nodoc:
+ alias_for = @is_alias_for ? " (alias for #{@is_alias_for.name})" : nil
+ "#<%s:0x%x %s (%s)%s>" % [
+ self.class, object_id,
+ full_name,
+ visibility,
+ alias_for,
+ ]
+ end
+
+ def to_s # :nodoc:
+ if @is_alias_for
+ "#{self.class.name}: #{full_name} -> #{is_alias_for}"
+ else
+ "#{self.class.name}: #{full_name}"
+ end
+ end
+
+end
+
diff --git a/lib/rdoc/normal_class.rb b/lib/rdoc/normal_class.rb
index e7ca6fffde..1ed8eaf974 100644
--- a/lib/rdoc/normal_class.rb
+++ b/lib/rdoc/normal_class.rb
@@ -6,10 +6,10 @@ require 'rdoc/class_module'
class RDoc::NormalClass < RDoc::ClassModule
##
- # Ancestor ClassModules
+ # Appends the superclass, if any, to the included modules.
def ancestors
- includes + [superclass]
+ superclass ? super + [superclass] : super
end
def inspect # :nodoc:
@@ -20,6 +20,15 @@ class RDoc::NormalClass < RDoc::ClassModule
]
end
+ def to_s # :nodoc:
+ display = "#{self.class.name} #{self.full_name}"
+ if superclass
+ display << ' < ' << (superclass.is_a?(String) ? superclass : superclass.full_name)
+ end
+ display << ' -> ' << is_alias_for.to_s if is_alias_for
+ display
+ end
+
def pretty_print q # :nodoc:
superclass = @superclass ? " < #{@superclass}" : nil
diff --git a/lib/rdoc/normal_module.rb b/lib/rdoc/normal_module.rb
index 92abe6b440..74a31f2668 100644
--- a/lib/rdoc/normal_module.rb
+++ b/lib/rdoc/normal_module.rb
@@ -5,11 +5,6 @@ require 'rdoc/class_module'
class RDoc::NormalModule < RDoc::ClassModule
- ##
- # Included NormalModules
-
- alias ancestors includes
-
def inspect # :nodoc:
"#<%s:0x%x module %s includes: %p attributes: %p methods: %p aliases: %p>" % [
self.class, object_id,
diff --git a/lib/rdoc/options.rb b/lib/rdoc/options.rb
index 90415f0aa4..810f7fac0a 100644
--- a/lib/rdoc/options.rb
+++ b/lib/rdoc/options.rb
@@ -8,9 +8,41 @@ require 'rdoc/ri/paths'
class RDoc::Options
##
- # Character-set
+ # The deprecated options.
+
+ DEPRECATED = {
+ '--accessor' => 'support discontinued',
+ '--diagram' => 'support discontinued',
+ '--help-output' => 'support discontinued',
+ '--image-format' => 'was an option for --diagram',
+ '--inline-source' => 'source code is now always inlined',
+ '--merge' => 'ri now always merges class information',
+ '--one-file' => 'support discontinued',
+ '--op-name' => 'support discontinued',
+ '--opname' => 'support discontinued',
+ '--promiscuous' => 'files always only document their content',
+ '--ri-system' => 'Ruby installers use other techniques',
+ }
- attr_reader :charset
+ ##
+ # Template option validator for OptionParser
+
+ Template = nil
+
+ ##
+ # Character-set for HTML output. #encoding is preferred over #charset
+
+ attr_accessor :charset
+
+ ##
+ # If true, RDoc will not write any files.
+
+ attr_accessor :dry_run
+
+ ##
+ # Encoding of output where. This is set via --encoding.
+
+ attr_accessor :encoding if Object.const_defined? :Encoding
##
# Files matching this pattern will be excluded
@@ -23,9 +55,20 @@ class RDoc::Options
attr_accessor :files
##
+ # Create the output even if the output directory does not look
+ # like an rdoc output directory
+
+ attr_accessor :force_output
+
+ ##
# Scan newer sources than the flag file if true.
- attr_reader :force_update
+ attr_accessor :force_update
+
+ ##
+ # Formatter to mark up text with
+
+ attr_accessor :formatter
##
# Description of the output generator (set with the <tt>-fmt</tt> option)
@@ -33,9 +76,21 @@ class RDoc::Options
attr_accessor :generator
##
- # Formatter to mark up text with
+ # Loaded generator options. Used to prevent --help from loading the same
+ # options multiple times.
- attr_accessor :formatter
+ attr_accessor :generator_options
+
+ ##
+ # Old rdoc behavior: hyperlink all words that match a method name,
+ # even if not preceded by '#' or '::'
+
+ attr_accessor :hyperlink_all
+
+ ##
+ # Include line numbers in the source code
+
+ attr_accessor :line_numbers
##
# Name of the file, class or module to display in the initial index page (if
@@ -44,44 +99,54 @@ class RDoc::Options
attr_accessor :main_page
##
+ # If true, only report on undocumented files
+
+ attr_accessor :coverage_report
+
+ ##
# The name of the output directory
attr_accessor :op_dir
##
- # Is RDoc in pipe mode?
+ # The OptionParser for this instance
- attr_accessor :pipe
+ attr_accessor :option_parser
##
- # Array of directories to search for files to satisfy an :include:
+ # Is RDoc in pipe mode?
- attr_reader :rdoc_include
+ attr_accessor :pipe
##
- # Include private and protected methods in the output?
+ # Array of directories to search for files to satisfy an :include:
- attr_accessor :show_all
+ attr_accessor :rdoc_include
##
# Include the '#' at the front of hyperlinked instance method names
- attr_reader :show_hash
+ attr_accessor :show_hash
##
# The number of columns in a tab
- attr_reader :tab_width
+ attr_accessor :tab_width
##
# Template to be used when generating output
- attr_reader :template
+ attr_accessor :template
+
+ ##
+ # Directory the template lives in
+
+ attr_accessor :template_dir
##
# Documentation title
- attr_reader :title
+ attr_accessor :title
##
# Verbosity, zero means quiet
@@ -91,29 +156,88 @@ class RDoc::Options
##
# URL of web cvs frontend
- attr_reader :webcvs
+ attr_accessor :webcvs
+
+ ##
+ # Minimum visibility of a documented method. One of +:public+,
+ # +:protected+, +:private+. May be overridden on a per-method
+ # basis with the :doc: directive.
+
+ attr_accessor :visibility
def initialize # :nodoc:
require 'rdoc/rdoc'
- @op_dir = nil
- @show_all = false
- @main_page = nil
+ @dry_run = false
@exclude = []
- @generators = RDoc::RDoc::GENERATORS
- @generator = RDoc::Generator::Darkfish
+ @force_output = false
+ @force_update = true
+ @generator = nil
@generator_name = nil
+ @generator_options = []
+ @generators = RDoc::RDoc::GENERATORS
+ @hyperlink_all = false
+ @line_numbers = false
+ @main_page = nil
+ @coverage_report = false
+ @op_dir = nil
+ @pipe = false
@rdoc_include = []
- @title = nil
- @template = nil
@show_hash = false
+ @stylesheet_url = nil
@tab_width = 8
- @force_update = true
+ @template = nil
+ @template_dir = nil
+ @title = nil
@verbosity = 1
- @pipe = false
-
+ @visibility = :protected
@webcvs = nil
- @charset = 'utf-8'
+ if Object.const_defined? :Encoding then
+ @encoding = Encoding.default_external
+ @charset = @encoding.to_s
+ else
+ @charset = 'UTF-8'
+ end
+ end
+
+ ##
+ # Check that the files on the command line exist
+
+ def check_files
+ @files.delete_if do |file|
+ if File.exist? file then
+ if File.readable? file then
+ false
+ else
+ warn "file '#{file}' not readable"
+
+ true
+ end
+ else
+ warn "file '#{file}' not found"
+
+ true
+ end
+ end
+ end
+
+ ##
+ # Ensure only one generator is loaded
+
+ def check_generator
+ if @generator then
+ raise OptionParser::InvalidOption,
+ "generator already set to #{@generator_name}"
+ end
+ end
+
+ ##
+ # Set the title, but only if not already set. Used to set the title
+ # from a source file, so that a title set from the command line
+ # will have the priority.
+
+ def default_title=(string)
+ @title ||= string
end
##
@@ -122,7 +246,10 @@ class RDoc::Options
def parse(argv)
ignore_invalid = true
+ argv.insert(0, *ENV['RDOCOPT'].split) if ENV['RDOCOPT']
+
opts = OptionParser.new do |opt|
+ @option_parser = opt
opt.program_name = File.basename $0
opt.version = RDoc::VERSION
opt.release = nil
@@ -139,6 +266,14 @@ Usage: #{opt.program_name} [options] [names...]
How RDoc generates output depends on the output formatter being used, and on
the options you give.
+ Options can be specified via the RDOCOPT environment variable, which
+ functions similar to the RUBYOPT environment variable for ruby.
+
+ $ export RDOCOPT="--show-hash"
+
+ will make rdoc show hashes in method links by default. Command-line options
+ always will override those in RDOCOPT.
+
- Darkfish creates frameless HTML output by Michael Granger.
- ri creates ri data files
@@ -156,14 +291,44 @@ Usage: #{opt.program_name} [options] [names...]
opt.banner << " - #{parser}: #{regexp.join ', '}\n"
end
+ opt.banner << "\n The following options are deprecated:\n\n"
+
+ name_length = DEPRECATED.keys.sort_by { |k| k.length }.last.length
+
+ DEPRECATED.sort_by { |k,| k }.each do |name, reason|
+ opt.banner << " %*1$2$s %3$s\n" % [-name_length, name, reason]
+ end
+
+ opt.accept Template do |template|
+ template_dir = template_dir_for template
+
+ unless template_dir then
+ warn "could not find template #{template}"
+ nil
+ else
+ [template, template_dir]
+ end
+ end
+
opt.separator nil
- opt.separator "Parsing Options:"
+ opt.separator "Parsing options:"
opt.separator nil
+ if Object.const_defined? :Encoding then
+ opt.on("--encoding=ENCODING", "-e", Encoding.list.map { |e| e.name },
+ "Specifies the output encoding. All files",
+ "read will be converted to this encoding.",
+ "Preferred over --charset") do |value|
+ @encoding = Encoding.find value
+ @charset = @encoding.to_s # may not be valid value
+ end
+
+ opt.separator nil
+ end
+
opt.on("--all", "-a",
- "Include all methods (not just public) in",
- "the output.") do |value|
- @show_all = value
+ "Synonym for --visibility=private.") do |value|
+ @visibility = :private
end
opt.separator nil
@@ -207,20 +372,41 @@ Usage: #{opt.program_name} [options] [names...]
end
opt.separator nil
- opt.separator "Generator Options:"
+
+ opt.on("--tab-width=WIDTH", "-w", OptionParser::DecimalInteger,
+ "Set the width of tab characters.") do |value|
+ @tab_width = value
+ end
+
opt.separator nil
- opt.on("--charset=CHARSET", "-c",
- "Specifies the output HTML character-set.") do |value|
- @charset = value
+ opt.on("--visibility=VISIBILITY", "-V", RDoc::VISIBILITIES,
+ "Minimum visibility to document a method.",
+ "One of 'public', 'protected' (the default)",
+ "or 'private'. Can be abbreviated.") do |value|
+ @visibility = value
+ end
+
+ opt.separator nil
+ opt.separator "Common generator options:"
+ opt.separator nil
+
+ opt.on("--force-output", "-O",
+ "Forces rdoc to write the output files,",
+ "even if the output directory exists",
+ "and does not seem to have been created",
+ "by rdoc.") do |value|
+ @force_output = value
end
opt.separator nil
generator_text = @generators.keys.map { |name| " #{name}" }.sort
- opt.on("--fmt=FORMAT", "--format=FORMAT", "-f", @generators.keys,
+ opt.on("-f", "--fmt=FORMAT", "--format=FORMAT", @generators.keys,
"Set the output formatter. One of:", *generator_text) do |value|
+ check_generator
+
@generator_name = value.downcase
setup_generator
end
@@ -236,9 +422,11 @@ Usage: #{opt.program_name} [options] [names...]
opt.separator nil
- opt.on("--main=NAME", "-m",
- "NAME will be the initial page displayed.") do |value|
- @main_page = value
+ opt.on("--[no-]coverage-report", "--[no-]dcov", "-C",
+ "Prints a report on undocumented items.",
+ "Does not generate files.") do |value|
+ @coverage_report = value
+ @force_update = true if value
end
opt.separator nil
@@ -250,6 +438,51 @@ Usage: #{opt.program_name} [options] [names...]
opt.separator nil
+ opt.on("-d",
+ "Deprecated --diagram option.",
+ "Prevents firing debug mode",
+ "with legacy invocation.") do |value|
+ end
+
+ opt.separator nil
+ opt.separator 'HTML generator options:'
+ opt.separator nil
+
+ opt.on("--charset=CHARSET", "-c",
+ "Specifies the output HTML character-set.",
+ "Use --encoding instead of --charset if",
+ "available.") do |value|
+ @charset = value
+ end
+
+ opt.separator nil
+
+ opt.on("--hyperlink-all", "-A",
+ "Generate hyperlinks for all words that",
+ "correspond to known methods, even if they",
+ "do not start with '#' or '::' (legacy",
+ "behavior).") do |value|
+ @hyperlink_all = value
+ end
+
+ opt.separator nil
+
+ opt.on("--main=NAME", "-m",
+ "NAME will be the initial page displayed.") do |value|
+ @main_page = value
+ end
+
+ opt.separator nil
+
+ opt.on("--[no-]line-numbers", "-N",
+ "Include line numbers in the source code.",
+ "By default, only the number of the first",
+ "line is displayed, in a leading comment.") do |value|
+ @line_numbers = value
+ end
+
+ opt.separator nil
+
opt.on("--show-hash", "-H",
"A name of the form #name in a comment is a",
"possible hyperlink to an instance method",
@@ -260,17 +493,12 @@ Usage: #{opt.program_name} [options] [names...]
opt.separator nil
- opt.on("--tab-width=WIDTH", "-w", OptionParser::DecimalInteger,
- "Set the width of tab characters.") do |value|
- @tab_width = value
- end
-
- opt.separator nil
-
- opt.on("--template=NAME", "-T",
+ opt.on("--template=NAME", "-T", Template,
"Set the template used when generating",
- "output.") do |value|
- @template = value
+ "output. The default depends on the",
+ "formatter used.") do |(template, template_dir)|
+ @template = template
+ @template_dir = template_dir
end
opt.separator nil
@@ -292,11 +520,7 @@ Usage: #{opt.program_name} [options] [names...]
end
opt.separator nil
-
- opt.on("-d", "--diagram", "Prevents -d from tripping --debug")
-
- opt.separator nil
- opt.separator "ri Generator Options:"
+ opt.separator "ri generator options:"
opt.separator nil
opt.on("--ri", "-r",
@@ -305,6 +529,8 @@ Usage: #{opt.program_name} [options] [names...]
"your home directory unless overridden by a",
"subsequent --op parameter, so no special",
"privileges are needed.") do |value|
+ check_generator
+
@generator_name = "ri"
@op_dir ||= RDoc::RI::Paths::HOMEDIR
setup_generator
@@ -317,22 +543,30 @@ Usage: #{opt.program_name} [options] [names...]
"are stored in a site-wide directory,",
"making them accessible to others, so",
"special privileges are needed.") do |value|
+ check_generator
+
@generator_name = "ri"
@op_dir = RDoc::RI::Paths::SITEDIR
setup_generator
end
opt.separator nil
- opt.separator "Generic Options:"
+ opt.separator "Generic options:"
opt.separator nil
+ opt.on("--[no-]dry-run",
+ "Don't write any files") do |value|
+ @dry_run = value
+ end
+
opt.on("-D", "--[no-]debug",
"Displays lots on internal stuff.") do |value|
$DEBUG_RDOC = value
end
opt.on("--[no-]ignore-invalid",
- "Ignore invalid options and continue.") do |value|
+ "Ignore invalid options and continue",
+ "(default true).") do |value|
ignore_invalid = value
end
@@ -342,38 +576,70 @@ Usage: #{opt.program_name} [options] [names...]
end
opt.on("--verbose", "-v",
- "Display extra progress as we parse.") do |value|
+ "Display extra progress as RDoc parses") do |value|
@verbosity = 2
end
+ opt.on("--help",
+ "Display this help") do
+ RDoc::RDoc::GENERATORS.each_key do |generator|
+ setup_generator generator
+ end
+
+ puts opt.help
+ exit
+ end
+
opt.separator nil
end
- argv.insert(0, *ENV['RDOCOPT'].split) if ENV['RDOCOPT']
- ignored = []
+ setup_generator 'darkfish' if
+ argv.grep(/\A(-f|--fmt|--format|-r|-R|--ri|--ri-site)\b/).empty?
+
+ deprecated = []
+ invalid = []
begin
opts.parse! argv
rescue OptionParser::InvalidArgument, OptionParser::InvalidOption => e
- if ignore_invalid then
- ignored << e.args.join(' ')
- retry
+ if DEPRECATED[e.args.first] then
+ deprecated << e.args.first
+ elsif %w[--format --ri -r --ri-site -R].include? e.args.first then
+ raise
else
- $stderr.puts opts
- $stderr.puts
- $stderr.puts e
- exit 1
+ invalid << e.args.join(' ')
end
+
+ retry
+ end
+
+ unless @generator then
+ @generator = RDoc::Generator::Darkfish
+ @generator_name = 'darkfish'
end
if @pipe and not argv.empty? then
@pipe = false
- ignored << '-p (with files)'
+ invalid << '-p (with files)'
end
- unless ignored.empty? or quiet then
- $stderr.puts "invalid options: #{ignored.join ', '}"
- $stderr.puts '(invalid options are ignored)'
+ unless quiet then
+ deprecated.each do |opt|
+ $stderr.puts 'option ' << opt << ' is deprecated: ' << DEPRECATED[opt]
+ end
+
+ unless invalid.empty? then
+ invalid = "invalid options: #{invalid.join ', '}"
+
+ if ignore_invalid then
+ $stderr.puts invalid
+ $stderr.puts '(invalid options are ignored)'
+ else
+ $stderr.puts opts
+ $stderr.puts invalid
+ exit 1
+ end
+ end
end
@op_dir ||= 'doc'
@@ -392,15 +658,10 @@ Usage: #{opt.program_name} [options] [names...]
# If no template was specified, use the default template for the output
# formatter
- @template ||= @generator_name
- end
-
- ##
- # Set the title, but only if not already set. This means that a title set
- # from the command line trumps one set in a source file
-
- def title=(string)
- @title ||= string
+ unless @template then
+ @template = @generator_name
+ @template_dir = template_dir_for @template
+ end
end
##
@@ -410,30 +671,46 @@ Usage: #{opt.program_name} [options] [names...]
@verbosity.zero?
end
- def quiet=(bool)
+ ##
+ # Set quietness to +bool+
+
+ def quiet= bool
@verbosity = bool ? 0 : 1
end
- private
-
##
- # Set up an output generator for the format in @generator_name
+ # Set up an output generator for the named +generator_name+.
+ #
+ # If the found generator responds to :setup_options it will be called with
+ # the options instance. This allows generators to add custom options or set
+ # default options.
- def setup_generator
- @generator = @generators[@generator_name]
+ def setup_generator generator_name = @generator_name
+ @generator = @generators[generator_name]
unless @generator then
- raise OptionParser::InvalidArgument, "Invalid output formatter"
+ raise OptionParser::InvalidArgument,
+ "Invalid output formatter #{generator_name}"
end
+
+ return if @generator_options.include? @generator
+
+ @generator_name = generator_name
+ @generator_options << @generator
+
+ @generator.setup_options self if @generator.respond_to? :setup_options
end
##
- # Check that the files on the command line exist
+ # Finds the template dir for +template+
- def check_files
- @files.each do |f|
- stat = File.stat f rescue next
- raise RDoc::Error, "file '#{f}' not readable" unless stat.readable?
+ def template_dir_for template
+ template_path = File.join 'rdoc', 'generator', 'template', template
+
+ $LOAD_PATH.map do |path|
+ File.join File.expand_path(path), template_path
+ end.find do |dir|
+ File.directory? dir
end
end
diff --git a/lib/rdoc/parser.rb b/lib/rdoc/parser.rb
index 1e4ab7c7a4..d218aba62a 100644
--- a/lib/rdoc/parser.rb
+++ b/lib/rdoc/parser.rb
@@ -1,6 +1,6 @@
require 'rdoc'
require 'rdoc/code_objects'
-require 'rdoc/markup/preprocess'
+require 'rdoc/markup/pre_process'
require 'rdoc/stats'
##
@@ -43,7 +43,15 @@ class RDoc::Parser
@parsers = []
class << self
+
+ ##
+ # A Hash that maps file exetensions regular expressions to parsers that
+ # will consume them.
+ #
+ # Use parse_files_matching to register a parser's file extensions.
+
attr_reader :parsers
+
end
##
@@ -67,18 +75,51 @@ class RDoc::Parser
# content that an RDoc parser shouldn't try to consume.
def self.binary?(file)
+ return false if file =~ /\.(rdoc|txt)$/
+
s = File.read(file, 1024) or return false
- if s[0, 2] == Marshal.dump('')[0, 2] then
- true
- elsif file =~ /erb\.rb$/ then
- false
- elsif s.scan(/<%|%>/).length >= 4 || s.index("\x00") then
- true
- elsif 0.respond_to? :fdiv then
- s.count("\x00-\x7F", "^ -~\t\r\n").fdiv(s.size) > 0.3
- else # HACK 1.8.6
- (s.count("\x00-\x7F", "^ -~\t\r\n").to_f / s.size) > 0.3
+ have_encoding = s.respond_to? :encoding
+
+ if have_encoding then
+ return false if s.encoding != Encoding::ASCII_8BIT and s.valid_encoding?
+ end
+
+ return true if s[0, 2] == Marshal.dump('')[0, 2] or s.index("\x00")
+
+ if have_encoding then
+ s.force_encoding Encoding.default_external
+
+ not s.valid_encoding?
+ else
+ if 0.respond_to? :fdiv then
+ s.count("\x00-\x7F", "^ -~\t\r\n").fdiv(s.size) > 0.3
+ else # HACK 1.8.6
+ (s.count("\x00-\x7F", "^ -~\t\r\n").to_f / s.size) > 0.3
+ end
+ end
+ end
+
+ ##
+ # Processes common directives for CodeObjects for the C and Ruby parsers.
+ #
+ # Applies +directive+'s +value+ to +code_object+, if appropriate
+
+ def self.process_directive code_object, directive, value
+ case directive
+ when 'nodoc' then
+ code_object.document_self = nil # notify nodoc
+ code_object.document_children = value.downcase != 'all'
+ when 'doc' then
+ code_object.document_self = true
+ code_object.force_documentation = true
+ when 'yield', 'yields' then
+ # remove parameter &block
+ code_object.params.sub!(/,?\s*&\w+/, '') if code_object.params
+
+ code_object.block_params = value
+ when 'arg', 'args' then
+ code_object.params = value
end
end
@@ -143,6 +184,12 @@ class RDoc::Parser
RDoc::Parser.parsers.unshift [regexp, self]
end
+ ##
+ # Creates a new Parser storing +top_level+, +file_name+, +content+,
+ # +options+ and +stats+ in instance variables.
+ #
+ # Usually invoked by +super+
+
def initialize(top_level, file_name, content, options, stats)
@top_level = top_level
@file_name = file_name
diff --git a/lib/rdoc/parser/c.rb b/lib/rdoc/parser/c.rb
index e218298dfe..60e9fefd61 100644
--- a/lib/rdoc/parser/c.rb
+++ b/lib/rdoc/parser/c.rb
@@ -3,9 +3,9 @@ require 'rdoc/parser/ruby'
require 'rdoc/known_classes'
##
-# We attempt to parse C extension files. Basically we look for
+# RDoc::Parser::C attempts to parse C extension files. It looks 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
+# rb_define_method</tt> and so on. It tries to find the corresponding
# C source for the methods and extract comments, but if we fail
# we don't worry too much.
#
@@ -49,13 +49,26 @@ require 'rdoc/known_classes'
#
# 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-class: +name+]
+# Documentation for the named class.
#
-# [Document-method: <i>name</i>]
-# This comment documents the named method. Use when RDoc cannot
-# automatically find the method from it's declaration
+# [Document-module: +name+]
+# Documentation for the named module.
+#
+# [Document-const: +name+]
+# Documentation for the named +rb_define_const+.
+#
+# [Document-global: +name+]
+# Documentation for the named +rb_define_global_const+
+#
+# [Document-variable: +name+]
+# Documentation for the named +rb_define_variable+
+#
+# [Document-method: +name+]
+# Documentation for the named method.
+#
+# [Document-attr: +name+]
+# Documentation for the named attribute.
#
# [call-seq: <i>text up to an empty line</i>]
# Because C source doesn't give descripive names to Ruby-level parameters,
@@ -120,21 +133,61 @@ class RDoc::Parser::C < RDoc::Parser
@known_classes = RDoc::KNOWN_CLASSES.dup
@content = handle_tab_width handle_ifdefs_in(@content)
@classes = Hash.new
+ @singleton_classes = Hash.new
@file_dir = File.dirname(@file_name)
end
+ ##
+ # Scans #content for rb_define_alias
+
def do_aliases
- @content.scan(%r{rb_define_alias\s*\(\s*(\w+),\s*"([^"]+)",\s*"([^"]+)"\s*\)}m) do
- |var_name, new_name, old_name|
+ @content.scan(/rb_define_alias\s*\(
+ \s*(\w+),
+ \s*"(.+?)",
+ \s*"(.+?)"
+ \s*\)/xm) do |var_name, new_name, old_name|
class_name = @known_classes[var_name] || var_name
- class_obj = find_class(var_name, class_name)
+ class_obj = find_class var_name, class_name
+
+ al = RDoc::Alias.new '', old_name, new_name, ''
+ al.singleton = @singleton_classes.key?(var_name)
+
+ comment = find_alias_comment var_name, new_name, old_name
+ comment = strip_stars comment
+ al.comment = comment
+
+ class_obj.add_alias al
+ @stats.add_alias al
+ end
+ end
- as = class_obj.add_alias RDoc::Alias.new("", old_name, new_name, "")
+ ##
+ # Scans #content for rb_attr and rb_define_attr
+
+ def do_attrs
+ @content.scan(/rb_attr\s*\(
+ \s*(\w+),
+ \s*([\w"()]+),
+ \s*([01]),
+ \s*([01]),
+ \s*\w+\);/xm) do |var_name, attr_name, read, write|
+ handle_attr var_name, attr_name, read, write
+ end
- @stats.add_alias as
+ @content.scan(%r%rb_define_attr\(
+ \s*([\w\.]+),
+ \s*"([^"]+)",
+ \s*(\d+),
+ \s*(\d+)\s*\);
+ %xm) do |var_name, attr_name, read, write|
+ handle_attr var_name, attr_name, read, write
end
end
+ ##
+ # Scans #content for rb_define_module, rb_define_class, boot_defclass,
+ # rb_define_module_under, rb_define_class_under and rb_singleton_class
+
def do_classes
@content.scan(/(\w+)\s* = \s*rb_define_module\s*\(\s*"(\w+)"\s*\)/mx) do
|var_name, class_name|
@@ -165,35 +218,44 @@ class RDoc::Parser::C < RDoc::Parser
end
@content.scan(/([\w\.]+)\s* = \s*rb_define_class_under\s*
- \(
- \s*(\w+),
- \s*"(\w+)",
- \s*([\w\*\s\(\)\.\->]+)\s* # for SWIG
- \s*\)/mx) do |var_name, in_module, class_name, parent|
+ \(
+ \s*(\w+),
+ \s*"(\w+)",
+ \s*([\w\*\s\(\)\.\->]+)\s* # for SWIG
+ \s*\)/mx) do |var_name, in_module, class_name, parent|
handle_class_module(var_name, "class", class_name, parent, in_module)
end
+
+ @content.scan(/([\w\.]+)\s* = \s*rb_singleton_class\s*
+ \(
+ \s*(\w+)
+ \s*\)/mx) do |sclass_var, class_var|
+ handle_singleton sclass_var, class_var
+ end
end
+ ##
+ # Scans #content for rb_define_variable, rb_define_readonly_variable,
+ # rb_define_const and rb_define_global_const
+
def do_constants
- @content.scan(%r{\Wrb_define_
+ @content.scan(%r%\Wrb_define_
( variable |
readonly_variable |
const |
- global_const | )
+ global_const )
\s*\(
(?:\s*(\w+),)?
\s*"(\w+)",
\s*(.*?)\s*\)\s*;
- }xm) do |type, var_name, const_name, definition|
+ %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);
+ # Scans #content for rb_include_module
def do_includes
@content.scan(/rb_include_module\s*\(\s*(\w+?),\s*(\w+?)\s*\)/) do |c,m|
@@ -204,8 +266,13 @@ class RDoc::Parser::C < RDoc::Parser
end
end
+ ##
+ # Scans #content for rb_define_method, rb_define_singleton_method,
+ # rb_define_module_function, rb_define_private_method,
+ # rb_define_global_function and define_filetest_function
+
def do_methods
- @content.scan(%r{rb_define_
+ @content.scan(%r%rb_define_
(
singleton_method |
method |
@@ -217,8 +284,7 @@ class RDoc::Parser::C < RDoc::Parser
\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|
+ %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"
@@ -231,44 +297,69 @@ class RDoc::Parser::C < RDoc::Parser
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*\(
+ @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|
+ %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|
+ \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
+ ##
+ # Finds the comment for an alias on +class_name+ from +new_name+ to
+ # +old_name+
+
+ def find_alias_comment class_name, new_name, old_name
+ content =~ %r%((?>/\*.*?\*/\s+))
+ rb_define_alias\(\s*#{Regexp.escape class_name}\s*,
+ \s*"#{Regexp.escape new_name}"\s*,
+ \s*"#{Regexp.escape old_name}"\s*\);%xm
+
+ $1 || ''
+ end
+
+ ##
+ # Finds a comment for rb_define_attr, rb_attr or Document-attr.
+ #
+ # +var_name+ is the C class variable the attribute is defined on.
+ # +attr_name+ is the attribute's name.
+ #
+ # +read+ and +write+ are the read/write flags ('1' or '0'). Either both or
+ # neither must be provided.
+
+ def find_attr_comment var_name, attr_name, read = nil, write = nil
+ attr_name = Regexp.escape attr_name
+
+ rw = if read and write then
+ /\s*#{read}\s*,\s*#{write}\s*/xm
+ else
+ /.*?/m
+ end
+
+ if @content =~ %r%((?>/\*.*?\*/\s+))
+ rb_define_attr\((?:\s*#{var_name},)?\s*
+ "#{attr_name}"\s*,
+ #{rw}\)\s*;%xm then
+ $1
+ elsif @content =~ %r%((?>/\*.*?\*/\s+))
+ rb_attr\(\s*#{var_name}\s*,
+ \s*#{attr_name}\s*,
+ #{rw},.*?\)\s*;%xm then
$1
- elsif @content =~ %r{Document-attr:\s#{attr_name}\s*?\n((?>.*?\*/))}m
+ elsif @content =~ %r%Document-attr:\s#{attr_name}\s*?\n
+ ((?>.*?\*/))%xm then
$1
else
''
@@ -280,8 +371,10 @@ class RDoc::Parser::C < RDoc::Parser
def find_body(class_name, meth_name, meth_obj, body, quiet = false)
case body
- when %r"((?>/\*.*?\*/\s*))((?:(?:static|SWIGINTERN)\s+)?(?:intern\s+)?VALUE\s+#{meth_name}
- \s*(\([^)]*\))([^;]|$))"xm
+ when %r%((?>/\*.*?\*/\s*))
+ ((?:(?:static|SWIGINTERN)\s+)?
+ (?:intern\s+)?VALUE\s+#{meth_name}
+ \s*(\([^)]*\))([^;]|$))%xm then
comment = $1
body_text = $2
@@ -303,12 +396,13 @@ class RDoc::Parser::C < RDoc::Parser
find_modifiers comment, meth_obj if comment
+ #meth_obj.params = params
meth_obj.start_collecting_tokens
tk = RDoc::RubyToken::Token.new nil, 1, 1
tk.set_text body_text
meth_obj.add_token tk
meth_obj.comment = strip_stars comment
- when %r{((?>/\*.*?\*/\s*))^\s*(\#\s*define\s+#{meth_name}\s+(\w+))}m
+ when %r%((?>/\*.*?\*/\s*))^\s*(\#\s*define\s+#{meth_name}\s+(\w+))%m
comment = $1
body_text = $2
find_body class_name, $3, meth_obj, body, true
@@ -319,26 +413,29 @@ class RDoc::Parser::C < RDoc::Parser
tk.set_text body_text
meth_obj.add_token tk
meth_obj.comment = strip_stars(comment) + meth_obj.comment.to_s
- when %r{^\s*\#\s*define\s+#{meth_name}\s+(\w+)}m
+ when %r%^\s*\#\s*define\s+#{meth_name}\s+(\w+)%m
unless find_body(class_name, $1, meth_obj, body, true)
- warn "No definition for #{meth_name}" unless @options.quiet
+ warn "No definition for #{meth_name}" if @options.verbosity > 1
return false
end
- else
- # No body, but might still have an override comment
- comment = find_override_comment(class_name, meth_obj.name)
+ else # No body, but might still have an override comment
+ comment = find_override_comment class_name, meth_obj.name
if comment
- find_modifiers(comment, meth_obj)
+ find_modifiers comment, meth_obj
meth_obj.comment = strip_stars comment
else
- warn "No definition for #{meth_name}" unless @options.quiet
+ warn "No definition for #{meth_name}" if @options.verbosity > 1
return false
end
end
+
true
end
+ ##
+ # Finds a RDoc::NormalClass or RDoc::NormalModule for +raw_name+
+
def find_class(raw_name, name)
unless @classes[raw_name]
if raw_name =~ /^rb_m/
@@ -382,16 +479,17 @@ class RDoc::Parser::C < RDoc::Parser
def find_class_comment(class_name, class_mod)
comment = nil
- if @content =~ %r{
+ if @content =~ %r%
((?>/\*.*?\*/\s+))
(static\s+)?
void\s+
- Init_#{class_name}\s*(?:_\(\s*)?\(\s*(?:void\s*)?\)}xmi then # )
- comment = $1
- elsif @content =~ %r{Document-(?:class|module):\s+#{class_name}\s*?(?:<\s+[:,\w]+)?\n((?>.*?\*/))}m then
+ Init_#{class_name}\s*(?:_\(\s*)?\(\s*(?:void\s*)?\)%xmi then
+ comment = $1.sub(%r%Document-(?:class|module):\s+#{class_name}%, '')
+ elsif @content =~ %r%Document-(?:class|module):\s+#{class_name}\s*?
+ (?:<\s+[:,\w]+)?\n((?>.*?\*/))%xm then
comment = $1
- elsif @content =~ %r{((?>/\*.*?\*/\s+))
- ([\w\.\s]+\s* = \s+)?rb_define_(class|module).*?"(#{class_name})"}xm then # "
+ elsif @content =~ %r%((?>/\*.*?\*/\s+))
+ ([\w\.\s]+\s* = \s+)?rb_define_(class|module).*?"(#{class_name})"%xm then
comment = $1
end
@@ -409,10 +507,13 @@ class RDoc::Parser::C < RDoc::Parser
# 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
+ if @content =~ %r%((?>^\s*/\*.*?\*/\s+))
+ rb_define_#{type}\((?:\s*(\w+),)?\s*
+ "#{const_name}"\s*,
+ .*?\)\s*;%xmi then
$1
- elsif @content =~ %r{Document-(?:const|global|variable):\s#{const_name}\s*?\n((?>.*?\*/))}m
+ elsif @content =~ %r%Document-(?:const|global|variable):\s#{const_name}
+ \s*?\n((?>.*?\*/))%xm
$1
else
''
@@ -420,56 +521,111 @@ class RDoc::Parser::C < RDoc::Parser
end
##
- # If the comment block contains a section that looks like:
+ # Handles modifiers in +comment+ and updates +meth_obj+ as appropriate.
+ #
+ # If <tt>:nodoc:</tt> is found, documentation on +meth_obj+ is suppressed.
+ #
+ # If <tt>:yields:</tt> is followed by an argument list it is used for the
+ # #block_params of +meth_obj+.
+ #
+ # If the comment block contains a <tt>call-seq:</tt> section like:
+ #
+ # call-seq:
+ # ARGF.readlines(sep=$/) -> array
+ # ARGF.readlines(limit) -> array
+ # ARGF.readlines(sep, limit) -> array
#
- # call-seq:
- # Array.new
- # Array.new(10)
+ # ARGF.to_a(sep=$/) -> array
+ # ARGF.to_a(limit) -> array
+ # ARGF.to_a(sep, limit) -> array
#
- # use it for the parameters.
+ # it is used for the parameters of +meth_obj+.
+
+ def find_modifiers comment, meth_obj
+ # we must handle situations like the above followed by an unindented first
+ # comment. The difficulty is to make sure not to match lines starting
+ # with ARGF at the same indent, but that are after the first description
+ # paragraph.
+
+ if comment =~ /call-seq:(.*?[^\s\*].*?)^\s*\*?\s*$/m then
+ all_start, all_stop = $~.offset(0)
+ seq_start, seq_stop = $~.offset(1)
+
+ # we get the following lines that start with the leading word at the
+ # same indent, even if they have blank lines before
+ if $1 =~ /(^\s*\*?\s*\n)+^(\s*\*?\s*\w+)/m then
+ leading = $2 # ' * ARGF' in the example above
+ re = %r%
+ \A(
+ (^\s*\*?\s*\n)+
+ (^#{Regexp.escape leading}.*?\n)+
+ )+
+ ^\s*\*?\s*$
+ %xm
+ if comment[seq_stop..-1] =~ re then
+ all_stop = seq_stop + $~.offset(0).last
+ seq_stop = seq_stop + $~.offset(1).last
+ end
+ end
- 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*/, '')
+ seq = comment[seq_start..seq_stop]
+ seq.gsub!(/^(\s*\*?\s*?)(\S|\n)/m, '\2')
+ comment.slice! all_start...all_stop
meth_obj.call_seq = seq
+ elsif comment.sub!(/\A\/\*\s*call-seq:(.*?)\*\/\Z/, '') then
+ meth_obj.call_seq = $1.strip
+ end
+
+ if comment.sub!(/\s*:(nodoc|doc|yields?|args?):\s*(.*)/, '') then
+ RDoc::Parser.process_directive meth_obj, $1, $2
end
end
+ ##
+ # Finds a <tt>Document-method</tt> override for +meth_name+ in +class_name+
+
def find_override_comment(class_name, meth_name)
name = Regexp.escape(meth_name)
- if @content =~ %r{Document-method:\s+#{class_name}(?:\.|::|#)#{name}\s*?\n((?>.*?\*/))}m then
+
+ if @content =~ %r%Document-method:\s+#{class_name}(?:\.|::|#)#{name}\s*?\n((?>.*?\*/))%m then
$1
- elsif @content =~ %r{Document-method:\s#{name}\s*?\n((?>.*?\*/))}m then
+ elsif @content =~ %r%Document-method:\s#{name}\s*?\n((?>.*?\*/))%m then
$1
end
end
- def handle_attr(var_name, attr_name, reader, writer)
+ ##
+ # Creates a new RDoc::Attr +attr_name+ on class +var_name+ that is either
+ # +read+, +write+ or both
+
+ def handle_attr(var_name, attr_name, read, write)
rw = ''
- rw << 'R' if reader
- rw << 'W' if writer
+ rw << 'R' if '1' == read
+ rw << 'W' if '1' == write
class_name = @known_classes[var_name]
return unless class_name
- class_obj = find_class(var_name, class_name)
+ class_obj = find_class var_name, class_name
+
+ return unless class_obj
- if class_obj
- comment = find_attr_comment(attr_name)
- comment = strip_stars comment
- att = RDoc::Attr.new '', attr_name, rw, comment
- @stats.add_method att
- class_obj.add_attribute(att)
- end
+ comment = find_attr_comment var_name, attr_name
+ comment = strip_stars comment
+
+ name = attr_name.gsub(/rb_intern\("([^"]+)"\)/, '\1')
+
+ attr = RDoc::Attr.new '', name, rw, comment
+
+ class_obj.add_attribute attr
+ @stats.add_attribute attr
end
+ ##
+ # Creates a new RDoc::NormalClass or RDoc::NormalModule based on +type+
+ # named +class_name+ in +parent+ which was assigned to the C +var_name+.
+
def handle_class_module(var_name, type, class_name, parent, in_module)
parent_name = @known_classes[parent] || parent
@@ -497,7 +653,7 @@ class RDoc::Parser::C < RDoc::Parser
class_name
end
- if @content =~ %r{Document-class:\s+#{full_name}\s*<\s+([:,\w]+)} then
+ if @content =~ %r%Document-class:\s+#{full_name}\s*<\s+([:,\w]+)% then
parent_name = $1
end
@@ -519,15 +675,14 @@ class RDoc::Parser::C < RDoc::Parser
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.
+ # Adds constants. By providing some_value: at the start of the 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 (\:).
+ # Will override <tt>INT2FIX(300)</tt> with the value +300+ in the output
+ # RDoc. Values may include quotes and escaped colons (\:).
def handle_constants(type, var_name, const_name, definition)
class_name = @known_classes[var_name]
@@ -588,22 +743,35 @@ class RDoc::Parser::C < RDoc::Parser
body.gsub(/^#ifdef HAVE_PROTOTYPES.*?#else.*?\n(.*?)#endif.*?\n/m, '\1')
end
+ ##
+ # Adds an RDoc::AnyMethod +meth_name+ defined on a class or module assigned
+ # to +var_name+. +type+ is the type of method definition function used.
+ # +singleton_method+ and +module_function+ create a singleton method.
+
def handle_method(type, var_name, meth_name, meth_body, param_count,
source_file = nil)
+ singleton = false
class_name = @known_classes[var_name]
+ unless class_name then
+ class_name = @singleton_classes[var_name]
+ singleton = true if class_name
+ end
+
return unless class_name
class_obj = find_class var_name, class_name
if class_obj then
- if meth_name == "initialize" then
- meth_name = "new"
- type = "singleton_method"
+ if meth_name == 'initialize' then
+ meth_name = 'new'
+ singleton = true
+ type = 'method' # force public
end
meth_obj = RDoc::AnyMethod.new '', meth_name
- meth_obj.singleton = %w[singleton_method module_function].include? type
+ meth_obj.singleton =
+ singleton || %w[singleton_method module_function].include?(type)
p_count = Integer(param_count) rescue -1
@@ -627,7 +795,8 @@ class RDoc::Parser::C < RDoc::Parser
body = @content
end
- if find_body(class_name, meth_body, meth_obj, body) and meth_obj.document_self then
+ if find_body(class_name, meth_body, meth_obj, body) and
+ meth_obj.document_self then
class_obj.add_method meth_obj
@stats.add_method meth_obj
meth_obj.visibility = :private if 'private_method' == type
@@ -635,13 +804,27 @@ class RDoc::Parser::C < RDoc::Parser
end
end
+ ##
+ # Registers a singleton class +sclass_var+ as a singleton of +class_var+
+
+ def handle_singleton sclass_var, class_var
+ class_name = @known_classes[class_var]
+
+ @singleton_classes[sclass_var] = class_name
+ end
+
+ ##
+ # Normalizes tabs in +body+
+
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)} && $~ #`
+ 1 while line.gsub!(/\t+/) do
+ ' ' * (tab_width * $&.length - $`.length % tab_width)
+ end && $~
line
- end .join("\n")
+ end.join "\n"
else
body
end
@@ -654,7 +837,7 @@ class RDoc::Parser::C < RDoc::Parser
# * :title: My Awesome Project
# */
#
- # This routine modifies it's parameter
+ # This routine modifies its parameter
def look_for_directives_in(context, comment)
preprocess = RDoc::Markup::PreProcess.new @file_name, @options.rdoc_include
@@ -665,29 +848,33 @@ class RDoc::Parser::C < RDoc::Parser
@options.main_page = param
''
when 'title' then
- @options.title = param
+ @options.default_title = param if @options.respond_to? :default_title=
''
end
end
comment
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_}, '//')
+ @content.gsub!(%r%//.*rb_define_%, '//')
end
+ ##
+ # Removes private comments from +comment+
+
def remove_private_comments(comment)
comment.gsub!(/\/?\*--\n(.*?)\/?\*\+\+/m, '')
comment.sub!(/\/?\*--\n.*/m, '')
end
##
- # Extract the classes/modules and methods from a C file and return the
- # corresponding top-level object
+ # Extracts the classes, modules, methods, attributes, constants and aliases
+ # from a C file and returns an RDoc::TopLevel for this file
def scan
remove_commented_out_lines
@@ -696,6 +883,7 @@ class RDoc::Parser::C < RDoc::Parser
do_methods
do_includes
do_aliases
+ do_attrs
@top_level
end
diff --git a/lib/rdoc/parser/perl.rb b/lib/rdoc/parser/perl.rb
deleted file mode 100644
index 0023a013a6..0000000000
--- a/lib/rdoc/parser/perl.rb
+++ /dev/null
@@ -1,165 +0,0 @@
-require 'rdoc/parser'
-
-##
-#
-# This is an attamept to write a basic parser for Perl's
-# POD (Plain old Documentation) format. Ruby code must
-# co-exist with Perl, and some tasks are easier in Perl
-# than Ruby because of existing libraries.
-#
-# One difficult is that Perl POD has no means of identifying
-# the classes (packages) and methods (subs) with which it
-# is associated, it is more like literate programming in so
-# far as it just happens to be in the same place as the code,
-# but need not be.
-#
-# We would like to support all the markup the POD provides
-# so that it will convert happily to HTML. At the moment
-# I don't think I can do that: time constraints.
-#
-
-class RDoc::Parser::PerlPOD < RDoc::Parser
-
- parse_files_matching(/.p[lm]$/)
-
- ##
- # Prepare to parse a perl file
-
- def initialize(top_level, file_name, content, options, stats)
- super
-
- preprocess = RDoc::Markup::PreProcess.new @file_name, @options.rdoc_include
-
- preprocess.handle @content do |directive, param|
- warn "Unrecognized directive '#{directive}' in #{@file_name}"
- end
- end
-
- ##
- # Extract the Pod(-like) comments from the code.
- # At its most basic there will ne no need to distinguish
- # between the different types of header, etc.
- #
- # This uses a simple finite state machine, in a very
- # procedural pattern. I could "replace case with polymorphism"
- # but I think it would obscure the intent, scatter the
- # code all over tha place. This machine is necessary
- # because POD requires that directives be preceded by
- # blank lines, so reading line by line is necessary,
- # and preserving state about what is seen is necesary.
-
- def scan
-
- @top_level.comment ||= ""
- state=:code_blank
- line_number = 0
- line = nil
-
- # This started out as a really long nested case statement,
- # which also led to repetitive code. I'd like to avoid that
- # so I'm using a "table" instead.
-
- # Firstly we need some procs to do the transition and processing
- # work. Because these are procs they are closures, and they can
- # use variables in the local scope.
- #
- # First, the "nothing to see here" stuff.
- code_noop = lambda do
- if line =~ /^\s+$/
- state = :code_blank
- end
- end
-
- pod_noop = lambda do
- if line =~ /^\s+$/
- state = :pod_blank
- end
- @top_level.comment += filter(line)
- end
-
- begin_noop = lambda do
- if line =~ /^\s+$/
- state = :begin_blank
- end
- @top_level.comment += filter(line)
- end
-
- # Now for the blocks that process code and comments...
-
- transit_to_pod = lambda do
- case line
- when /^=(?:pod|head\d+)/
- state = :pod_no_blank
- @top_level.comment += filter(line)
- when /^=over/
- state = :over_no_blank
- @top_level.comment += filter(line)
- when /^=(?:begin|for)/
- state = :begin_no_blank
- end
- end
-
- process_pod = lambda do
- case line
- when /^\s*$/
- state = :pod_blank
- @top_level.comment += filter(line)
- when /^=cut/
- state = :code_no_blank
- when /^=end/
- $stderr.puts "'=end' unexpected at #{line_number} in #{@file_name}"
- else
- @top_level.comment += filter(line)
- end
- end
-
-
- process_begin = lambda do
- case line
- when /^\s*$/
- state = :begin_blank
- @top_level.comment += filter(line)
- when /^=end/
- state = :code_no_blank
- when /^=cut/
- $stderr.puts "'=cut' unexpected at #{line_number} in #{@file_name}"
- else
- @top_level.comment += filter(line)
- end
-
- end
-
-
- transitions = { :code_no_blank => code_noop,
- :code_blank => transit_to_pod,
- :pod_no_blank => pod_noop,
- :pod_blank => process_pod,
- :begin_no_blank => begin_noop,
- :begin_blank => process_begin}
- @content.each_line do |l|
- line = l
- line_number += 1
- transitions[state].call
- end # each line
-
- @top_level
- end
-
- # Filter the perl markup that does the same as the rdoc
- # filtering. Only basic for now. Will probably need a
- # proper parser to cope with C<<...>> etc
- def filter(comment)
- return '' if comment =~ /^=pod\s*$/
- comment.gsub!(/^=pod/, '==')
- comment.gsub!(/^=head(\d+)/) do
- "=" * $1.to_i
- end
- comment.gsub!(/=item/, '');
- comment.gsub!(/C<(.*?)>/, '<tt>\1</tt>');
- comment.gsub!(/I<(.*?)>/, '<i>\1</i>');
- comment.gsub!(/B<(.*?)>/, '<b>\1</b>');
- comment
- end
-
-end
-
diff --git a/lib/rdoc/parser/ruby.rb b/lib/rdoc/parser/ruby.rb
index 2874c47a20..e6f07d66da 100644
--- a/lib/rdoc/parser/ruby.rb
+++ b/lib/rdoc/parser/ruby.rb
@@ -11,8 +11,8 @@ require 'rdoc/ruby_token'
require 'rdoc/ruby_lex'
require 'rdoc/code_objects'
-require 'rdoc/tokenstream'
-require 'rdoc/markup/preprocess'
+require 'rdoc/token_stream'
+require 'rdoc/markup/pre_process'
require 'rdoc/parser'
require 'rdoc/parser/ruby_tools'
@@ -162,6 +162,9 @@ class RDoc::Parser::Ruby < RDoc::Parser
SINGLE = "<<"
+ ##
+ # Creates a new Ruby parser.
+
def initialize(top_level, file_name, content, options, stats)
super
@@ -209,10 +212,13 @@ class RDoc::Parser::Ruby < RDoc::Parser
comment
end
+ ##
+ # Aborts with +msg+
+
def error(msg)
msg = make_message msg
- $stderr.puts msg
- exit false
+
+ abort msg
end
##
@@ -229,6 +235,10 @@ class RDoc::Parser::Ruby < RDoc::Parser
meth
end
+ ##
+ # Looks for a true or false token. Returns false if TkFALSE or TkNIL are
+ # found.
+
def get_bool
skip_tkspace
tk = get_tk
@@ -245,20 +255,24 @@ class RDoc::Parser::Ruby < RDoc::Parser
##
# Look for the name of a class of module (optionally with a leading :: or
- # with :: separated named) and return the ultimate name and container
+ # with :: separated named) and return the ultimate name, the associated
+ # container, and the given name (with the ::).
def get_class_or_module(container)
skip_tkspace
name_t = get_tk
+ given_name = ''
# class ::A -> A is in the top level
case name_t
when TkCOLON2, TkCOLON3 then # bug
name_t = get_tk
container = @top_level
+ given_name << '::'
end
skip_tkspace false
+ given_name << name_t.name
while TkCOLON2 === peek_tk do
prev_container = container
@@ -268,9 +282,10 @@ class RDoc::Parser::Ruby < RDoc::Parser
end
get_tk
name_t = get_tk
+ given_name << '::' << name_t.name
end
skip_tkspace false
- return [container, name_t]
+ return [container, name_t, given_name]
end
##
@@ -347,6 +362,9 @@ class RDoc::Parser::Ruby < RDoc::Parser
name
end
+ ##
+ # Extracts a name or symbol from the token stream.
+
def get_symbol_or_name
tk = get_tk
case tk
@@ -361,7 +379,10 @@ class RDoc::Parser::Ruby < RDoc::Parser
text
when TkId, TkOp then
tk.name
- when TkSTRING, TkDSTRING then
+ when TkAMPER,
+ TkDSTRING,
+ TkSTAR,
+ TkSTRING then
tk.text
else
raise RDoc::Error, "Name or symbol expected (got #{tk})"
@@ -374,7 +395,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
# # :stopdoc:
# # Don't display comment from this point forward
#
- # This routine modifies it's parameter
+ # This routine modifies its +comment+ parameter.
def look_for_directives_in(context, comment)
preprocess = RDoc::Markup::PreProcess.new @file_name, @options.rdoc_include
@@ -382,9 +403,10 @@ class RDoc::Parser::Ruby < RDoc::Parser
preprocess.handle comment, context do |directive, param|
case directive
when 'enddoc' then
- throw :enddoc
+ context.done_documenting = true
+ ''
when 'main' then
- @options.main_page = param
+ @options.main_page = param if @options.respond_to? :main_page
''
when 'method', 'singleton-method',
'attr', 'attr_accessor', 'attr_reader', 'attr_writer' then
@@ -401,7 +423,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
context.stop_doc
''
when 'title' then
- @options.title = param
+ @options.default_title = param if @options.respond_to? :default_title=
''
end
end
@@ -426,23 +448,28 @@ class RDoc::Parser::Ruby < RDoc::Parser
def parse_attr(context, single, tk, comment)
args = parse_symbol_arg 1
- if args.size > 0
+ if args.size > 0 then
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
+
+ att = RDoc::Attr.new get_tkread, name, rw, comment, single == SINGLE
+ att.record_location @top_level
+
read_documentation_modifiers att, RDoc::ATTR_MODIFIERS
- if att.document_self
- context.add_attribute(att)
- end
+
+ context.add_attribute att if att.document_self
+
+ @stats.add_attribute att
else
- warn("'attr' ignored - looks like a variable")
+ warn "'attr' ignored - looks like a variable"
end
end
@@ -452,12 +479,8 @@ class RDoc::Parser::Ruby < RDoc::Parser
def parse_attr_accessor(context, single, tk, comment)
args = parse_symbol_arg
- get_tkread
-
rw = "?"
- # TODO If nodoc is given, don't document any of them
-
tmp = RDoc::CodeObject.new
read_documentation_modifiers tmp, RDoc::ATTR_MODIFIERS
return unless tmp.document_self
@@ -471,17 +494,25 @@ class RDoc::Parser::Ruby < RDoc::Parser
end
for name in args
- att = RDoc::Attr.new get_tkread, name, rw, comment
+ att = RDoc::Attr.new get_tkread, name, rw, comment, single == SINGLE
+ att.record_location @top_level
+
context.add_attribute att
+ @stats.add_attribute att
end
end
+ ##
+ # Parses an +alias+ in +context+ with +comment+
+
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 }
@@ -498,11 +529,20 @@ class RDoc::Parser::Ruby < RDoc::Parser
return
end
- al = RDoc::Alias.new get_tkread, old_name, new_name, comment
+ al = RDoc::Alias.new(get_tkread, old_name, new_name, comment,
+ single == SINGLE)
+ al.record_location @top_level
+
read_documentation_modifiers al, RDoc::ATTR_MODIFIERS
context.add_alias al if al.document_self
+ @stats.add_alias al
+
+ al
end
+ ##
+ # Extracts call parameters from the token stream.
+
def parse_call_parameters(tk)
end_token = case tk
when TkLPAREN, TkfLPAREN
@@ -540,28 +580,33 @@ class RDoc::Parser::Ruby < RDoc::Parser
res
end
+ ##
+ # Parses a class in +context+ with +comment+
+
def parse_class(container, single, tk, comment)
- container, name_t = get_class_or_module container
+ declaration_context = container
+ container, name_t, given_name = get_class_or_module container
case name_t
when TkCONSTANT
name = name_t.name
- superclass = "Object"
+ superclass = '::Object'
if TkLT === peek_tk then
get_tk
skip_tkspace
superclass = get_class_specification
- superclass = "<unknown>" if superclass.empty?
+ superclass = '(unknown)' if superclass.empty?
end
cls_type = single == SINGLE ? RDoc::SingleClass : RDoc::NormalClass
- cls = container.add_class cls_type, name, superclass
+ cls = declaration_context.add_class cls_type, given_name, superclass
read_documentation_modifiers cls, RDoc::CLASS_MODIFIERS
cls.record_location @top_level
- cls.comment = comment
+ cls.comment = comment if cls.document_self
+ @top_level.add_to_classes_or_modules cls
@stats.add_class cls
parse_statements cls
@@ -569,7 +614,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
case name = get_class_specification
when "self", container.name
parse_statements container, SINGLE
- when /\A[A-Z]/
+ else
other = RDoc::TopLevel.find_class_named name
unless other then
@@ -578,6 +623,15 @@ class RDoc::Parser::Ruby < RDoc::Parser
other.comment = comment
end
+ # notify :nodoc: all if not a constant-named class/module
+ # (and remove any comment)
+ unless name =~ /\A(::)?[A-Z]/
+ other.document_self = nil
+ other.document_children = false
+ other.clear_comment
+ end
+
+ @top_level.add_to_classes_or_modules other
@stats.add_class other
read_documentation_modifiers other, RDoc::CLASS_MODIFIERS
@@ -589,9 +643,15 @@ class RDoc::Parser::Ruby < RDoc::Parser
end
end
+ ##
+ # Parses a constant in +context+ with +comment+
+
def parse_constant(container, tk, comment)
name = tk.name
skip_tkspace false
+
+ return unless name =~ /^\w+$/
+
eq_tk = get_tk
unless TkASSIGN === eq_tk then
@@ -615,9 +675,9 @@ class RDoc::Parser::Ruby < RDoc::Parser
loop do
case tk
when TkSEMICOLON then
- break
- when TkLPAREN, TkfLPAREN, TkLBRACE, TkLBRACK, TkDO, TkIF, TkUNLESS,
- TkCASE then
+ break if nest <= 0
+ when TkLPAREN, TkfLPAREN, TkLBRACE, TkfLBRACE, TkLBRACK, TkfLBRACK,
+ TkDO, TkIF, TkUNLESS, TkCASE, TkDEF, TkBEGIN then
nest += 1
when TkRPAREN, TkRBRACE, TkRBRACK, TkEND then
nest -= 1
@@ -654,10 +714,11 @@ class RDoc::Parser::Ruby < RDoc::Parser
tk = get_tk
end
- res = get_tkread.tr("\n", " ").strip
+ res = get_tkread.gsub(/^[ \t]+/, '').strip
res = "" if res == ";"
con = RDoc::Constant.new name, res, comment
+ con.record_location @top_level
read_documentation_modifiers con, RDoc::CONSTANT_MODIFIERS
@stats.add_constant con
@@ -679,6 +740,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
name = $1 unless $1.empty?
meth = RDoc::GhostMethod.new get_tkread, name
+ meth.record_location @top_level
meth.singleton = singleton
meth.start_collecting_tokens
@@ -709,13 +771,19 @@ class RDoc::Parser::Ruby < RDoc::Parser
name = $3 unless $3.empty?
+ # TODO authorize 'singleton-attr...'?
att = RDoc::Attr.new get_tkread, name, rw, comment
+ att.record_location @top_level
+
container.add_attribute att
- @stats.add_method att
+ @stats.add_attribute att
end
end
+ ##
+ # Parses an +include+ in +context+ with +comment+
+
def parse_include(context, comment)
loop do
skip_tkspace_comment
@@ -759,8 +827,6 @@ class RDoc::Parser::Ruby < RDoc::Parser
def parse_meta_attr(context, single, tk, comment)
args = parse_symbol_arg
- get_tkread
-
rw = "?"
# If nodoc is given, don't document any of them
@@ -779,12 +845,19 @@ class RDoc::Parser::Ruby < RDoc::Parser
end
if name then
- att = RDoc::Attr.new get_tkread, name, rw, comment
+ att = RDoc::Attr.new get_tkread, name, rw, comment, single == SINGLE
+ att.record_location @top_level
+
context.add_attribute att
+ @stats.add_attribute att
else
args.each do |attr_name|
- att = RDoc::Attr.new get_tkread, attr_name, rw, comment
+ att = RDoc::Attr.new(get_tkread, attr_name, rw, comment,
+ single == SINGLE)
+ att.record_location @top_level
+
context.add_attribute att
+ @stats.add_attribute att
end
end
end
@@ -825,6 +898,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
end
meth = RDoc::MetaMethod.new get_tkread, name
+ meth.record_location @top_level
meth.singleton = singleton
remove_token_listener self
@@ -882,7 +956,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
token_listener self do
@scanner.instance_eval do @lex_state = EXPR_FNAME end
- skip_tkspace false
+ skip_tkspace
name_t = get_tk
back_tk = skip_tkspace
meth = nil
@@ -922,11 +996,17 @@ class RDoc::Parser::Ruby < RDoc::Parser
container.record_location @top_level
end
- when TkIDENTIFIER, TkIVAR then
+ when TkIDENTIFIER, TkIVAR, TkGVAR then
dummy = RDoc::Context.new
dummy.parent = container
skip_method dummy
return
+ when TkTRUE, TkFALSE, TkNIL then
+ klass_name = "#{name_t.name.capitalize}Class"
+ container = RDoc::TopLevel.find_class_named klass_name
+ container ||= @top_level.add_class RDoc::NormalClass, klass_name
+
+ name = name_t2.name
else
warn "unexpected method name token #{name_t.inspect}"
# break
@@ -959,6 +1039,8 @@ class RDoc::Parser::Ruby < RDoc::Parser
end
end
+ meth.record_location @top_level
+
meth.start_collecting_tokens
indent = TkSPACE.new nil, 1, 1
indent.set_text " " * column
@@ -1001,6 +1083,9 @@ class RDoc::Parser::Ruby < RDoc::Parser
@stats.add_method meth
end
+ ##
+ # Extracts +yield+ parameters from +method+
+
def parse_method_or_yield_parameters(method = nil,
modifiers = RDoc::METHOD_MODIFIERS)
skip_tkspace false
@@ -1024,14 +1109,16 @@ class RDoc::Parser::Ruby < RDoc::Parser
loop do
case tk
when TkSEMICOLON then
- break
- when TkLBRACE then
+ break if nest == 0
+ when TkLBRACE, TkfLBRACE then
nest += 1
when TkRBRACE then
- # we might have a.each {|i| yield i }
- unget_tk(tk) if nest.zero?
nest -= 1
- break if nest <= 0
+ if nest <= 0
+ # we might have a.each { |i| yield i }
+ unget_tk(tk) if nest < 0
+ break
+ end
when TkLPAREN, TkfLPAREN then
nest += 1
when end_token then
@@ -1041,6 +1128,8 @@ class RDoc::Parser::Ruby < RDoc::Parser
else
break unless @scanner.continue
end
+ when TkRPAREN then
+ nest -= 1
when method && method.block_params.nil? && TkCOMMENT then
unget_tk tk
read_documentation_modifiers method, modifiers
@@ -1078,8 +1167,11 @@ class RDoc::Parser::Ruby < RDoc::Parser
end
end
+ ##
+ # Parses an RDoc::NormalModule in +container+ with +comment+
+
def parse_module(container, single, tk, comment)
- container, name_t = get_class_or_module container
+ container, name_t, = get_class_or_module container
name = name_t.name
@@ -1087,12 +1179,16 @@ class RDoc::Parser::Ruby < RDoc::Parser
mod.record_location @top_level
read_documentation_modifiers mod, RDoc::CLASS_MODIFIERS
+ mod.comment = comment if mod.document_self
parse_statements(mod)
- mod.comment = comment
+ @top_level.add_to_classes_or_modules mod
@stats.add_module mod
end
+ ##
+ # Parses an RDoc::Require in +context+ containing +comment+
+
def parse_require(context, comment)
skip_tkspace_comment
tk = get_tk
@@ -1105,7 +1201,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
name = tk.text if TkSTRING === tk
if name then
- context.add_require RDoc::Require.new(name, comment)
+ @top_level.add_require RDoc::Require.new(name, comment)
else
unget_tk tk
end
@@ -1206,7 +1302,7 @@ class RDoc::Parser::Ruby < RDoc::Parser
# We can't solve the general case, but we can handle most occurrences by
# ignoring a do at the end of a line.
- when TkUNTIL, TkWHILE then
+ when TkUNTIL, TkWHILE then
nest += 1
skip_optional_do_after_expression
@@ -1275,9 +1371,14 @@ class RDoc::Parser::Ruby < RDoc::Parser
end
end
+ ##
+ # Parse up to +no+ symbol arguments
+
def parse_symbol_arg(no = nil)
args = []
+
skip_tkspace_comment
+
case tk = get_tk
when TkLPAREN
loop do
@@ -1320,28 +1421,40 @@ class RDoc::Parser::Ruby < RDoc::Parser
end
end
end
+
args
end
+ ##
+ # Returns symbol text from the next token
+
def parse_symbol_in_arg
case tk = get_tk
when TkSYMBOL
tk.text.sub(/^:/, '')
when TkSTRING
eval @read[-1]
+ when TkDSTRING, TkIDENTIFIER then
+ nil # ignore
else
warn("Expected symbol or string, got #{tk.inspect}") if $DEBUG_RDOC
nil
end
end
+ ##
+ # Parses statements at the toplevel in +container+
+
def parse_top_level_statements(container)
comment = collect_first_comment
look_for_directives_in(container, comment)
- container.comment = comment unless comment.empty?
+ container.comment = comment if container.document_self unless comment.empty?
parse_statements container, NORMAL, nil, comment
end
+ ##
+ # Determines the visibility in +container+ from +tk+
+
def parse_visibility(container, single, tk)
singleton = (single == SINGLE)
@@ -1383,7 +1496,8 @@ class RDoc::Parser::Ruby < RDoc::Parser
container.methods_matching args do |m|
s_m = m.dup
- s_m.singleton = true if RDoc::AnyMethod === s_m
+ s_m.record_location @top_level
+ s_m.singleton = true
s_m.visibility = :public
module_functions << s_m
end
@@ -1403,6 +1517,9 @@ class RDoc::Parser::Ruby < RDoc::Parser
end
end
+ ##
+ # Determines the block parameter for +context+
+
def parse_yield(context, single, tk, method)
return if method.block_params
@@ -1423,93 +1540,81 @@ class RDoc::Parser::Ruby < RDoc::Parser
#
# We return the directive name and any parameters as a two element array
- def read_directive(allowed)
+ def read_directive allowed
tk = get_tk
- result = nil
if TkCOMMENT === tk then
- if tk.text =~ /\s*:?(\w+):\s*(.*)/ then
- directive = $1.downcase
- if allowed.include? directive then
- result = [directive, $2]
- end
- end
+ return unless tk.text =~ /\s*:?(\w+):\s*(.*)/
+
+ directive = $1.downcase
+
+ return [directive, $2] if allowed.include? directive
else
unget_tk tk
end
-
- result
end
- def read_documentation_modifiers(context, allow)
- dir = read_directive(allow)
-
- case dir[0]
- when "notnew", "not_new", "not-new" then
- context.dont_rename_initialize = true
-
- when "nodoc" then
- context.document_self = false
- if dir[1].downcase == "all"
- context.document_children = false
- end
-
- when "doc" then
- context.document_self = true
- context.force_documentation = true
+ ##
+ # Handles the directive for +context+ if the directive is listed in +allow+.
+ # This method is called for directives following a definition.
- when "yield", "yields" then
- unless context.params.nil?
- context.params.sub!(/(,|)\s*&\w+/,'') # remove parameter &proc
- end
+ def read_documentation_modifiers(context, allow)
+ directive, value = read_directive allow
- context.block_params = dir[1]
+ return unless directive
- when "arg", "args" then
- context.params = dir[1]
- end if dir
+ case directive
+ when 'notnew', 'not_new', 'not-new' then
+ context.dont_rename_initialize = true
+ else
+ RDoc::Parser.process_directive context, directive, value
+ end
end
+ ##
+ # Removes private comments from +comment+
+
def remove_private_comments(comment)
- comment.gsub!(/^#--\n.*?^#\+\+/m, '')
- comment.sub!(/^#--\n.*/m, '')
+ comment.gsub!(/^#--\n.*?^#\+\+\n?/m, '')
+ comment.sub!(/^#--\n.*\n?/m, '')
end
+ ##
+ # Scans this ruby file for ruby constructs
+
def scan
reset
catch :eof do
- catch :enddoc do
- begin
- parse_top_level_statements @top_level
- rescue StandardError => e
- bytes = ''
-
- 20.times do @scanner.ungetc end
- count = 0
- 60.times do |i|
- count = i
- byte = @scanner.getc
- break unless byte
- bytes << byte
- end
- count -= 20
- count.times do @scanner.ungetc end
+ begin
+ parse_top_level_statements @top_level
+ rescue StandardError => e
+ bytes = ''
+
+ 20.times do @scanner.ungetc end
+ count = 0
+ 60.times do |i|
+ count = i
+ byte = @scanner.getc
+ break unless byte
+ bytes << byte
+ end
+ count -= 20
+ count.times do @scanner.ungetc end
- $stderr.puts <<-EOF
+ $stderr.puts <<-EOF
#{self.class} failure around line #{@scanner.line_no} of
#{@file_name}
- EOF
+ EOF
- unless bytes.empty? then
- $stderr.puts
- $stderr.puts bytes.inspect
- end
-
- raise e
+ unless bytes.empty? then
+ $stderr.puts
+ $stderr.puts bytes.inspect
end
+
+ raise e
end
end
@@ -1574,6 +1679,9 @@ class RDoc::Parser::Ruby < RDoc::Parser
unget_tk(tk) unless TkIN === tk
end
+ ##
+ # Skips the next method in +container+
+
def skip_method container
meth = RDoc::AnyMethod.new "", "anon"
parse_method_parameters meth
@@ -1591,6 +1699,9 @@ class RDoc::Parser::Ruby < RDoc::Parser
end
end
+ ##
+ # Prints +msg+ to +$stderr+ unless we're being quiet
+
def warn(msg)
return if @options.quiet
msg = make_message msg
diff --git a/lib/rdoc/parser/ruby_tools.rb b/lib/rdoc/parser/ruby_tools.rb
index 90c03307b4..3f6190884e 100644
--- a/lib/rdoc/parser/ruby_tools.rb
+++ b/lib/rdoc/parser/ruby_tools.rb
@@ -49,7 +49,6 @@ module RDoc::Parser::RubyTools
obj.pop_token
end if @token_listeners
else
- warn("':' not followed by identifier or operator")
tk = tk1
end
end
@@ -62,6 +61,10 @@ module RDoc::Parser::RubyTools
tk
end
+ ##
+ # Reads and returns all tokens up to one of +tokens+. Leaves the matched
+ # token in the token list.
+
def get_tk_until(*tokens)
read = []
diff --git a/lib/rdoc/parser/simple.rb b/lib/rdoc/parser/simple.rb
index e99d2d4319..1e82eb5097 100644
--- a/lib/rdoc/parser/simple.rb
+++ b/lib/rdoc/parser/simple.rb
@@ -1,7 +1,6 @@
##
# 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.
+# comment.
class RDoc::Parser::Simple < RDoc::Parser
@@ -32,10 +31,16 @@ class RDoc::Parser::Simple < RDoc::Parser
@top_level
end
- def remove_private_comments(comment)
- comment.gsub(/^--\n.*?^\+\+/m, '').sub(/^--\n.*/m, '')
+ ##
+ # Removes comments wrapped in <tt>--/++</tt>
+
+ def remove_private_comments text
+ text.gsub(/^--\n.*?^\+\+/m, '').sub(/^--\n.*/m, '')
end
+ ##
+ # Removes the encoding magic comment from +text+
+
def remove_coding_comment text
text.sub(/\A# .*coding[=:].*$/, '')
end
diff --git a/lib/rdoc/rdoc.rb b/lib/rdoc/rdoc.rb
index 8771b408d9..a885d8dded 100644
--- a/lib/rdoc/rdoc.rb
+++ b/lib/rdoc/rdoc.rb
@@ -1,5 +1,6 @@
require 'rdoc'
+require 'rdoc/encoding'
require 'rdoc/parser'
# Simple must come first
@@ -23,7 +24,28 @@ require 'time'
# rdoc.document(args)
#
# Where +args+ is an array of strings, each corresponding to an argument you'd
-# give rdoc on the command line. See rdoc/rdoc.rb for details.
+# give rdoc on the command line. See <tt>rdoc --help<tt> for details.
+#
+# = Plugins
+#
+# When you <tt>require 'rdoc/rdoc'</tt> RDoc looks for 'rdoc/discover' files
+# in your installed gems. This can be used to load alternate generators or
+# add additional preprocessor directives.
+#
+# You will want to wrap your plugin loading in an RDoc version check.
+# Something like:
+#
+# begin
+# gem 'rdoc', '~> 3'
+# require 'path/to/my/awesome/rdoc/plugin'
+# rescue Gem::LoadError
+# end
+#
+# The most obvious plugin type is a new output generator. See RDoc::Generator
+# for details.
+#
+# You can also hook into RDoc::Markup to add new directives (:nodoc: is a
+# directive). See RDoc::Markup::PreProcess::register for details.
class RDoc::RDoc
@@ -79,6 +101,10 @@ class RDoc::RDoc
@current = rdoc
end
+ ##
+ # Creates a new RDoc::RDoc instance. Call #document to parse files and
+ # generate documentation.
+
def initialize
@current = nil
@exclude = nil
@@ -142,7 +168,9 @@ class RDoc::RDoc
last = {}
- if File.exist? dir then
+ if @options.dry_run then
+ # do nothing
+ elsif File.exist? dir then
error "#{dir} exists and is not a directory" unless File.directory? dir
begin
@@ -167,7 +195,7 @@ you'll need to specify a different output directory name (using the --op <dir>
option)
ERROR
- end
+ end unless @options.force_output
else
FileUtils.mkdir_p dir
end
@@ -179,6 +207,8 @@ option)
# Update the flag file in an output directory.
def update_output_dir(op_dir, time, last = {})
+ return if @options.dry_run
+
open output_flag_file(op_dir), "w" do |f|
f.puts time.rfc2822
last.each do |n, t|
@@ -277,7 +307,9 @@ option)
def parse_file filename
@stats.add_file filename
- content = read_file_contents filename
+ encoding = @options.encoding if defined?(Encoding)
+
+ content = RDoc::Encoding.read_file filename, encoding
return unless content
@@ -288,11 +320,22 @@ option)
return unless parser
parser.scan
+
+ # restart documentation for the classes & modules found
+ top_level.classes_or_modules.each do |cm|
+ cm.done_documenting = false
+ end
+
+ top_level
+
rescue => e
$stderr.puts <<-EOF
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.
+has proper syntax:
+
+ #{Gem.ruby} -c #{filename}
+
+RDoc is not a full Ruby parser and will fail when fed invalid ruby programs.
The internal error was:
@@ -300,7 +343,7 @@ The internal error was:
EOF
- $stderr.puts e.backtrace.join("\n\t") if $RDOC_DEBUG
+ $stderr.puts e.backtrace.join("\n\t") if $DEBUG_RDOC
raise e
nil
@@ -344,11 +387,9 @@ The internal error was:
# For simplicity, +argv+ is an array of strings, equivalent to the strings
# that would be passed on the command line. (This isn't a coincidence, as
# we _do_ pass in ARGV when running interactively). For a list of options,
- # see rdoc/rdoc.rb. By default, output will be stored in a directory
+ # see <tt>rdoc --help</tt>. By default, output will be stored in a directory
# called +doc+ below the current directory, so make sure you're somewhere
# writable before invoking.
- #
- # Throws: RDoc::Error on error
def document(argv)
RDoc::TopLevel.reset
@@ -364,29 +405,36 @@ The internal error was:
@exclude = @options.exclude
- @last_modified = setup_output_dir @options.op_dir, @options.force_update
+ unless @options.coverage_report then
+ @last_modified = setup_output_dir @options.op_dir, @options.force_update
+ end
start_time = Time.now
file_info = parse_files @options.files
- @options.title = "RDoc Documentation"
+ @options.default_title = "RDoc Documentation"
+
+ RDoc::TopLevel.complete @options.visibility
- if file_info.empty?
+ if @options.coverage_report then
+ puts
+ puts @stats.report
+ elsif file_info.empty?
$stderr.puts "\nNo newer files." unless @options.quiet
else
gen_klass = @options.generator
- unless @options.quiet then
- $stderr.puts "\nGenerating #{gen_klass.name.sub(/^.*::/, '')}..."
- end
-
- @generator = gen_klass.for @options
+ @generator = gen_klass.new @options
Dir.chdir @options.op_dir do
begin
self.class.current = self
+ unless @options.quiet then
+ $stderr.puts "\nGenerating #{gen_klass.name.sub(/^.*::/, '')} format into #{Dir.pwd}..."
+ end
+
@generator.generate file_info
update_output_dir ".", start_time, @last_modified
ensure
@@ -397,26 +445,10 @@ The internal error was:
unless @options.quiet or not @stats then
puts
- @stats.print
- end
- end
-
- def read_file_contents(filename)
- content = open filename, "rb" do |f| f.read end
-
- utf8 = content.sub!(/\A\xef\xbb\xbf/, '')
- if defined? Encoding then
- if /coding[=:]\s*([^\s;]+)/i =~ content[%r"\A(?:#!.*\n)?.*\n"]
- enc = ::Encoding.find($1)
- end
- if enc ||= (Encoding::UTF_8 if utf8)
- content.force_encoding(enc)
- end
+ puts @stats.summary
end
- content
- rescue Errno::EISDIR, Errno::ENOENT
- nil
+ exit @stats.fully_documented? if @options.coverage_report
end
##
diff --git a/lib/rdoc/require.rb b/lib/rdoc/require.rb
index 407b55af35..65d3d464da 100644
--- a/lib/rdoc/require.rb
+++ b/lib/rdoc/require.rb
@@ -16,6 +16,7 @@ class RDoc::Require < RDoc::CodeObject
def initialize(name, comment)
super()
@name = name.gsub(/'|"/, "") #'
+ @top_level = nil
self.comment = comment
end
@@ -28,5 +29,25 @@ class RDoc::Require < RDoc::CodeObject
]
end
+ def to_s # :nodoc:
+ "require #{name} in: #{parent}"
+ end
+
+ ##
+ # The RDoc::TopLevel corresponding to this require, or +nil+ if not found.
+
+ def top_level
+ @top_level ||= begin
+ tl = RDoc::TopLevel.all_files_hash[name + '.rb']
+
+ if tl.nil? and RDoc::TopLevel.all_files.first.full_name =~ %r(^lib/) then
+ # second chance
+ tl = RDoc::TopLevel.all_files_hash['lib/' + name + '.rb']
+ end
+
+ tl
+ end
+ end
+
end
diff --git a/lib/rdoc/ri/driver.rb b/lib/rdoc/ri/driver.rb
index f3fd9539e3..9d61b1f243 100644
--- a/lib/rdoc/ri/driver.rb
+++ b/lib/rdoc/ri/driver.rb
@@ -6,6 +6,11 @@ begin
rescue LoadError
end
+begin
+ require 'win32console'
+rescue LoadError
+end
+
require 'rdoc/ri'
require 'rdoc/ri/paths'
require 'rdoc/markup'
@@ -55,6 +60,9 @@ class RDoc::RI::Driver
end
end
+ ##
+ # An RDoc::RI::Store for each entry in the RI path
+
attr_accessor :stores
##
@@ -97,23 +105,10 @@ class RDoc::RI::Driver
##
# Parses +argv+ and returns a Hash of options
- def self.process_args argv = []
+ def self.process_args argv
options = default_options
- opts = OptionParser.new
- setup_options(opts, options)
- argv = ENV['RI'].to_s.split.concat argv
- opts.parse!(argv)
-
- fixup_options(options, argv)
-
- rescue OptionParser::ParseError => e
- puts opts, nil, e
- abort
- end
-
- def self.setup_options(opt, options)
- begin
+ opts = OptionParser.new do |opt|
opt.accept File do |file,|
File.readable?(file) and not File.directory?(file) and file
end
@@ -133,7 +128,7 @@ Where name can be:
All class names may be abbreviated to their minimum unambiguous form. If a name
is ambiguous, all valid options will be listed.
-The form '.' method matches either class or instance methods, while #method
+A '.' matches either class or instance methods, while #method
matches only instance and ::method matches only class methods.
For example:
@@ -143,7 +138,7 @@ For example:
#{opt.program_name} File.new
#{opt.program_name} zip
-Note that shell quoting may be required for method names containing
+Note that shell quoting or escaping may be required for method names containing
punctuation:
#{opt.program_name} 'Array.[]'
@@ -287,9 +282,11 @@ Options may also be set in the 'RI' environment variable.
options[:dump_path] = value
end
end
- end
- def self.fixup_options(options, argv)
+ argv = ENV['RI'].to_s.split.concat argv
+
+ opts.parse! argv
+
options[:names] = argv
options[:use_stdout] ||= !$stdout.tty?
@@ -297,6 +294,12 @@ Options may also be set in the 'RI' environment variable.
options[:width] ||= 72
options
+
+ rescue OptionParser::InvalidArgument, OptionParser::InvalidOption => e
+ puts opts
+ puts
+ puts e
+ exit 1
end
##
@@ -359,7 +362,7 @@ Options may also be set in the 'RI' environment variable.
paths = RDoc::Markup::Verbatim.new
also_in.each do |store|
- paths.parts.push ' ', store.friendly_path, "\n"
+ paths.parts.push store.friendly_path, "\n"
end
out << paths
end
@@ -427,7 +430,7 @@ Options may also be set in the 'RI' environment variable.
verb = RDoc::Markup::Verbatim.new
wout.each do |incl|
- verb.push ' ', incl.name, "\n"
+ verb.push incl.name, "\n"
end
out << verb
@@ -446,7 +449,7 @@ Options may also be set in the 'RI' environment variable.
out << RDoc::Markup::BlankLine.new
out.push(*methods.map do |method|
- RDoc::Markup::Verbatim.new ' ', method
+ RDoc::Markup::Verbatim.new method
end)
out << RDoc::Markup::BlankLine.new
@@ -664,8 +667,8 @@ Options may also be set in the 'RI' environment variable.
if method.arglists then
arglists = method.arglists.chomp.split "\n"
- arglists = arglists.map { |line| [' ', line, "\n"] }
- out << RDoc::Markup::Verbatim.new(*arglists.flatten)
+ arglists = arglists.map { |line| line + "\n" }
+ out << RDoc::Markup::Verbatim.new(*arglists)
out << RDoc::Markup::Rule.new(1)
end
@@ -847,6 +850,17 @@ Options may also be set in the 'RI' environment variable.
end
##
+ # Is +file+ in ENV['PATH']?
+
+ def in_path? file
+ return true if file =~ %r%\A/% and File.exist? file
+
+ ENV['PATH'].split(File::PATH_SEPARATOR).any? do |path|
+ File.exist? File.join(path, file)
+ end
+ end
+
+ ##
# Lists classes known to ri
def list_known_classes
@@ -1041,7 +1055,11 @@ Options may also be set in the 'RI' environment variable.
pagers.compact.uniq.each do |pager|
next unless pager
- io = IO.popen pager, "w" rescue next
+ pager_cmd = pager.split.first
+
+ next unless in_path? pager_cmd
+
+ io = IO.popen(pager, 'w') rescue next
next if $? and $?.exited? # pager didn't work
diff --git a/lib/rdoc/ri/paths.rb b/lib/rdoc/ri/paths.rb
index 9b338d7ad8..ec4d16b857 100644
--- a/lib/rdoc/ri/paths.rb
+++ b/lib/rdoc/ri/paths.rb
@@ -10,10 +10,21 @@ module RDoc::RI::Paths
version = RbConfig::CONFIG['ruby_version']
- base = File.join RbConfig::CONFIG['ridir'], version
+ base = if RbConfig::CONFIG.key? 'ridir' then
+ File.join RbConfig::CONFIG['ridir'], version
+ else
+ File.join RbConfig::CONFIG['datadir'], 'ri', version
+ end
+
SYSDIR = File.join base, "system"
SITEDIR = File.join base, "site"
- HOMEDIR = (File.expand_path('~/.rdoc') rescue nil)
+
+ homedir = File.expand_path('~') ||
+ ENV['HOME'] || ENV['USERPROFILE'] || ENV['HOMEPATH']
+
+ HOMEDIR = if homedir then
+ File.join homedir, ".rdoc"
+ end
#:startdoc:
@gemdirs = nil
diff --git a/lib/rdoc/ri/store.rb b/lib/rdoc/ri/store.rb
index db02202f81..1fcd313d0f 100644
--- a/lib/rdoc/ri/store.rb
+++ b/lib/rdoc/ri/store.rb
@@ -11,6 +11,11 @@ require 'fileutils'
class RDoc::RI::Store
##
+ # If true this Store will not write any files
+
+ attr_accessor :dry_run
+
+ ##
# Path this store reads or writes
attr_accessor :path
@@ -21,14 +26,18 @@ class RDoc::RI::Store
attr_accessor :type
+ ##
+ # The contents of the Store
+
attr_reader :cache
##
# Creates a new Store of +type+ that will load or save to +path+
def initialize path, type = nil
- @type = type
- @path = path
+ @dry_run = false
+ @type = type
+ @path = path
@cache = {
:class_methods => {},
@@ -178,6 +187,8 @@ class RDoc::RI::Store
@cache[:instance_methods].each do |_, m| m.uniq!; m.sort! end
@cache[:modules].uniq!; @cache[:modules].sort!
+ return if @dry_run
+
open cache_path, 'wb' do |io|
Marshal.dump @cache, io
end
@@ -187,7 +198,7 @@ class RDoc::RI::Store
# Writes the ri data for +klass+
def save_class klass
- FileUtils.mkdir_p class_path(klass.full_name)
+ FileUtils.mkdir_p class_path(klass.full_name) unless @dry_run
@cache[:modules] << klass.full_name
@@ -214,7 +225,7 @@ class RDoc::RI::Store
@cache[:ancestors][klass.full_name].push(*ancestors)
attributes = klass.attributes.map do |attribute|
- "#{attribute.type} #{attribute.name}"
+ "#{attribute.definition} #{attribute.name}"
end
unless attributes.empty? then
@@ -222,6 +233,8 @@ class RDoc::RI::Store
@cache[:attributes][klass.full_name].push(*attributes)
end
+ return if @dry_run
+
open path, 'wb' do |io|
Marshal.dump klass, io
end
@@ -231,7 +244,7 @@ class RDoc::RI::Store
# Writes the ri data for +method+ on +klass+
def save_method klass, method
- FileUtils.mkdir_p class_path(klass.full_name)
+ FileUtils.mkdir_p class_path(klass.full_name) unless @dry_run
cache = if method.singleton then
@cache[:class_methods]
@@ -241,6 +254,8 @@ class RDoc::RI::Store
cache[klass.full_name] ||= []
cache[klass.full_name] << method.name
+ return if @dry_run
+
open method_file(klass.full_name, method.full_name), 'wb' do |io|
Marshal.dump method, io
end
diff --git a/lib/rdoc/ruby_lex.rb b/lib/rdoc/ruby_lex.rb
index fe289fb1a2..cbe3ec9061 100644
--- a/lib/rdoc/ruby_lex.rb
+++ b/lib/rdoc/ruby_lex.rb
@@ -92,9 +92,9 @@ class RDoc::RubyLex
end
def inspect # :nodoc:
- "#<%s:0x%x lex_state %p space_seen %p>" % [
+ "#<%s:0x%x pos %d lex_state %p space_seen %p>" % [
self.class, object_id,
- @lex_state, @space_seen,
+ @io.pos, @lex_state, @space_seen,
]
end
@@ -149,6 +149,7 @@ class RDoc::RubyLex
else
@char_no += 1
end
+
c
end
@@ -674,7 +675,7 @@ class RDoc::RubyLex
tk_c = TkLPAREN
end
@indent_stack.push tk_c
- Token(tk_c)
+ Token tk_c
end
@OP.def_rule("[]", proc{|op, io| @lex_state == EXPR_FNAME}) do
@@ -822,6 +823,12 @@ class RDoc::RubyLex
end
end
+ IDENT_RE = if defined? Encoding then
+ /[\w\u0080-\uFFFF]/u
+ else
+ /[\w\x80-\xFF]/
+ end
+
def identify_identifier
token = ""
if peek(0) =~ /[$@]/
@@ -831,15 +838,7 @@ class RDoc::RubyLex
end
end
- # HACK to avoid a warning the regexp is hidden behind an eval
- # HACK need a better way to detect oniguruma
- @identifier_re ||= if defined? Encoding then
- eval '/[\p{Alnum}_]/u'
- else
- eval '/[\w\x80-\xff]/'
- end
-
- while (ch = getc) =~ @identifier_re
+ while (ch = getc) =~ IDENT_RE do
print " :#{ch}: " if RDoc::RubyLex.debug?
token.concat ch
end
diff --git a/lib/rdoc/ruby_token.rb b/lib/rdoc/ruby_token.rb
index 0c4f837193..93b7a5cbc8 100644
--- a/lib/rdoc/ruby_token.rb
+++ b/lib/rdoc/ruby_token.rb
@@ -178,7 +178,7 @@ module RDoc::RubyToken
end
class TkUnknownChar < Token
- def initialize(seek, line_no, char_no, id)
+ def initialize(seek, line_no, char_no, name)
super(seek, line_no, char_no)
@name = name
end
@@ -253,7 +253,7 @@ module RDoc::RubyToken
[:TkWHILE, TkKW, "while", EXPR_BEG, :TkWHILE_MOD],
[:TkUNTIL, TkKW, "until", EXPR_BEG, :TkUNTIL_MOD],
[:TkFOR, TkKW, "for", EXPR_BEG],
- [:TkBREAK, TkKW, "break", EXPR_END],
+ [:TkBREAK, TkKW, "break", EXPR_MID],
[:TkNEXT, TkKW, "next", EXPR_END],
[:TkREDO, TkKW, "redo", EXPR_END],
[:TkRETRY, TkKW, "retry", EXPR_END],
diff --git a/lib/rdoc/single_class.rb b/lib/rdoc/single_class.rb
index 1226d56f84..60336a759b 100644
--- a/lib/rdoc/single_class.rb
+++ b/lib/rdoc/single_class.rb
@@ -5,8 +5,9 @@ require 'rdoc/class_module'
class RDoc::SingleClass < RDoc::ClassModule
+ # Adds the superclass to the included modules.
def ancestors
- includes + [superclass]
+ superclass ? super + [superclass] : super
end
end
diff --git a/lib/rdoc/stats.rb b/lib/rdoc/stats.rb
index 6a5d96faa8..e0af445539 100644
--- a/lib/rdoc/stats.rb
+++ b/lib/rdoc/stats.rb
@@ -1,247 +1,287 @@
require 'rdoc'
##
-# RDoc stats collector
+# RDoc statistics collector which prints a summary and report of a project's
+# documentation totals.
class RDoc::Stats
- attr_reader :nodoc_constants
- attr_reader :nodoc_methods
+ ##
+ # Count of files parsed during parsing
- attr_reader :num_constants
- attr_reader :num_files
- attr_reader :num_methods
+ attr_reader :files_so_far
- attr_reader :total_files
+ ##
+ # Total number of files found
- def initialize(total_files, verbosity = 1)
- @nodoc_constants = 0
- @nodoc_methods = 0
+ attr_reader :num_files
- @num_constants = 0
- @num_files = 0
- @num_methods = 0
+ ##
+ # Creates a new Stats that will have +num_files+. +verbosity+ defaults to 1
+ # which will create an RDoc::Stats::Normal outputter.
- @total_files = total_files
+ def initialize num_files, verbosity = 1
+ @files_so_far = 0
+ @num_files = num_files
+ @fully_documented = nil
@start = Time.now
@display = case verbosity
- when 0 then Quiet.new total_files
- when 1 then Normal.new total_files
- else Verbose.new total_files
+ when 0 then Quiet.new num_files
+ when 1 then Normal.new num_files
+ else Verbose.new num_files
end
end
- def begin_adding
- @display.begin_adding
- end
+ ##
+ # Records the parsing of an alias +as+.
- def add_alias(as)
+ def add_alias as
@display.print_alias as
- @num_methods += 1
- @nodoc_methods += 1 if as.document_self and as.comment.empty?
end
- def add_class(klass)
+ ##
+ # Records the parsing of an attribute +attribute+
+
+ def add_attribute attribute
+ @display.print_attribute attribute
+ end
+
+ ##
+ # Records the parsing of a class +klass+
+
+ def add_class klass
@display.print_class klass
end
- def add_constant(constant)
+ ##
+ # Records the parsing of +constant+
+
+ def add_constant constant
@display.print_constant constant
- @num_constants += 1
- @nodoc_constants += 1 if constant.document_self and constant.comment.empty?
end
+ ##
+ # Records the parsing of +file+
+
def add_file(file)
- @display.print_file @num_files, file
- @num_files += 1
+ @files_so_far += 1
+ @display.print_file @files_so_far, file
end
+ ##
+ # Records the parsing of +method+
+
def add_method(method)
@display.print_method method
- @num_methods += 1
- @nodoc_methods += 1 if method.document_self and method.comment.empty?
end
+ ##
+ # Records the parsing of a module +mod+
+
def add_module(mod)
@display.print_module mod
end
- def done_adding
- @display.done_adding
- end
+ ##
+ # Call this to mark the beginning of parsing for display purposes
- def print
- classes = RDoc::TopLevel.classes
- num_classes = classes.length
- nodoc_classes = classes.select do |klass|
- klass.document_self and klass.comment.empty?
- end.length
-
- modules = RDoc::TopLevel.modules
- num_modules = modules.length
- nodoc_modules = modules.select do |mod|
- mod.document_self and mod.comment.empty?
- end.length
-
- items = num_classes + @num_constants + num_modules + @num_methods
- doc_items = items -
- nodoc_classes - @nodoc_constants - nodoc_modules - @nodoc_methods
-
- percent_doc = doc_items.to_f / items * 100 if items.nonzero?
-
- puts "Files: %5d" % @num_files
- puts "Classes: %5d (%5d undocumented)" % [num_classes, nodoc_classes]
- puts "Constants: %5d (%5d undocumented)" %
- [@num_constants, @nodoc_constants]
- puts "Modules: %5d (%5d undocumented)" % [num_modules, nodoc_modules]
- puts "Methods: %5d (%5d undocumented)" % [@num_methods, @nodoc_methods]
- puts "%6.2f%% documented" % percent_doc if percent_doc
- puts
- puts "Elapsed: %0.1fs" % (Time.now - @start)
+ def begin_adding
+ @display.begin_adding
end
##
- # Stats printer that prints nothing
-
- class Quiet
+ # Calculates documentation totals and percentages
- def initialize total_files
- @total_files = total_files
- end
-
- ##
- # Prints a message at the beginning of parsing
+ def calculate
+ return if @percent_doc
- def begin_adding(*) end
+ ucm = RDoc::TopLevel.unique_classes_and_modules
+ constants = []
+ ucm.each { |cm| constants.concat cm.constants }
- ##
- # Prints when an alias is added
+ methods = []
+ ucm.each { |cm| methods.concat cm.method_list }
- def print_alias(*) end
+ attributes = []
+ ucm.each { |cm| attributes.concat cm.attributes }
- ##
- # Prints when a class is added
+ @num_attributes, @undoc_attributes = doc_stats attributes
+ @num_classes, @undoc_classes = doc_stats RDoc::TopLevel.unique_classes
+ @num_constants, @undoc_constants = doc_stats constants
+ @num_methods, @undoc_methods = doc_stats methods
+ @num_modules, @undoc_modules = doc_stats RDoc::TopLevel.unique_modules
- def print_class(*) end
+ @num_items =
+ @num_attributes +
+ @num_classes +
+ @num_constants +
+ @num_methods +
+ @num_modules
- ##
- # Prints when a constant is added
+ @undoc_items =
+ @undoc_attributes +
+ @undoc_classes +
+ @undoc_constants +
+ @undoc_methods +
+ @undoc_modules
- def print_constant(*) end
+ @doc_items = @num_items - @undoc_items
- ##
- # Prints when a file is added
+ @fully_documented = (@num_items - @doc_items) == 0
- def print_file(*) end
-
- ##
- # Prints when a method is added
+ @percent_doc = @doc_items.to_f / @num_items * 100 if @num_items.nonzero?
+ end
- def print_method(*) end
+ ##
+ # Returns the length and number of undocumented items in +collection+.
- ##
- # Prints when a module is added
+ def doc_stats collection
+ [collection.length, collection.count { |item| not item.documented? }]
+ end
- def print_module(*) end
+ ##
+ # Call this to mark the end of parsing for display purposes
- ##
- # Prints when RDoc is done
+ def done_adding
+ @display.done_adding
+ end
- def done_adding(*) end
+ ##
+ # The documentation status of this project. +true+ when 100%, +false+ when
+ # less than 100% and +nil+ when unknown.
+ #
+ # Set by calling #calculate
+ def fully_documented?
+ @fully_documented
end
##
- # Stats printer that prints just the files being documented with a progress
- # bar
+ # Returns a report on which items are not documented
- class Normal < Quiet
+ def report
+ report = []
- def begin_adding # :nodoc:
- puts "Parsing sources..."
- end
+ calculate
- ##
- # Prints a file with a progress bar
-
- def print_file(files_so_far, filename)
- progress_bar = sprintf("%3d%% [%2d/%2d] ",
- 100 * (files_so_far + 1) / @total_files,
- files_so_far + 1,
- @total_files)
-
- if $stdout.tty?
- # Print a progress bar, but make sure it fits on a single line. Filename
- # will be truncated if necessary.
- terminal_width = (ENV['COLUMNS'] || 80).to_i
- max_filename_size = terminal_width - progress_bar.size
- if filename.size > max_filename_size
- # Turn "some_long_filename.rb" to "...ong_filename.rb"
- filename = filename[(filename.size - max_filename_size) .. -1]
- filename[0..2] = "..."
- end
+ if @num_items == @doc_items then
+ report << '100% documentation!'
+ report << nil
+ report << 'Great Job!'
- # Pad the line with whitespaces so that leftover output from the
- # previous line doesn't show up.
- line = "#{progress_bar}#{filename}"
- padding = terminal_width - line.size
- line << (" " * padding) if padding > 0
-
- $stdout.print("#{line}\r")
- else
- $stdout.puts "#{progress_bar} #{filename}"
- end
- $stdout.flush
+ return report.join "\n"
end
- def done_adding # :nodoc:
- puts
- end
+ report << 'The following items are not documented:'
+ report << nil
+
+ ucm = RDoc::TopLevel.unique_classes_and_modules
+
+ ucm.sort.each do |cm|
+ type = case cm # TODO #definition
+ when RDoc::NormalClass then 'class'
+ when RDoc::SingleClass then 'class <<'
+ when RDoc::NormalModule then 'module'
+ end
+
+ if cm.fully_documented? then
+ next
+ elsif cm.in_files.empty? or
+ (cm.constants.empty? and cm.method_list.empty?) then
+ report << "# #{type} #{cm.full_name} is referenced but empty."
+ report << '#'
+ report << '# It probably came from another project. ' \
+ 'I\'m sorry I\'m holding it against you.'
+ report << nil
+
+ next
+ elsif cm.documented? then
+ report << "#{type} #{cm.full_name} # is documented"
+ else
+ report << '# in files:'
- end
+ cm.in_files.each do |file|
+ report << "# #{file.full_name}"
+ end
- ##
- # Stats printer that prints everything documented, including the documented
- # status
+ report << nil
- class Verbose < Normal
+ report << "#{type} #{cm.full_name}"
+ end
- ##
- # Returns a marker for RDoc::CodeObject +co+ being undocumented
+ unless cm.constants.empty? then
+ report << nil
- def nodoc co
- " (undocumented)" unless co.documented?
- end
+ cm.each_constant do |constant|
+ next if constant.documented?
+ report << " # in file #{constant.file.full_name}"
+ report << " #{constant.name} = nil"
+ end
+ end
- def print_alias as # :nodoc:
- puts "\t\talias #{as.new_name} #{as.old_name}#{nodoc as}"
- end
+ unless cm.attributes.empty? then
+ report << nil
- def print_class(klass) # :nodoc:
- puts "\tclass #{klass.full_name}#{nodoc klass}"
- end
+ cm.each_attribute do |attr|
+ next if attr.documented?
+ report << " #{attr.definition} #{attr.name} " \
+ "# in file #{attr.file.full_name}"
+ end
+ end
- def print_constant(constant) # :nodoc:
- puts "\t\t#{constant.name}#{nodoc constant}"
- end
+ unless cm.method_list.empty? then
+ report << nil
- def print_file(files_so_far, file) # :nodoc:
- super
- puts
- end
+ cm.each_method do |method|
+ next if method.documented?
+ report << " # in file #{method.file.full_name}"
+ report << " def #{method.name}#{method.params}; end"
+ report << nil
+ end
+ end
- def print_method(method) # :nodoc:
- puts "\t\t#{method.singleton ? '::' : '#'}#{method.name}#{nodoc method}"
+ report << 'end'
+ report << nil
end
- def print_module(mod) # :nodoc:
- puts "\tmodule #{mod.full_name}#{nodoc mod}"
- end
+ report.join "\n"
+ end
+ ##
+ # Returns a summary of the collected statistics.
+
+ def summary
+ calculate
+
+ report = []
+ report << 'Files: %5d' % @num_files
+ report << nil
+ report << 'Classes: %5d (%5d undocumented)' % [@num_classes,
+ @undoc_classes]
+ report << 'Modules: %5d (%5d undocumented)' % [@num_modules,
+ @undoc_modules]
+ report << 'Constants: %5d (%5d undocumented)' % [@num_constants,
+ @undoc_constants]
+ report << 'Attributes: %5d (%5d undocumented)' % [@num_attributes,
+ @undoc_attributes]
+ report << 'Methods: %5d (%5d undocumented)' % [@num_methods,
+ @undoc_methods]
+ report << nil
+ report << 'Total: %5d (%5d undocumented)' % [@num_items,
+ @undoc_items]
+
+ report << '%6.2f%% documented' % @percent_doc if @percent_doc
+ report << nil
+ report << 'Elapsed: %0.1fs' % (Time.now - @start)
+
+ report.join "\n"
end
-end
+ autoload :Quiet, 'rdoc/stats/quiet'
+ autoload :Normal, 'rdoc/stats/normal'
+ autoload :Verbose, 'rdoc/stats/verbose'
+end
diff --git a/lib/rdoc/stats/normal.rb b/lib/rdoc/stats/normal.rb
new file mode 100644
index 0000000000..2057332b2d
--- /dev/null
+++ b/lib/rdoc/stats/normal.rb
@@ -0,0 +1,51 @@
+##
+# Stats printer that prints just the files being documented with a progress
+# bar
+
+class RDoc::Stats::Normal < RDoc::Stats::Quiet
+
+ def begin_adding # :nodoc:
+ puts "Parsing sources..."
+ end
+
+ ##
+ # Prints a file with a progress bar
+
+ def print_file files_so_far, filename
+ progress_bar = sprintf("%3d%% [%2d/%2d] ",
+ 100 * files_so_far / @num_files,
+ files_so_far,
+ @num_files)
+
+ if $stdout.tty? then
+ # Print a progress bar, but make sure it fits on a single line. Filename
+ # will be truncated if necessary.
+ terminal_width = (ENV['COLUMNS'] || 80).to_i
+ max_filename_size = terminal_width - progress_bar.size
+
+ if filename.size > max_filename_size then
+ # Turn "some_long_filename.rb" to "...ong_filename.rb"
+ filename = filename[(filename.size - max_filename_size) .. -1]
+ filename[0..2] = "..."
+ end
+
+ # Pad the line with whitespaces so that leftover output from the
+ # previous line doesn't show up.
+ line = "#{progress_bar}#{filename}"
+ padding = terminal_width - line.size
+ line << (" " * padding) if padding > 0
+
+ $stdout.print("#{line}\r")
+ else
+ $stdout.puts "#{progress_bar} #{filename}"
+ end
+
+ $stdout.flush
+ end
+
+ def done_adding # :nodoc:
+ puts
+ end
+
+end
+
diff --git a/lib/rdoc/stats/quiet.rb b/lib/rdoc/stats/quiet.rb
new file mode 100644
index 0000000000..eed27b2a88
--- /dev/null
+++ b/lib/rdoc/stats/quiet.rb
@@ -0,0 +1,59 @@
+##
+# Stats printer that prints nothing
+
+class RDoc::Stats::Quiet
+
+ ##
+ # Creates a new Quiet that will print nothing
+
+ def initialize num_files
+ @num_files = num_files
+ end
+
+ ##
+ # Prints a message at the beginning of parsing
+
+ def begin_adding(*) end
+
+ ##
+ # Prints when an alias is added
+
+ def print_alias(*) end
+
+ ##
+ # Prints when an attribute is added
+
+ def print_attribute(*) end
+
+ ##
+ # Prints when a class is added
+
+ def print_class(*) end
+
+ ##
+ # Prints when a constant is added
+
+ def print_constant(*) end
+
+ ##
+ # Prints when a file is added
+
+ def print_file(*) end
+
+ ##
+ # Prints when a method is added
+
+ def print_method(*) end
+
+ ##
+ # Prints when a module is added
+
+ def print_module(*) end
+
+ ##
+ # Prints when RDoc is done
+
+ def done_adding(*) end
+
+end
+
diff --git a/lib/rdoc/stats/verbose.rb b/lib/rdoc/stats/verbose.rb
new file mode 100644
index 0000000000..430809ae07
--- /dev/null
+++ b/lib/rdoc/stats/verbose.rb
@@ -0,0 +1,45 @@
+##
+# Stats printer that prints everything documented, including the documented
+# status
+
+class RDoc::Stats::Verbose < RDoc::Stats::Normal
+
+ ##
+ # Returns a marker for RDoc::CodeObject +co+ being undocumented
+
+ def nodoc co
+ " (undocumented)" unless co.documented?
+ end
+
+ def print_alias as # :nodoc:
+ puts " alias #{as.new_name} #{as.old_name}#{nodoc as}"
+ end
+
+ def print_attribute attribute # :nodoc:
+ puts " #{attribute.definition} #{attribute.name}#{nodoc attribute}"
+ end
+
+ def print_class(klass) # :nodoc:
+ puts " class #{klass.full_name}#{nodoc klass}"
+ end
+
+ def print_constant(constant) # :nodoc:
+ puts " #{constant.name}#{nodoc constant}"
+ end
+
+ def print_file(files_so_far, file) # :nodoc:
+ super
+ puts
+ end
+
+ def print_method(method) # :nodoc:
+ puts " #{method.singleton ? '::' : '#'}#{method.name}#{nodoc method}"
+ end
+
+ def print_module(mod) # :nodoc:
+ puts " module #{mod.full_name}#{nodoc mod}"
+ end
+
+end
+
+
diff --git a/lib/rdoc/task.rb b/lib/rdoc/task.rb
index f87ef7dc0e..005c516eed 100644
--- a/lib/rdoc/task.rb
+++ b/lib/rdoc/task.rb
@@ -21,13 +21,238 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
+require 'rubygems'
+begin
+ gem 'rdoc'
+rescue Gem::LoadError
+end
+
+begin
+ gem 'rake'
+rescue Gem::LoadError
+end
+
require 'rdoc'
require 'rake'
-require 'rake/rdoctask'
+require 'rake/tasklib'
+
+##
+# Create a documentation task that will generate the RDoc files for a project.
+#
+# The RDoc::Task will create the following targets:
+#
+# [rdoc]
+# Main task for this RDoc task.
+#
+# [clobber_rdoc]
+# Delete all the rdoc files. This target is automatically added to the main
+# clobber target.
+#
+# [rerdoc]
+# Rebuild the rdoc files from scratch, even if they are not out of date.
+#
+# Simple Example:
+#
+# RDoc::Task.new do |rd|
+# rd.main = "README.rdoc"
+# rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
+# end
+#
+# The +rd+ object passed to the block is an RDoc::Task object. See the
+# attributes list for the RDoc::Task class for available customization options.
+#
+# == Specifying different task names
+#
+# You may wish to give the task a different name, such as if you are
+# generating two sets of documentation. For instance, if you want to have a
+# development set of documentation including private methods:
+#
+# RDoc::Task.new :rdoc_dev do |rd|
+# rd.main = "README.doc"
+# rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
+# rd.options << "--all"
+# end
+#
+# The tasks would then be named :<em>rdoc_dev</em>,
+# :clobber_<em>rdoc_dev</em>, and :re<em>rdoc_dev</em>.
+#
+# If you wish to have completely different task names, then pass a Hash as
+# first argument. With the <tt>:rdoc</tt>, <tt>:clobber_rdoc</tt> and
+# <tt>:rerdoc</tt> options, you can customize the task names to your liking.
+#
+# For example:
+#
+# RDoc::Task.new(:rdoc => "rdoc", :clobber_rdoc => "rdoc:clean",
+# :rerdoc => "rdoc:force")
+#
+# This will create the tasks <tt>:rdoc</tt>, <tt>:rdoc:clean</tt> and
+# <tt>:rdoc:force</tt>.
+
+class RDoc::Task < Rake::TaskLib
+
+ ##
+ # Name of the main, top level task. (default is :rdoc)
+
+ attr_accessor :name
+
+ ##
+ # Name of directory to receive the html output files. (default is "html")
+
+ attr_accessor :rdoc_dir
+
+ ##
+ # Title of RDoc documentation. (defaults to rdoc's default)
+
+ attr_accessor :title
+
+ ##
+ # Name of file to be used as the main, top level file of the RDoc. (default
+ # is none)
+
+ attr_accessor :main
+
+ ##
+ # Name of template to be used by rdoc. (defaults to rdoc's default)
+
+ attr_accessor :template
+
+ ##
+ # List of files to be included in the rdoc generation. (default is [])
+
+ attr_accessor :rdoc_files
+
+ ##
+ # Additional list of options to be passed rdoc. (default is [])
+
+ attr_accessor :options
+
+ ##
+ # Whether to run the rdoc process as an external shell (default is false)
+
+ attr_accessor :external
+
+ ##
+ # Create an RDoc task with the given name. See the RDoc::Task class overview
+ # for documentation.
+
+ def initialize(name = :rdoc) # :yield: self
+ if name.is_a? Hash then
+ invalid_options = name.keys.map { |k| k.to_sym } -
+ [:rdoc, :clobber_rdoc, :rerdoc]
+
+ unless invalid_options.empty? then
+ raise ArgumentError, "invalid options: #{invalid_options.join(", ")}"
+ end
+ end
+
+ @name = name
+ @rdoc_files = Rake::FileList.new
+ @rdoc_dir = 'html'
+ @main = nil
+ @title = nil
+ @template = nil
+ @options = []
+ yield self if block_given?
+ define
+ end
+
+ ##
+ # Create the tasks defined by this task lib.
+
+ def define
+ desc "Build RDoc HTML files"
+ task rdoc_task_name
+
+ desc "Rebuild RDoc HTML files"
+ task rerdoc_task_name => [clobber_task_name, rdoc_task_name]
+
+ desc "Remove RDoc HTML files"
+ task clobber_task_name do
+ rm_r @rdoc_dir rescue nil
+ end
+
+ task :clobber => [clobber_task_name]
+
+ directory @rdoc_dir
+
+ rdoc_target_deps = [
+ @rdoc_files,
+ Rake.application.rakefile
+ ].flatten.compact
+
+ task rdoc_task_name => [rdoc_target]
+ file rdoc_target => rdoc_target_deps do
+ @before_running_rdoc.call if @before_running_rdoc
+ args = option_list + @rdoc_files
+
+ if Rake.application.options.trace then
+ $stderr.puts "rdoc #{args.join ' '}"
+ end
+ require 'rdoc/rdoc'
+ RDoc::RDoc.new.document(args)
+ end
+
+ self
+ end
+
+ ##
+ # List of options that will be supplied to RDoc
+
+ def option_list
+ result = @options.dup
+ result << "-o" << @rdoc_dir
+ result << "--main" << main if main
+ result << "--title" << title if title
+ result << "-T" << template if template
+ result
+ end
+
+ ##
+ # The block passed to this method will be called just before running the
+ # RDoc generator. It is allowed to modify RDoc::Task attributes inside the
+ # block.
+
+ def before_running_rdoc(&block)
+ @before_running_rdoc = block
+ end
+
+ private
+
+ def rdoc_target
+ "#{rdoc_dir}/index.html"
+ end
+
+ def rdoc_task_name
+ case name
+ when Hash then (name[:rdoc] || "rdoc").to_s
+ else name.to_s
+ end
+ end
+
+ def clobber_task_name
+ case name
+ when Hash then (name[:clobber_rdoc] || "clobber_rdoc").to_s
+ else "clobber_#{name}"
+ end
+ end
+
+ def rerdoc_task_name
+ case name
+ when Hash then (name[:rerdoc] || "rerdoc").to_s
+ else "re#{name}"
+ end
+ end
+
+end
# :stopdoc:
-module RDoc
- Task = Rake::RDocTask
+module Rake
+
+ ##
+ # For backwards compatibility
+
+ RDocTask = RDoc::Task
+
end
# :startdoc:
diff --git a/lib/rdoc/text.rb b/lib/rdoc/text.rb
index 5280aa0fd2..20cd8a258a 100644
--- a/lib/rdoc/text.rb
+++ b/lib/rdoc/text.rb
@@ -1,9 +1,44 @@
+# coding: utf-8
+
+##
+# For RDoc::Text#to_html
+
+require 'strscan'
+
##
# Methods for manipulating comment text
module RDoc::Text
##
+ # Maps an encoding to a Hash of characters properly transcoded for that
+ # encoding.
+ #
+ # See also encode_fallback.
+
+ TO_HTML_CHARACTERS = Hash.new do |h, encoding|
+ h[encoding] = {
+ :close_dquote => encode_fallback('”', encoding, '"'),
+ :close_squote => encode_fallback('’', encoding, '\''),
+ :copyright => encode_fallback('©', encoding, '(c)'),
+ :ellipsis => encode_fallback('…', encoding, '...'),
+ :em_dash => encode_fallback('—', encoding, '---'),
+ :en_dash => encode_fallback('–', encoding, '--'),
+ :open_dquote => encode_fallback('“', encoding, '"'),
+ :open_squote => encode_fallback('‘', encoding, '\''),
+ :trademark => encode_fallback('®', encoding, '(r)'),
+ }
+ end if Object.const_defined? :Encoding
+
+ ##
+ # Transcodes +character+ to +encoding+ with a +fallback+ character.
+
+ def self.encode_fallback character, encoding, fallback
+ character.encode(encoding, :fallback => { character => fallback },
+ :undef => :replace, :replace => fallback)
+ end
+
+ ##
# Expands tab characters in +text+ to eight spaces
def expand_tabs text
@@ -43,8 +78,7 @@ module RDoc::Text
end
##
- # Convert a string in markup format into HTML. Removes the first paragraph
- # tags if +remove_para+ is true.
+ # Convert a string in markup format into HTML.
#
# Requires the including class to implement #formatter
@@ -105,7 +139,7 @@ http://rubyforge.org/tracker/?atid=2472&group_id=627&func=browse
def strip_hashes text
return text if text =~ /^(?>\s*)[^\#]/
- text.gsub(/^\s*(#+)/) { $1.tr '#',' ' }
+ text.gsub(/^\s*(#+)/) { $1.tr '#',' ' }.gsub(/^\s+$/, '')
end
##
@@ -123,7 +157,102 @@ http://rubyforge.org/tracker/?atid=2472&group_id=627&func=browse
text.sub! %r%/\*+% do " " * $&.length end
text.sub! %r%\*+/% do " " * $&.length end
text.gsub! %r%^[ \t]*\*%m do " " * $&.length end
- text
+ text.gsub(/^\s+$/, '')
+ end
+
+ ##
+ # Converts ampersand, dashes, ellipsis, quotes, copyright and registered
+ # trademark symbols in +text+ to properly encoded characters.
+
+ def to_html text
+ if Object.const_defined? :Encoding then
+ html = ''.encode text.encoding
+
+ encoded = RDoc::Text::TO_HTML_CHARACTERS[text.encoding]
+ else
+ html = ''
+ encoded = {
+ :close_dquote => '”',
+ :close_squote => '’',
+ :copyright => '©',
+ :ellipsis => '…',
+ :em_dash => '—',
+ :en_dash => '–',
+ :open_dquote => '“',
+ :open_squote => '‘',
+ :trademark => '®',
+ }
+ end
+
+ s = StringScanner.new text
+ insquotes = false
+ indquotes = false
+ after_word = nil
+
+ until s.eos? do
+ case
+ when s.scan(/<tt>.*?<\/tt>/) then # skip contents of tt
+ html << s.matched.gsub('\\\\', '\\')
+ when s.scan(/<tt>.*?/) then
+ warn 'mismatched <tt> tag' # TODO signal file/line
+ html << s.matched
+ when s.scan(/<[^>]+\/?s*>/) then # skip HTML tags
+ html << s.matched
+ when s.scan(/\\(\S)/) then # unhandled suppressed crossref
+ html << s[1]
+ after_word = nil
+ when s.scan(/\.\.\.(\.?)/) then
+ html << s[1] << encoded[:ellipsis]
+ after_word = nil
+ when s.scan(/\(c\)/) then
+ html << encoded[:copyright]
+ after_word = nil
+ when s.scan(/\(r\)/) then
+ html << encoded[:trademark]
+ after_word = nil
+ when s.scan(/---/) then
+ html << encoded[:em_dash]
+ after_word = nil
+ when s.scan(/--/) then
+ html << encoded[:en_dash]
+ after_word = nil
+ when s.scan(/&quot;|"/) then
+ html << encoded[indquotes ? :close_dquote : :open_dquote]
+ indquotes = !indquotes
+ after_word = nil
+ when s.scan(/``/) then # backtick double quote
+ html << encoded[:open_dquote]
+ after_word = nil
+ when s.scan(/''/) then # tick double quote
+ html << encoded[:close_dquote]
+ after_word = nil
+ when s.scan(/'/) then # single quote
+ if insquotes
+ html << encoded[:close_squote]
+ insquotes = false
+ elsif after_word
+ # Mary's dog, my parents' house: do not start paired quotes
+ html << encoded[:close_squote]
+ else
+ html << encoded[:open_squote]
+ insquotes = true
+ end
+
+ after_word = nil
+ else # advance to the next potentially significant character
+ match = s.scan(/.+?(?=[<\\.("'`&-])/) #"
+
+ if match then
+ html << match
+ after_word = match =~ /\w$/
+ else
+ html << s.rest
+ break
+ end
+ end
+ end
+
+ html
end
end
diff --git a/lib/rdoc/tokenstream.rb b/lib/rdoc/token_stream.rb
index b1e86543f7..fb887f2fa4 100644
--- a/lib/rdoc/tokenstream.rb
+++ b/lib/rdoc/token_stream.rb
@@ -1,5 +1,3 @@
-module RDoc; end
-
##
# A TokenStream is a list of tokens, gathered during the parse of some entity
# (say a method). Entities populate these streams by being registered with the
diff --git a/lib/rdoc/top_level.rb b/lib/rdoc/top_level.rb
index 306790fc15..d0ea621f8c 100644
--- a/lib/rdoc/top_level.rb
+++ b/lib/rdoc/top_level.rb
@@ -20,7 +20,14 @@ class RDoc::TopLevel < RDoc::Context
attr_accessor :absolute_name
- attr_accessor :diagram
+ ##
+ # All the classes or modules that were declared in
+ # this file. These are assigned to either +#classes_hash+
+ # or +#modules_hash+ once we know what they really are.
+
+ attr_reader :classes_or_modules
+
+ attr_accessor :diagram # :nodoc:
##
# The parser that processed this file
@@ -28,45 +35,110 @@ class RDoc::TopLevel < RDoc::Context
attr_accessor :parser
##
- # Returns all classes and modules discovered by RDoc
+ # Returns all classes discovered by RDoc
- def self.all_classes_and_modules
- classes_hash.values + modules_hash.values
+ def self.all_classes
+ @all_classes_hash.values
end
##
- # Returns all classes discovered by RDoc
+ # Returns all classes and modules discovered by RDoc
- def self.classes
- classes_hash.values
+ def self.all_classes_and_modules
+ @all_classes_hash.values + @all_modules_hash.values
end
##
# Hash of all classes known to RDoc
- def self.classes_hash
- @all_classes
+ def self.all_classes_hash
+ @all_classes_hash
end
##
# All TopLevels known to RDoc
- def self.files
- @all_files.values
+ def self.all_files
+ @all_files_hash.values
end
##
# Hash of all files known to RDoc
- def self.files_hash
- @all_files
+ def self.all_files_hash
+ @all_files_hash
+ end
+
+ ##
+ # Returns all modules discovered by RDoc
+
+ def self.all_modules
+ all_modules_hash.values
+ end
+
+ ##
+ # Hash of all modules known to RDoc
+
+ def self.all_modules_hash
+ @all_modules_hash
+ end
+
+ ##
+ # Prepares the RDoc code object tree for use by a generator.
+ #
+ # It finds unique classes/modules defined, and replaces classes/modules that
+ # are aliases for another one by a copy with RDoc::ClassModule#is_alias_for
+ # set.
+ #
+ # It updates the RDoc::ClassModule#constant_aliases attribute of "real"
+ # classes or modules.
+ #
+ # It also completely removes the classes and modules that should be removed
+ # from the documentation and the methods that have a visibility below
+ # +min_visibility+, which is the <tt>--visibility</tt> option.
+ #
+ # See also RDoc::Context#remove_from_documentation?
+
+ def self.complete min_visibility
+ fix_basic_object_inheritance
+
+ # cache included modules before they are removed from the documentation
+ all_classes_and_modules.each { |cm| cm.ancestors }
+
+ remove_nodoc @all_classes_hash
+ remove_nodoc @all_modules_hash
+
+ @unique_classes = find_unique @all_classes_hash
+ @unique_modules = find_unique @all_modules_hash
+
+ unique_classes_and_modules.each do |cm|
+ cm.complete min_visibility
+ end
+
+ @all_files_hash.each_key do |file_name|
+ tl = @all_files_hash[file_name]
+
+ unless RDoc::Parser::Simple === tl.parser then
+ tl.modules_hash.clear
+ tl.classes_hash.clear
+
+ tl.classes_or_modules.each do |cm|
+ name = cm.full_name
+ if cm.type == 'class' then
+ tl.classes_hash[name] = cm if @all_classes_hash[name]
+ else
+ tl.modules_hash[name] = cm if @all_modules_hash[name]
+ end
+ end
+ end
+ end
end
##
# Finds the class with +name+ in all discovered classes
def self.find_class_named(name)
- classes_hash[name]
+ @all_classes_hash[name]
end
##
@@ -91,9 +163,7 @@ class RDoc::TopLevel < RDoc::Context
# Finds the class or module with +name+
def self.find_class_or_module(name)
- name =~ /^::/
- name = $' || name
-
+ name = $' if name =~ /^::/
RDoc::TopLevel.classes_hash[name] || RDoc::TopLevel.modules_hash[name]
end
@@ -101,7 +171,7 @@ class RDoc::TopLevel < RDoc::Context
# Finds the file with +name+ in all discovered files
def self.find_file_named(name)
- @all_files[name]
+ @all_files_hash[name]
end
##
@@ -112,26 +182,98 @@ class RDoc::TopLevel < RDoc::Context
end
##
- # Returns all modules discovered by RDoc
+ # Finds unique classes/modules defined in +all_hash+,
+ # and returns them as an array. Performs the alias
+ # updates in +all_hash+: see ::complete.
+ #--
+ # TODO aliases should be registered by Context#add_module_alias
- def self.modules
- modules_hash.values
+ def self.find_unique(all_hash)
+ unique = []
+
+ all_hash.each_pair do |full_name, cm|
+ unique << cm if full_name == cm.full_name
+ end
+
+ unique
end
##
- # Hash of all modules known to RDoc
+ # Fixes the erroneous <tt>BasicObject < Object</tt> in 1.9.
+ #
+ # Because we assumed all classes without a stated superclass
+ # inherit from Object, we have the above wrong inheritance.
+ #
+ # We fix BasicObject right away if we are running in a Ruby
+ # version >= 1.9. If not, we may be documenting 1.9 source
+ # while running under 1.8: we search the files of BasicObject
+ # for "object.c", and fix the inheritance if we find it.
+
+ def self.fix_basic_object_inheritance
+ basic = all_classes_hash['BasicObject']
+ return unless basic
+ if RUBY_VERSION >= '1.9'
+ basic.superclass = nil
+ elsif basic.in_files.any? { |f| File.basename(f.full_name) == 'object.c' }
+ basic.superclass = nil
+ end
+ end
- def self.modules_hash
- @all_modules
+ ##
+ # Removes from +all_hash+ the contexts that are nodoc or have no content.
+ #
+ # See RDoc::Context#remove_from_documentation?
+
+ def self.remove_nodoc(all_hash)
+ all_hash.keys.each do |name|
+ context = all_hash[name]
+ all_hash.delete(name) if context.remove_from_documentation?
+ end
end
##
# Empties RDoc of stored class, module and file information
def self.reset
- @all_classes = {}
- @all_modules = {}
- @all_files = {}
+ @all_classes_hash = {}
+ @all_modules_hash = {}
+ @all_files_hash = {}
+ end
+
+ ##
+ # Returns the unique classes discovered by RDoc.
+ #
+ # ::complete must have been called prior to using this method.
+
+ def self.unique_classes
+ @unique_classes
+ end
+
+ ##
+ # Returns the unique classes and modules discovered by RDoc.
+ # ::complete must have been called prior to using this method.
+
+ def self.unique_classes_and_modules
+ @unique_classes + @unique_modules
+ end
+
+ ##
+ # Returns the unique modules discovered by RDoc.
+ # ::complete must have been called prior to using this method.
+
+ def self.unique_modules
+ @unique_modules
+ end
+
+ class << self
+ alias classes all_classes
+ alias classes_hash all_classes_hash
+
+ alias files all_files
+ alias files_hash all_files_hash
+
+ alias modules all_modules
+ alias modules_hash all_modules_hash
end
reset
@@ -148,17 +290,49 @@ class RDoc::TopLevel < RDoc::Context
@diagram = nil
@parser = nil
+ @classes_or_modules = []
+
RDoc::TopLevel.files_hash[file_name] = self
end
##
- # Adds +method+ to Object instead of RDoc::TopLevel
+ # Adds +an_alias+ to +Object+ instead of +self+.
+
+ def add_alias(an_alias)
+ return an_alias unless @document_self
+ object_class.add_alias an_alias
+ end
+
+ ##
+ # Adds +constant+ to +Object+ instead of +self+.
+
+ def add_constant(constant)
+ return constant unless @document_self
+ object_class.add_constant constant
+ end
+
+ ##
+ # Adds +include+ to +Object+ instead of +self+.
+
+ def add_include(include)
+ return include unless @document_self
+ object_class.add_include include
+ end
+
+ ##
+ # Adds +method+ to +Object+ instead of +self+.
def add_method(method)
- object = self.class.find_class_named 'Object'
- object = add_class RDoc::NormalClass, 'Object' unless object
+ return method unless @document_self
+ object_class.add_method method
+ end
+
+ ##
+ # Adds class or module +mod+. Used in the building phase
+ # by the ruby parser.
- object.add_method method
+ def add_to_classes_or_modules mod
+ @classes_or_modules << mod
end
##
@@ -168,8 +342,13 @@ class RDoc::TopLevel < RDoc::Context
File.basename @absolute_name
end
+ alias name base_name
+
##
- # See RDoc::TopLevel.find_class_or_module
+ # See RDoc::TopLevel::find_class_or_module
+ #--
+ # TODO Why do we search through all classes/modules found, not just the
+ # ones of this instance?
def find_class_or_module name
RDoc::TopLevel.find_class_or_module name
@@ -186,11 +365,11 @@ class RDoc::TopLevel < RDoc::Context
# Finds a module or class with +name+
def find_module_named(name)
- find_class_or_module(name) || find_enclosing_module_named(name)
+ find_class_or_module(name)
end
##
- # The name of this file
+ # Returns the relative name of this file
def full_name
@relative_name
@@ -215,16 +394,24 @@ class RDoc::TopLevel < RDoc::Context
end
##
- # Date this file was last modified, if known
+ # Time this file was last modified, if known
def last_modified
- @file_stat ? file_stat.mtime.to_s : 'Unknown'
+ @file_stat ? file_stat.mtime : nil
end
##
- # Base name of this file
-
- alias name base_name
+ # Returns the NormalClass "Object", creating it if not found.
+ #
+ # Records +self+ as a location in "Object".
+
+ def object_class
+ @object_class ||= begin
+ oc = self.class.find_class_named('Object') || add_class(RDoc::NormalClass, 'Object')
+ oc.record_location self
+ oc
+ end
+ end
##
# Path to this file
@@ -244,5 +431,9 @@ class RDoc::TopLevel < RDoc::Context
end
end
+ def to_s # :nodoc:
+ "file #{full_name}"
+ end
+
end
diff --git a/test/rdoc/test.ja.rdoc b/test/rdoc/test.ja.rdoc
index 96e1db93d3..cd01cab37a 100644
--- a/test/rdoc/test.ja.rdoc
+++ b/test/rdoc/test.ja.rdoc
@@ -1,3 +1,5 @@
+# -*- coding: utf-8 -*-
+
こんにちは!
初めまして。アーロンと申します。
diff --git a/test/rdoc/test_attribute_manager.rb b/test/rdoc/test_attribute_manager.rb
index e908b86b02..25e8ca5e04 100644
--- a/test/rdoc/test_attribute_manager.rb
+++ b/test/rdoc/test_attribute_manager.rb
@@ -2,29 +2,35 @@ require 'rubygems'
require 'minitest/autorun'
require 'rdoc'
require 'rdoc/markup'
+require 'rdoc/markup/formatter'
require 'rdoc/markup/attribute_manager'
-class TestAttributeManager < MiniTest::Unit::TestCase
+class TestAttributeManager < MiniTest::Unit::TestCase # HACK fix test name
def setup
@am = RDoc::Markup::AttributeManager.new
@klass = RDoc::Markup::AttributeManager
+ @formatter = RDoc::Markup::Formatter.new
+ @formatter.add_tag :BOLD, '<B>', '</B>'
+ @formatter.add_tag :EM, '<EM>', '</EM>'
+ @formatter.add_tag :TT, '<TT>', '</TT>'
end
def test_convert_attrs_ignores_code
- collector = RDoc::Markup::AttrSpan.new 10
- str = 'foo <code>__send__</code> bar'
- @am.convert_html str, collector
- @am.convert_attrs str, collector
- assert_match(/__send__/, str)
+ assert_equal 'foo <TT>__send__</TT> bar', output('foo <code>__send__</code> bar')
end
def test_convert_attrs_ignores_tt
- collector = RDoc::Markup::AttrSpan.new 10
- str = 'foo <tt>__send__</tt> bar'
- @am.convert_html str, collector
- @am.convert_attrs str, collector
- assert_match(/__send__/, str)
+ assert_equal 'foo <TT>__send__</TT> bar', output('foo <tt>__send__</tt> bar')
+ end
+
+ def test_convert_attrs_preserves_double
+ assert_equal 'foo.__send__ :bar', output('foo.__send__ :bar')
+ assert_equal 'use __FILE__ to', output('use __FILE__ to')
+ end
+
+ def test_convert_attrs_does_not_ignore_after_tt
+ assert_equal 'the <TT>IF:</TT><EM>key</EM> directive', output('the <tt>IF:</tt>_key_ directive')
end
def test_initial_word_pairs
@@ -73,12 +79,41 @@ class TestAttributeManager < MiniTest::Unit::TestCase
assert(specials.has_key?("WikiWord"))
end
- def silently(&block)
- warn_level = $VERBOSE
- $VERBOSE = nil
- result = block.call
- $VERBOSE = warn_level
- result
+ def test_escapes
+ assert_equal '<TT>text</TT>', output('<tt>text</tt>')
+ assert_equal '<tt>text</tt>', output('\\<tt>text</tt>')
+ assert_equal '<tt>', output('\\<tt>')
+ assert_equal '<TT><tt></TT>', output('<tt>\\<tt></tt>')
+ assert_equal '<TT>\\<tt></TT>', output('<tt>\\\\<tt></tt>')
+ assert_equal '<B>text</B>', output('*text*')
+ assert_equal '*text*', output('\\*text*')
+ assert_equal '\\', output('\\')
+ assert_equal '\\text', output('\\text')
+ assert_equal '\\\\text', output('\\\\text')
+ assert_equal 'text \\ text', output('text \\ text')
+
+ assert_equal 'and <TT>\\s</TT> matches space',
+ output('and <tt>\\s</tt> matches space')
+ assert_equal 'use <TT><tt>text</TT></tt> for code',
+ output('use <tt>\\<tt>text</tt></tt> for code')
+ assert_equal 'use <TT><tt>text</tt></TT> for code',
+ output('use <tt>\\<tt>text\\</tt></tt> for code')
+ assert_equal 'use <tt><tt>text</tt></tt> for code',
+ output('use \\<tt>\\<tt>text</tt></tt> for code')
+ assert_equal 'use <tt><TT>text</TT></tt> for code',
+ output('use \\<tt><tt>text</tt></tt> for code')
+ assert_equal 'use <TT>+text+</TT> for code',
+ output('use <tt>\\+text+</tt> for code')
+ assert_equal 'use <tt><TT>text</TT></tt> for code',
+ output('use \\<tt>+text+</tt> for code')
+ assert_equal 'illegal <tag>not</tag> changed',
+ output('illegal <tag>not</tag> changed')
+ assert_equal 'unhandled <p>tag</p> unchanged',
+ output('unhandled <p>tag</p> unchanged')
+ end
+
+ def output str
+ @formatter.convert_flow @am.flow str
end
end
diff --git a/test/rdoc/test_rdoc_alias.rb b/test/rdoc/test_rdoc_alias.rb
new file mode 100644
index 0000000000..ff499af962
--- /dev/null
+++ b/test/rdoc/test_rdoc_alias.rb
@@ -0,0 +1,13 @@
+require File.expand_path '../xref_test_case', __FILE__
+
+class TestRDocAlias < XrefTestCase
+
+ def test_to_s
+ a = RDoc::Alias.new nil, 'a', 'b', ''
+ a.parent = @c2
+
+ assert_equal 'alias: b -> #a in: RDoc::NormalClass C2 < Object', a.to_s
+ end
+
+end
+
diff --git a/test/rdoc/test_rdoc_any_method.rb b/test/rdoc/test_rdoc_any_method.rb
index a4c3eec48c..2a7ae9042a 100644
--- a/test/rdoc/test_rdoc_any_method.rb
+++ b/test/rdoc/test_rdoc_any_method.rb
@@ -1,15 +1,17 @@
require File.expand_path '../xref_test_case', __FILE__
+require 'rdoc/code_objects'
+require 'rdoc/generator/markup'
class RDocAnyMethodTest < XrefTestCase
def test_aref
m = RDoc::AnyMethod.new nil, 'method?'
- assert_equal 'method-i-method%3F', m.aref
+ assert_equal 'method-i-method-3F', m.aref
m.singleton = true
- assert_equal 'method-c-method%3F', m.aref
+ assert_equal 'method-c-method-3F', m.aref
end
def test_arglists
@@ -36,6 +38,45 @@ method(a, b) { |c, d| ... }
assert_equal 'C1::m', @c1.method_list.first.full_name
end
+ def test_markup_code
+ tokens = [
+ RDoc::RubyToken::TkCONSTANT. new(0, 0, 0, 'CONSTANT'),
+ RDoc::RubyToken::TkKW. new(0, 0, 0, 'KW'),
+ RDoc::RubyToken::TkIVAR. new(0, 0, 0, 'IVAR'),
+ RDoc::RubyToken::TkOp. new(0, 0, 0, 'Op'),
+ RDoc::RubyToken::TkId. new(0, 0, 0, 'Id'),
+ RDoc::RubyToken::TkNode. new(0, 0, 0, 'Node'),
+ RDoc::RubyToken::TkCOMMENT. new(0, 0, 0, 'COMMENT'),
+ RDoc::RubyToken::TkREGEXP. new(0, 0, 0, 'REGEXP'),
+ RDoc::RubyToken::TkSTRING. new(0, 0, 0, 'STRING'),
+ RDoc::RubyToken::TkVal. new(0, 0, 0, 'Val'),
+ RDoc::RubyToken::TkBACKSLASH.new(0, 0, 0, '\\'),
+ ]
+
+ @c2_a.collect_tokens
+ @c2_a.add_tokens(*tokens)
+
+ expected = [
+ '<span class="ruby-constant">CONSTANT</span>',
+ '<span class="ruby-keyword">KW</span>',
+ '<span class="ruby-ivar">IVAR</span>',
+ '<span class="ruby-operator">Op</span>',
+ '<span class="ruby-identifier">Id</span>',
+ '<span class="ruby-node">Node</span>',
+ '<span class="ruby-comment">COMMENT</span>',
+ '<span class="ruby-regexp">REGEXP</span>',
+ '<span class="ruby-string">STRING</span>',
+ '<span class="ruby-value">Val</span>',
+ '\\'
+ ].join
+
+ assert_equal expected, @c2_a.markup_code
+ end
+
+ def test_markup_code_empty
+ assert_equal '', @c2_a.markup_code
+ end
+
def test_marshal_load
instance_method = Marshal.load Marshal.dump(@c1.method_list.last)
diff --git a/test/rdoc/test_rdoc_attr.rb b/test/rdoc/test_rdoc_attr.rb
index 10965d00b6..9751cc175d 100644
--- a/test/rdoc/test_rdoc_attr.rb
+++ b/test/rdoc/test_rdoc_attr.rb
@@ -8,6 +8,12 @@ class TestRDocAttr < MiniTest::Unit::TestCase
@a = RDoc::Attr.new nil, 'attr', 'RW', ''
end
+ def test_aref
+ m = RDoc::Attr.new nil, 'attr', 'RW', nil
+
+ assert_equal 'attribute-i-attr', m.aref
+ end
+
def test_arglists
assert_nil @a.arglists
end
@@ -20,6 +26,18 @@ class TestRDocAttr < MiniTest::Unit::TestCase
assert_nil @a.call_seq
end
+ def test_definition
+ assert_equal 'attr_accessor', @a.definition
+
+ @a.rw = 'R'
+
+ assert_equal 'attr_reader', @a.definition
+
+ @a.rw = 'W'
+
+ assert_equal 'attr_writer', @a.definition
+ end
+
def test_full_name
assert_equal '(unknown)#attr', @a.full_name
end
@@ -33,15 +51,10 @@ class TestRDocAttr < MiniTest::Unit::TestCase
end
def test_type
- assert_equal 'attr_accessor', @a.type
-
- @a.rw = 'R'
-
- assert_equal 'attr_reader', @a.type
-
- @a.rw = 'W'
+ assert_equal 'instance', @a.type
- assert_equal 'attr_writer', @a.type
+ @a.singleton = true
+ assert_equal 'class', @a.type
end
end
diff --git a/test/rdoc/test_rdoc_class_module.rb b/test/rdoc/test_rdoc_class_module.rb
index 23291b969e..aa9bab5ec9 100644
--- a/test/rdoc/test_rdoc_class_module.rb
+++ b/test/rdoc/test_rdoc_class_module.rb
@@ -92,9 +92,142 @@ class TestRDocClassModule < XrefTestCase
assert_equal expected, cm1.method_list.sort
end
+ def test_remove_nodoc_children
+ parent = RDoc::ClassModule.new 'A'
+ parent.modules_hash.replace 'B' => true, 'C' => true
+ RDoc::TopLevel.all_modules_hash.replace 'A::B' => true
+
+ parent.classes_hash.replace 'D' => true, 'E' => true
+ RDoc::TopLevel.all_classes_hash.replace 'A::D' => true
+
+ parent.remove_nodoc_children
+
+ assert_equal %w[B], parent.modules_hash.keys
+ assert_equal %w[D], parent.classes_hash.keys
+ end
+
def test_superclass
assert_equal @c3_h1, @c3_h2.superclass
end
+ def test_update_aliases_class
+ n1 = @xref_data.add_module RDoc::NormalClass, 'N1'
+ n1_k2 = n1.add_module RDoc::NormalClass, 'N2'
+
+ n1.add_module_alias n1_k2, 'A1'
+
+ n1_a1_c = n1.constants.find { |c| c.name == 'A1' }
+ refute_nil n1_a1_c
+ assert_equal n1_k2, n1_a1_c.is_alias_for, 'sanity check'
+
+ n1.update_aliases
+
+ n1_a1_k = @xref_data.find_class_or_module 'N1::A1'
+ refute_nil n1_a1_k
+ assert_equal n1_k2, n1_a1_k.is_alias_for
+ refute_equal n1_k2, n1_a1_k
+
+ assert_equal 1, n1_k2.aliases.length
+ assert_equal n1_a1_k, n1_k2.aliases.first
+
+ assert_equal 'N1::N2', n1_k2.full_name
+ assert_equal 'N1::A1', n1_a1_k.full_name
+ end
+
+ def test_update_aliases_module
+ n1 = @xref_data.add_module RDoc::NormalModule, 'N1'
+ n1_n2 = n1.add_module RDoc::NormalModule, 'N2'
+
+ n1.add_module_alias n1_n2, 'A1'
+
+ n1_a1_c = n1.constants.find { |c| c.name == 'A1' }
+ refute_nil n1_a1_c
+ assert_equal n1_n2, n1_a1_c.is_alias_for, 'sanity check'
+
+ n1.update_aliases
+
+ n1_a1_m = @xref_data.find_class_or_module 'N1::A1'
+ refute_nil n1_a1_m
+ assert_equal n1_n2, n1_a1_m.is_alias_for
+ refute_equal n1_n2, n1_a1_m
+
+ assert_equal 1, n1_n2.aliases.length
+ assert_equal n1_a1_m, n1_n2.aliases.first
+
+ assert_equal 'N1::N2', n1_n2.full_name
+ assert_equal 'N1::A1', n1_a1_m.full_name
+ end
+
+ def test_update_aliases_reparent
+ l1 = @xref_data.add_module RDoc::NormalModule, 'L1'
+ l1_l2 = l1.add_module RDoc::NormalModule, 'L2'
+ o1 = @xref_data.add_module RDoc::NormalModule, 'O1'
+
+ o1.add_module_alias l1_l2, 'A1'
+
+ o1_a1_c = o1.constants.find { |c| c.name == 'A1' }
+ refute_nil o1_a1_c
+ assert_equal l1_l2, o1_a1_c.is_alias_for
+ refute_equal l1_l2, o1_a1_c
+
+ o1.update_aliases
+
+ o1_a1_m = @xref_data.find_class_or_module 'O1::A1'
+ refute_nil o1_a1_m
+ assert_equal l1_l2, o1_a1_m.is_alias_for
+
+ assert_equal 1, l1_l2.aliases.length
+ assert_equal o1_a1_m, l1_l2.aliases[0]
+
+ assert_equal 'L1::L2', l1_l2.full_name
+ assert_equal 'O1::A1', o1_a1_m.full_name
+ end
+
+ def test_update_includes
+ a = RDoc::Include.new 'M1', nil
+ b = RDoc::Include.new 'M2', nil
+ c = RDoc::Include.new 'C', nil
+
+ @c1.add_include a
+ @c1.add_include b
+ @c1.add_include c
+ @c1.ancestors # cache included modules
+
+ @m1_m2.document_self = nil
+ assert @m1_m2.remove_from_documentation?
+
+ assert RDoc::TopLevel.all_modules_hash.key? @m1_m2.full_name
+ refute RDoc::TopLevel.all_modules_hash[@m1_m2.full_name].nil?
+ RDoc::TopLevel.remove_nodoc RDoc::TopLevel.all_modules_hash
+ refute RDoc::TopLevel.all_modules_hash.key? @m1_m2.full_name
+
+ @c1.update_includes
+
+ assert_equal [a, c], @c1.includes
+ end
+
+ def test_update_includes_with_colons
+ a = RDoc::Include.new 'M1', nil
+ b = RDoc::Include.new 'M1::M2', nil
+ c = RDoc::Include.new 'C', nil
+
+ @c1.add_include a
+ @c1.add_include b
+ @c1.add_include c
+ @c1.ancestors # cache included modules
+
+ @m1_m2.document_self = nil
+ assert @m1_m2.remove_from_documentation?
+
+ assert RDoc::TopLevel.all_modules_hash.key? @m1_m2.full_name
+ refute RDoc::TopLevel.all_modules_hash[@m1_m2.full_name].nil?
+ RDoc::TopLevel.remove_nodoc RDoc::TopLevel.all_modules_hash
+ refute RDoc::TopLevel.all_modules_hash.key? @m1_m2.full_name
+
+ @c1.update_includes
+
+ assert_equal [a, c], @c1.includes
+ end
+
end
diff --git a/test/rdoc/test_rdoc_code_object.rb b/test/rdoc/test_rdoc_code_object.rb
index 907bb7a3d5..8ae2d8b91e 100644
--- a/test/rdoc/test_rdoc_code_object.rb
+++ b/test/rdoc/test_rdoc_code_object.rb
@@ -16,6 +16,7 @@ class TestRDocCodeObject < XrefTestCase
assert @co.document_children, 'document_children'
refute @co.force_documentation, 'force_documentation'
refute @co.done_documenting, 'done_documenting'
+ refute @co.received_nodoc, 'received_nodoc'
assert_equal '', @co.comment, 'comment is empty'
end
@@ -33,16 +34,20 @@ class TestRDocCodeObject < XrefTestCase
@co.document_children = false
refute @co.document_children
- @c2.document_children = false
- assert_empty @c2.classes
+ # TODO this is not true anymore:
+ # test all the nodoc stuff etc...
+ #@c2.document_children = false
+ #assert_empty @c2.classes
end
def test_document_self_equals
@co.document_self = false
refute @co.document_self
- @c1.document_self = false
- assert_empty @c1.method_list
+ # TODO this is not true anymore:
+ # test all the nodoc stuff etc...
+ #@c1.document_self = false
+ #assert_empty @c1.method_list
end
def test_documented_eh
@@ -56,11 +61,46 @@ class TestRDocCodeObject < XrefTestCase
refute @co.documented?
- @co.document_self = false
+ @co.document_self = nil # notify :nodoc:
assert @co.documented?
end
+ def test_done_documenting
+ # once done_documenting is set, other properties refuse to go to "true"
+ @co.done_documenting = true
+
+ @co.document_self = true
+ refute @co.document_self
+
+ @co.document_children = true
+ refute @co.document_children
+
+ @co.force_documentation = true
+ refute @co.force_documentation
+
+ @co.start_doc
+ refute @co.document_self
+ refute @co.document_children
+
+ # turning done_documenting on
+ # resets others to true
+
+ @co.done_documenting = false
+ assert @co.document_self
+ assert @co.document_children
+ end
+
+ def test_full_name_equals
+ @co.full_name = 'hi'
+
+ assert_equal 'hi', @co.instance_variable_get(:@full_name)
+
+ @co.full_name = nil
+
+ assert_nil @co.instance_variable_get(:@full_name)
+ end
+
def test_metadata
assert_empty @co.metadata
@@ -84,6 +124,23 @@ class TestRDocCodeObject < XrefTestCase
assert_equal 'C2', @c2_c3.parent_name
end
+ def test_received_ndoc
+ @co.document_self = false
+ refute @co.received_nodoc
+
+ @co.document_self = nil
+ assert @co.received_nodoc
+
+ @co.document_self = true
+ end
+
+ def test_record_location
+ c = RDoc::CodeObject.new
+ c.record_location @xref_data
+
+ assert_equal 'xref_data.rb', c.file.relative_name
+ end
+
def test_start_doc
@co.document_self = false
@co.document_children = false
diff --git a/test/rdoc/test_rdoc_context.rb b/test/rdoc/test_rdoc_context.rb
index cd0ad0fae3..6961f7d214 100644
--- a/test/rdoc/test_rdoc_context.rb
+++ b/test/rdoc/test_rdoc_context.rb
@@ -34,13 +34,38 @@ class TestRDocContext < XrefTestCase
@context.add_alias as
- assert_equal [as], @context.aliases
- assert_equal [as], @context.unmatched_alias_lists['old_name']
+ assert_equal [as], @context.external_aliases
+ assert_equal [as], @context.unmatched_alias_lists['#old_name']
+ end
+
+ def test_add_alias_method_attr
+ top_level = RDoc::TopLevel.new 'file.rb'
+
+ attr = RDoc::Attr.new nil, 'old_name', 'R', ''
+
+ as = RDoc::Alias.new nil, 'old_name', 'new_name', 'comment'
+ as.record_location top_level
+ as.parent = @context
+
+ @context.add_attribute attr
+ @context.add_alias as
+
+ assert_empty @context.aliases
+ assert_empty @context.unmatched_alias_lists
+ assert_equal %w[old_name new_name], @context.attributes.map { |m| m.name }
+
+ new = @context.attributes.last
+ assert_equal top_level, new.file
end
def test_add_alias_method
+ top_level = RDoc::TopLevel.new 'file.rb'
+
meth = RDoc::AnyMethod.new nil, 'old_name'
+ meth.singleton = false
+
as = RDoc::Alias.new nil, 'old_name', 'new_name', 'comment'
+ as.record_location top_level
as.parent = @context
@context.add_method meth
@@ -49,28 +74,28 @@ class TestRDocContext < XrefTestCase
assert_empty @context.aliases
assert_empty @context.unmatched_alias_lists
assert_equal %w[old_name new_name], @context.method_list.map { |m| m.name }
+
+ new = @context.method_list.last
+ assert_equal top_level, new.file
end
- def test_add_alias_impl
+ def test_add_alias_method_singleton
meth = RDoc::AnyMethod.new nil, 'old_name'
- meth.comment = 'old comment'
- meth.singleton = false
- meth.visibility = :private
+ meth.singleton = true
- alas = RDoc::Alias.new nil, 'old_name', 'new_name', 'new comment'
+ as = RDoc::Alias.new nil, 'old_name', 'new_name', 'comment'
+ as.singleton = true
- @context.add_alias_impl alas, meth
+ as.parent = @context
- assert_equal 1, @context.method_list.length
+ @context.add_method meth
+ @context.add_alias as
- alas_meth = @context.method_list.first
- assert_equal 'new_name', alas_meth.name
- assert_equal 'new comment', alas_meth.comment
- assert_equal false, alas_meth.singleton
- assert_equal meth, alas_meth.is_alias_for
- assert_equal :private, alas_meth.visibility
+ assert_empty @context.aliases
+ assert_empty @context.unmatched_alias_lists
+ assert_equal %w[old_name new_name], @context.method_list.map { |m| m.name }
- assert_equal [alas_meth], meth.aliases
+ assert @context.method_list.last.singleton
end
def test_add_class
@@ -133,11 +158,11 @@ class TestRDocContext < XrefTestCase
meth = RDoc::AnyMethod.new nil, 'old_name'
@context.add_alias as
- refute_empty @context.aliases
+ refute_empty @context.external_aliases
@context.add_method meth
- assert_empty @context.aliases
+ assert_empty @context.external_aliases
assert_empty @context.unmatched_alias_lists
assert_equal %w[old_name new_name], @context.method_list.map { |m| m.name }
end
@@ -292,6 +317,46 @@ class TestRDocContext < XrefTestCase
assert_equal @c1__m, @c1.find_symbol('::m')
end
+ def test_fully_documented_eh
+ context = RDoc::Context.new
+
+ refute context.fully_documented?
+
+ context.comment = 'hi'
+
+ assert context.fully_documented?
+
+ m = @c1_m
+
+ context.add_method m
+
+ refute context.fully_documented?
+
+ m.comment = 'hi'
+
+ assert context.fully_documented?
+
+ c = RDoc::Constant.new 'C', '0', nil
+
+ context.add_constant c
+
+ refute context.fully_documented?
+
+ c.comment = 'hi'
+
+ assert context.fully_documented?
+
+ a = RDoc::Attr.new '', 'a', 'RW', nil
+
+ context.add_attribute a
+
+ refute context.fully_documented?
+
+ a.comment = 'hi'
+
+ assert context.fully_documented?
+ end
+
def test_spaceship
assert_equal(-1, @c2.<=>(@c3))
assert_equal 0, @c2.<=>(@c2)
diff --git a/test/rdoc/test_rdoc_encoding.rb b/test/rdoc/test_rdoc_encoding.rb
new file mode 100644
index 0000000000..b940d93606
--- /dev/null
+++ b/test/rdoc/test_rdoc_encoding.rb
@@ -0,0 +1,145 @@
+require 'rubygems'
+require 'minitest/autorun'
+require 'rdoc'
+require 'rdoc/encoding'
+
+require 'tempfile'
+
+class TestRDocEncoding < MiniTest::Unit::TestCase
+
+ def setup
+ @tempfile = Tempfile.new 'test_rdoc_encoding'
+ end
+
+ def test_class_read_file
+ @tempfile.write "hi everybody"
+ @tempfile.flush
+
+ assert_equal "hi everybody", RDoc::Encoding.read_file(@tempfile.path, nil)
+ end
+
+ def test_class_read_file_encoding
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ expected = "# coding: utf-8\nhi everybody"
+
+ @tempfile.write expected
+ @tempfile.flush
+
+ # FIXME 1.9 fix on windoze
+ expected.gsub!("\n", "\r\n") if RUBY_VERSION =~ /^1.9/ && RUBY_PLATFORM =~ /mswin|mingw/
+
+ contents = RDoc::Encoding.read_file @tempfile.path, Encoding::UTF_8
+ assert_equal expected, contents
+ assert_equal Encoding::UTF_8, contents.encoding
+ end
+
+ def test_class_read_file_encoding_convert
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ content = ""
+ content.encode! 'ISO-8859-1'
+ content << "# coding: ISO-8859-1\nhi \xE9verybody"
+
+ @tempfile.write content
+ @tempfile.flush
+
+ contents = RDoc::Encoding.read_file @tempfile.path, Encoding::UTF_8
+ assert_equal Encoding::UTF_8, contents.encoding
+ assert_equal "# coding: ISO-8859-1\nhi \u00e9verybody", contents.sub("\r", '')
+ end
+
+ def test_class_read_file_encoding_fancy
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ expected = "# -*- coding: utf-8; fill-column: 74 -*-\nhi everybody"
+ expected.encode! Encoding::UTF_8
+
+ @tempfile.write expected
+ @tempfile.flush
+
+ # FIXME 1.9 fix on windoze
+ expected.gsub!("\n", "\r\n") if RUBY_VERSION =~ /^1.9/ && RUBY_PLATFORM =~ /win32|mingw32/
+
+ contents = RDoc::Encoding.read_file @tempfile.path, Encoding::UTF_8
+ assert_equal expected, contents
+ assert_equal Encoding::UTF_8, contents.encoding
+ end
+
+ def test_class_read_file_encoding_guess
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ path = File.expand_path '../test.ja.txt', __FILE__
+ content = RDoc::Encoding.read_file path, Encoding::UTF_8
+
+ assert_equal Encoding::UTF_8, content.encoding
+ end
+
+ def test_class_read_file_encoding_with_signature
+ skip "Encoding not implemented" unless defined? ::Encoding
+
+ @tempfile.write "\xEF\xBB\xBFhi everybody"
+ @tempfile.flush
+
+ bug3360 = '[ruby-dev:41452]'
+ content = RDoc::Encoding.read_file @tempfile.path, Encoding::UTF_8
+ assert_equal Encoding::UTF_8, content.encoding, bug3360
+ assert_equal "hi everybody", content, bug3360
+ end
+
+ def test_class_set_encoding
+ s = "# coding: UTF-8\n"
+ RDoc::Encoding.set_encoding s
+
+ # sanity check for 1.8
+
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ assert_equal Encoding::UTF_8, s.encoding
+
+ s = "#!/bin/ruby\n# coding: UTF-8\n"
+ RDoc::Encoding.set_encoding s
+
+ assert_equal Encoding::UTF_8, s.encoding
+
+ s = "<?xml version='1.0' encoding='UTF-8'?>\n"
+ expected = s.encoding
+ RDoc::Encoding.set_encoding s
+
+ assert_equal Encoding::UTF_8, s.encoding
+
+ s = "<?xml version='1.0' encoding=\"UTF-8\"?>\n"
+ expected = s.encoding
+ RDoc::Encoding.set_encoding s
+
+ assert_equal Encoding::UTF_8, s.encoding
+ end
+
+ def test_class_set_encoding_bad
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ s = ""
+ expected = s.encoding
+ RDoc::Encoding.set_encoding s
+
+ assert_equal expected, s.encoding
+
+ s = "# vim:set fileencoding=utf-8:\n"
+ expected = s.encoding
+ RDoc::Encoding.set_encoding s
+
+ assert_equal expected, s.encoding
+
+ s = "# vim:set fileencoding=utf-8:\n"
+ expected = s.encoding
+ RDoc::Encoding.set_encoding s
+
+ assert_equal expected, s.encoding
+
+ assert_raises ArgumentError do
+ RDoc::Encoding.set_encoding "# -*- encoding: undecided -*-\n"
+ end
+ end
+
+end
+
diff --git a/test/rdoc/test_rdoc_generator_darkfish.rb b/test/rdoc/test_rdoc_generator_darkfish.rb
new file mode 100644
index 0000000000..b99803bac1
--- /dev/null
+++ b/test/rdoc/test_rdoc_generator_darkfish.rb
@@ -0,0 +1,119 @@
+require 'minitest/autorun'
+require 'rdoc/rdoc'
+require 'rdoc/generator/darkfish'
+require 'tmpdir'
+require 'fileutils'
+
+class TestRDocGeneratorDarkfish < MiniTest::Unit::TestCase
+
+ def setup
+ @pwd = Dir.pwd
+ @lib_dir = "#{@pwd}/lib"
+ $LOAD_PATH.unshift @lib_dir # ensure we load from this RDoc
+ RDoc::TopLevel.reset
+
+ @options = RDoc::Options.new
+ @options.option_parser = OptionParser.new
+
+ @tmpdir = File.join Dir.tmpdir, "test_rdoc_generator_darkfish_#{$$}"
+ FileUtils.mkdir_p @tmpdir
+ Dir.chdir @tmpdir
+ @options.op_dir = @tmpdir
+ @options.generator = RDoc::Generator::Darkfish
+
+ $LOAD_PATH.each do |path|
+ darkfish_dir = File.join path, 'rdoc/generator/template/darkfish'
+ next unless File.directory? darkfish_dir
+ @options.template_dir = darkfish_dir
+ break
+ end
+
+ rd = RDoc::RDoc.new
+ rd.options = @options
+ RDoc::RDoc.current = rd
+
+ @g = @options.generator.new @options
+
+ rd.generator = @g
+
+ @top_level = RDoc::TopLevel.new 'file.rb'
+ @klass = @top_level.add_class RDoc::NormalClass, 'Object'
+ @meth = RDoc::AnyMethod.new nil, 'method'
+ @meth_bang = RDoc::AnyMethod.new nil, 'method!'
+ @attr = RDoc::Attr.new nil, 'attr', 'RW', ''
+
+ @klass.add_method @meth
+ @klass.add_method @meth_bang
+ @klass.add_attribute @attr
+ end
+
+ def teardown
+ $LOAD_PATH.shift
+ Dir.chdir @pwd
+ FileUtils.rm_rf @tmpdir
+ end
+
+ def assert_file path
+ assert File.file?(path), "#{path} is not a file"
+ end
+
+ def refute_file path
+ refute File.exist?(path), "#{path} exists"
+ end
+
+ def test_generate
+ top_level = RDoc::TopLevel.new 'file.rb'
+ top_level.add_class @klass.class, @klass.name
+
+ @g.generate [top_level]
+
+ assert_file 'index.html'
+ assert_file 'Object.html'
+ assert_file 'file_rb.html'
+
+ encoding = if Object.const_defined? :Encoding then
+ Regexp.escape Encoding.default_external.name
+ else
+ Regexp.escape 'UTF-8'
+ end
+
+ assert_match(/<meta content="text\/html; charset=#{encoding}"/,
+ File.read('index.html'))
+ assert_match(/<meta content="text\/html; charset=#{encoding}"/,
+ File.read('Object.html'))
+ assert_match(/<meta content="text\/html; charset=#{encoding}"/,
+ File.read('file_rb.html'))
+ end
+
+ def test_generate_dry_run
+ @options.dry_run = true
+ top_level = RDoc::TopLevel.new 'file.rb'
+ top_level.add_class @klass.class, @klass.name
+
+ @g.generate [top_level]
+
+ refute_file 'index.html'
+ refute_file 'Object.html'
+ refute_file 'file_rb.html'
+ end
+
+ def test_template_for
+ classpage = Pathname.new @options.template_dir + '/classpage.rhtml'
+
+ template = @g.send(:template_for, classpage)
+ assert_kind_of RDoc::ERBIO, template
+
+ assert_same template, @g.send(:template_for, classpage)
+ end
+
+ def test_template_for_dry_run
+ classpage = Pathname.new @options.template_dir + '/classpage.rhtml'
+
+ template = @g.send(:template_for, classpage)
+ assert_kind_of ERB, template
+
+ assert_same template, @g.send(:template_for, classpage)
+ end
+
+end
+
diff --git a/test/rdoc/test_rdoc_generator_ri.rb b/test/rdoc/test_rdoc_generator_ri.rb
index 7027080c9b..780d9cc570 100644
--- a/test/rdoc/test_rdoc_generator_ri.rb
+++ b/test/rdoc/test_rdoc_generator_ri.rb
@@ -1,21 +1,23 @@
require 'rubygems'
require 'minitest/autorun'
require 'rdoc/rdoc'
+require 'rdoc/generator/ri'
require 'tmpdir'
require 'fileutils'
class TestRDocGeneratorRI < MiniTest::Unit::TestCase
def setup
+ @options = RDoc::Options.new
+
@pwd = Dir.pwd
RDoc::TopLevel.reset
@tmpdir = File.join Dir.tmpdir, "test_rdoc_generator_ri_#{$$}"
FileUtils.mkdir_p @tmpdir
Dir.chdir @tmpdir
- options = RDoc::Options.new
- @g = RDoc::Generator::RI.new options
+ @g = RDoc::Generator::RI.new @options
@top_level = RDoc::TopLevel.new 'file.rb'
@klass = @top_level.add_class RDoc::NormalClass, 'Object'
@@ -37,6 +39,10 @@ class TestRDocGeneratorRI < MiniTest::Unit::TestCase
assert File.file?(path), "#{path} is not a file"
end
+ def refute_file path
+ refute File.exist?(path), "#{path} exists"
+ end
+
def test_generate
top_level = RDoc::TopLevel.new 'file.rb'
top_level.add_class @klass.class, @klass.name
@@ -52,5 +58,19 @@ class TestRDocGeneratorRI < MiniTest::Unit::TestCase
assert_file File.join(@tmpdir, 'Object', 'method%21-i.ri')
end
+ def test_generate_dry_run
+ @options.dry_run = true
+ @g = RDoc::Generator::RI.new @options
+
+ top_level = RDoc::TopLevel.new 'file.rb'
+ top_level.add_class @klass.class, @klass.name
+
+ @g.generate nil
+
+ refute_file File.join(@tmpdir, 'cache.ri')
+
+ refute_file File.join(@tmpdir, 'Object')
+ end
+
end
diff --git a/test/rdoc/test_rdoc_include.rb b/test/rdoc/test_rdoc_include.rb
index e4a44b4358..71eaf9abf8 100644
--- a/test/rdoc/test_rdoc_include.rb
+++ b/test/rdoc/test_rdoc_include.rb
@@ -6,6 +6,7 @@ class TestRDocInclude < XrefTestCase
super
@inc = RDoc::Include.new 'M1', 'comment'
+ @inc.parent = @m1
end
def test_module
@@ -13,5 +14,83 @@ class TestRDocInclude < XrefTestCase
assert_equal 'Unknown', RDoc::Include.new('Unknown', 'comment').module
end
+ def test_module_extended
+ m1 = @xref_data.add_module RDoc::NormalModule, 'Mod1'
+ m1_m3 = m1.add_module RDoc::NormalModule, 'Mod3'
+ m1_m2 = m1.add_module RDoc::NormalModule, 'Mod2'
+ m1_m2_m3 = m1_m2.add_module RDoc::NormalModule, 'Mod3'
+ m1_m2_m3_m4 = m1_m2_m3.add_module RDoc::NormalModule, 'Mod4'
+ m1_m2_m4 = m1_m2.add_module RDoc::NormalModule, 'Mod4'
+ m1_m2_k0 = m1_m2.add_class RDoc::NormalClass, 'Klass0'
+ m1_m2_k0_m4 = m1_m2_k0.add_module RDoc::NormalModule, 'Mod4'
+ #m1_m2_k0_m4_m5 = m1_m2_k0_m4.add_module RDoc::NormalModule, 'Mod5'
+ m1_m2_k0_m4_m6 = m1_m2_k0_m4.add_module RDoc::NormalModule, 'Mod6'
+ m1_m2_k0_m5 = m1_m2_k0.add_module RDoc::NormalModule, 'Mod5'
+
+ i0_m4 = RDoc::Include.new 'Mod4', nil
+ i0_m5 = RDoc::Include.new 'Mod5', nil
+ i0_m6 = RDoc::Include.new 'Mod6', nil
+ i0_m1 = RDoc::Include.new 'Mod1', nil
+ i0_m2 = RDoc::Include.new 'Mod2', nil
+ i0_m3 = RDoc::Include.new 'Mod3', nil
+
+ m1_m2_k0.add_include i0_m4
+ m1_m2_k0.add_include i0_m5
+ m1_m2_k0.add_include i0_m6
+ m1_m2_k0.add_include i0_m1
+ m1_m2_k0.add_include i0_m2
+ m1_m2_k0.add_include i0_m3
+
+ assert_equal [i0_m4, i0_m5, i0_m6, i0_m1, i0_m2, i0_m3], m1_m2_k0.includes
+ assert_equal [m1_m2_m3, m1_m2, m1, m1_m2_k0_m4_m6, m1_m2_k0_m5,
+ m1_m2_k0_m4, 'Object'], m1_m2_k0.ancestors
+
+ m1_k1 = m1.add_class RDoc::NormalClass, 'Klass1'
+
+ i1_m1 = RDoc::Include.new 'Mod1', nil
+ i1_m2 = RDoc::Include.new 'Mod2', nil
+ i1_m3 = RDoc::Include.new 'Mod3', nil
+ i1_m4 = RDoc::Include.new 'Mod4', nil
+ i1_k0_m4 = RDoc::Include.new 'Klass0::Mod4', nil
+
+ m1_k1.add_include i1_m1
+ m1_k1.add_include i1_m2
+ m1_k1.add_include i1_m3
+ m1_k1.add_include i1_m4
+ m1_k1.add_include i1_k0_m4
+
+ assert_equal [i1_m1, i1_m2, i1_m3, i1_m4, i1_k0_m4], m1_k1.includes
+ assert_equal [m1_m2_k0_m4, m1_m2_m3_m4, m1_m2_m3, m1_m2, m1, 'Object'],
+ m1_k1.ancestors
+
+ m1_k2 = m1.add_class RDoc::NormalClass, 'Klass2'
+
+ i2_m1 = RDoc::Include.new 'Mod1', nil
+ i2_m2 = RDoc::Include.new 'Mod2', nil
+ i2_m3 = RDoc::Include.new 'Mod3', nil
+ i2_k0_m4 = RDoc::Include.new 'Klass0::Mod4', nil
+
+ m1_k2.add_include i2_m1
+ m1_k2.add_include i2_m3
+ m1_k2.add_include i2_m2
+ m1_k2.add_include i2_k0_m4
+
+ assert_equal [i2_m1, i2_m3, i2_m2, i2_k0_m4], m1_k2.includes
+ assert_equal [m1_m2_k0_m4, m1_m2, m1_m3, m1, 'Object'], m1_k2.ancestors
+
+ m1_k3 = m1.add_class RDoc::NormalClass, 'Klass3'
+
+ i3_m1 = RDoc::Include.new 'Mod1', nil
+ i3_m2 = RDoc::Include.new 'Mod2', nil
+ i3_m4 = RDoc::Include.new 'Mod4', nil
+
+ m1_k3.add_include i3_m1
+ m1_k3.add_include i3_m2
+ m1_k3.add_include i3_m4
+
+ assert_equal [i3_m1, i3_m2, i3_m4], m1_k3.includes
+ assert_equal [m1_m2_m4, m1_m2, m1, 'Object'], m1_k3.ancestors
+ end
+
end
diff --git a/test/rdoc/test_rdoc_markup_attribute_manager.rb b/test/rdoc/test_rdoc_markup_attribute_manager.rb
index f6b1b6cf17..b65457fc7e 100644
--- a/test/rdoc/test_rdoc_markup_attribute_manager.rb
+++ b/test/rdoc/test_rdoc_markup_attribute_manager.rb
@@ -1,9 +1,9 @@
-require "rubygems"
-require "minitest/autorun"
+require 'rubygems'
+require 'minitest/autorun'
require 'rdoc'
require 'rdoc/markup'
-require "rdoc/markup/inline"
-require "rdoc/markup/to_html_crossref"
+require 'rdoc/markup/inline'
+require 'rdoc/markup/to_html_crossref'
class TestRDocMarkupAttributeManager < MiniTest::Unit::TestCase
diff --git a/test/rdoc/test_rdoc_markup_parser.rb b/test/rdoc/test_rdoc_markup_parser.rb
index d418900116..a7951d9d01 100644
--- a/test/rdoc/test_rdoc_markup_parser.rb
+++ b/test/rdoc/test_rdoc_markup_parser.rb
@@ -1,8 +1,9 @@
+# coding: utf-8
+
require 'pp'
require 'rubygems'
require 'minitest/autorun'
require 'rdoc/markup'
-require 'rdoc/markup/to_test'
class TestRDocMarkupParser < MiniTest::Unit::TestCase
@@ -53,6 +54,19 @@ class TestRDocMarkupParser < MiniTest::Unit::TestCase
assert_equal expected, @RMP.parse(str).parts
end
+ def test_parse_bullet_utf_8
+ str = <<-STR
+* 新しい機能
+ STR
+
+ expected = [
+ @RM::List.new(:BULLET, *[
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('新しい機能'))])]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
def test_parse_bullet_verbatim_heading
str = <<-STR
* l1
@@ -65,7 +79,7 @@ class TestRDocMarkupParser < MiniTest::Unit::TestCase
@RM::List.new(:BULLET, *[
@RM::ListItem.new(nil,
@RM::Paragraph.new('l1'),
- @RM::Verbatim.new(' ', 'v', "\n"))]),
+ @RM::Verbatim.new("v\n"))]),
@RM::Heading.new(1, 'H')]
assert_equal expected, @RMP.parse(str).parts
@@ -183,8 +197,7 @@ the time
@RM::List.new(:BULLET, *[
@RM::ListItem.new(nil,
@RM::Paragraph.new('l1.1', 'text'),
- @RM::Verbatim.new(' ', 'code', "\n",
- ' ', 'code', "\n"),
+ @RM::Verbatim.new("code\n", " code\n"),
@RM::Paragraph.new('text'))])),
@RM::ListItem.new(nil,
@RM::Paragraph.new('l2'))])]
@@ -400,6 +413,68 @@ A. l4
assert_equal expected, @RMP.parse(str).parts
end
+ def test_parse_lalpha_utf_8
+ str = <<-STR
+a. 新しい機能
+ STR
+
+ expected = [
+ @RM::List.new(:LALPHA, *[
+ @RM::ListItem.new(nil,
+ @RM::Paragraph.new('新しい機能'))])]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_list_list_1
+ str = <<-STR
+10. para 1
+
+ [label 1]
+ para 1.1
+
+ code
+
+ para 1.2
+ STR
+
+ expected = [
+ @RM::List.new(:NUMBER, *[
+ @RM::ListItem.new(nil, *[
+ @RM::Paragraph.new('para 1'),
+ @RM::BlankLine.new,
+ @RM::List.new(:LABEL, *[
+ @RM::ListItem.new('label 1', *[
+ @RM::Paragraph.new('para 1.1'),
+ @RM::BlankLine.new,
+ @RM::Verbatim.new("code\n"),
+ @RM::Paragraph.new('para 1.2')])])])])]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_list_list_2
+ str = <<-STR
+6. para
+
+ label 1:: text 1
+ label 2:: text 2
+ STR
+
+ expected = [
+ @RM::List.new(:NUMBER, *[
+ @RM::ListItem.new(nil, *[
+ @RM::Paragraph.new('para'),
+ @RM::BlankLine.new,
+ @RM::List.new(:NOTE, *[
+ @RM::ListItem.new('label 1',
+ @RM::Paragraph.new('text 1')),
+ @RM::ListItem.new('label 2',
+ @RM::Paragraph.new('text 2'))])])])]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
def test_parse_list_verbatim
str = <<-STR
* one
@@ -412,8 +487,7 @@ A. l4
@RM::List.new(:BULLET, *[
@RM::ListItem.new(nil,
@RM::Paragraph.new('one'),
- @RM::Verbatim.new(' ', 'verb1', "\n",
- ' ', 'verb2', "\n")),
+ @RM::Verbatim.new("verb1\n", "verb2\n")),
@RM::ListItem.new(nil,
@RM::Paragraph.new('two'))])]
@@ -545,7 +619,7 @@ for all good men
expected = [
@RM::Paragraph.new('now is the time'),
- @RM::Verbatim.new(' ', 'code _line_ here', "\n"),
+ @RM::Verbatim.new("code _line_ here\n"),
@RM::Paragraph.new('for all good men'),
]
assert_equal expected, @RMP.parse(str).parts
@@ -567,6 +641,12 @@ B. l2
assert_equal expected, @RMP.parse(str).parts
end
+ def test_parse_trailing_cr
+ expected = [ @RM::Paragraph.new('Text') ]
+ # FIXME hangs the parser:
+ assert_equal expected, @RMP.parse("Text\r").parts
+ end
+
def test_parse_verbatim
str = <<-STR
now is
@@ -576,7 +656,7 @@ the time
expected = [
@RM::Paragraph.new('now is'),
- @RM::Verbatim.new(' ', 'code', "\n"),
+ @RM::Verbatim.new("code\n"),
@RM::Paragraph.new('the time'),
]
@@ -589,7 +669,18 @@ the time
STR
expected = [
- @RM::Verbatim.new(' ', '*', ' ', 'blah', "\n")]
+ @RM::Verbatim.new("* blah\n")]
+
+ assert_equal expected, @RMP.parse(str).parts
+ end
+
+ def test_parse_verbatim_dash
+ str = <<-STR
+ - blah
+ STR
+
+ expected = [
+ @RM::Verbatim.new("- blah\n")]
assert_equal expected, @RMP.parse(str).parts
end
@@ -607,9 +698,7 @@ the time
expected = [
@RM::Paragraph.new('now is'),
- @RM::Verbatim.new(' ', 'code', "\n",
- "\n",
- ' ', 'code1', "\n"),
+ @RM::Verbatim.new("code\n", "\n", "code1\n"),
@RM::Paragraph.new('the time'),
]
@@ -624,7 +713,7 @@ text
expected = [
@RM::Paragraph.new('text'),
- @RM::Verbatim.new(' ', '===', ' ', 'heading three', "\n")]
+ @RM::Verbatim.new("=== heading three\n")]
assert_equal expected, @RMP.parse(str).parts
end
@@ -634,7 +723,7 @@ text
expected = [
@RM::Paragraph.new('text'),
- @RM::Verbatim.new(' ', 'code', "\n"),
+ @RM::Verbatim.new("code\n"),
@RM::Heading.new(3, 'heading three')]
assert_equal expected, @RMP.parse(str).parts
@@ -646,7 +735,7 @@ text
STR
expected = [
- @RM::Verbatim.new(' ', '[blah]', ' ', 'blah', "\n")]
+ @RM::Verbatim.new("[blah] blah\n")]
assert_equal expected, @RMP.parse(str).parts
end
@@ -657,7 +746,7 @@ text
STR
expected = [
- @RM::Verbatim.new(' ', 'b.', ' ', 'blah', "\n")]
+ @RM::Verbatim.new("b. blah\n")]
assert_equal expected, @RMP.parse(str).parts
end
@@ -671,8 +760,7 @@ text
expected = [
@RM::Paragraph.new('text'),
- @RM::Verbatim.new(' ', 'code', "\n",
- ' ', '===', ' ', 'heading three', "\n")]
+ @RM::Verbatim.new("code\n", "=== heading three\n")]
assert_equal expected, @RMP.parse(str).parts
end
@@ -688,9 +776,7 @@ the time
expected = [
@RM::Paragraph.new('now is'),
- @RM::Verbatim.new(' ', 'code', "\n",
- "\n",
- ' ', 'code1', "\n"),
+ @RM::Verbatim.new("code\n", "\n", "code1\n"),
@RM::Paragraph.new('the time'),
]
@@ -710,11 +796,7 @@ the time
expected = [
@RM::Paragraph.new('now is'),
- @RM::Verbatim.new(' ', 'code', "\n",
- "\n",
- ' ', 'code1', "\n",
- "\n",
- ' ', 'code2', "\n"),
+ @RM::Verbatim.new("code\n", "\n", "code1\n", "\n", "code2\n"),
@RM::Paragraph.new('the time'),
]
@@ -731,8 +813,7 @@ the time
expected = [
@RM::Paragraph.new('now is'),
- @RM::Verbatim.new(' ', 'code', "\n",
- ' ', 'code1', "\n"),
+ @RM::Verbatim.new("code\n", "code1\n"),
@RM::Paragraph.new('the time'),
]
@@ -749,8 +830,8 @@ for all good men
expected = [
@RM::Paragraph.new('now is the time'),
- @RM::Verbatim.new(' ', 'code', "\n",
- ' ', 'more code', "\n"),
+ @RM::Verbatim.new(" code\n",
+ "more code\n"),
@RM::Paragraph.new('for all good men'),
]
@@ -763,7 +844,7 @@ for all good men
STR
expected = [
- @RM::Verbatim.new(' ', 'blah::', ' ', 'blah', "\n")]
+ @RM::Verbatim.new("blah:: blah\n")]
assert_equal expected, @RMP.parse(str).parts
end
@@ -774,7 +855,7 @@ for all good men
STR
expected = [
- @RM::Verbatim.new(' ', '2.', ' ', 'blah', "\n")]
+ @RM::Verbatim.new("2. blah\n")]
assert_equal expected, @RMP.parse(str).parts
end
@@ -790,8 +871,8 @@ text
expected = [
@RM::Paragraph.new('text'),
@RM::BlankLine.new,
- @RM::Verbatim.new(' ', '---', ' ', 'lib/blah.rb.orig', "\n",
- ' ', '+++', ' ', 'lib/blah.rb', "\n")]
+ @RM::Verbatim.new("--- lib/blah.rb.orig\n",
+ "+++ lib/blah.rb\n")]
assert_equal expected, @RMP.parse(str).parts
end
@@ -806,7 +887,7 @@ text
expected = [
@RM::Paragraph.new('text'),
@RM::BlankLine.new,
- @RM::Verbatim.new(' ', '---', '')]
+ @RM::Verbatim.new("---")]
assert_equal expected, @RMP.parse(str).parts
end
@@ -823,9 +904,9 @@ the time
expected = [
@RM::Paragraph.new('now is'),
- @RM::Verbatim.new(' ', 'code', "\n",
+ @RM::Verbatim.new("code\n",
"\n",
- ' ', 'code1', "\n"),
+ "code1\n"),
@RM::Paragraph.new('the time'),
]
@@ -838,7 +919,7 @@ the time
STR
expected = [
- @RM::Verbatim.new(' ', 'B.', ' ', 'blah', "\n")]
+ @RM::Verbatim.new("B. blah\n")]
assert_equal expected, @RMP.parse(str).parts
end
@@ -851,58 +932,57 @@ the time
assert_equal expected, @RMP.parse('hello').parts
expected = [
- @RM::Verbatim.new(' ', 'hello '),
+ @RM::Verbatim.new('hello '),
]
- assert_equal expected, @RMP.parse(' hello ').parts
+ assert_equal expected, @RMP.parse(' hello ').parts
expected = [
- @RM::Verbatim.new(' ', 'hello '),
+ @RM::Verbatim.new('hello '),
]
- assert_equal expected, @RMP.parse(" hello ").parts
+ assert_equal expected, @RMP.parse(' hello ').parts
expected = [
@RM::Paragraph.new('1'),
- @RM::Verbatim.new(' ', '2', "\n",
- ' ', '3'),
+ @RM::Verbatim.new("2\n", ' 3'),
]
assert_equal expected, @RMP.parse("1\n 2\n 3").parts
expected = [
- @RM::Verbatim.new(' ', '1', "\n",
- ' ', '2', "\n",
- ' ', '3'),
+ @RM::Verbatim.new("1\n",
+ " 2\n",
+ " 3"),
]
assert_equal expected, @RMP.parse(" 1\n 2\n 3").parts
expected = [
@RM::Paragraph.new('1'),
- @RM::Verbatim.new(' ', '2', "\n",
- ' ', '3', "\n"),
+ @RM::Verbatim.new("2\n",
+ " 3\n"),
@RM::Paragraph.new('1'),
- @RM::Verbatim.new(' ', '2'),
+ @RM::Verbatim.new('2'),
]
assert_equal expected, @RMP.parse("1\n 2\n 3\n1\n 2").parts
expected = [
- @RM::Verbatim.new(' ', '1', "\n",
- ' ', '2', "\n",
- ' ', '3', "\n",
- ' ', '1', "\n",
- ' ', '2'),
+ @RM::Verbatim.new("1\n",
+ " 2\n",
+ " 3\n",
+ "1\n",
+ ' 2'),
]
assert_equal expected, @RMP.parse(" 1\n 2\n 3\n 1\n 2").parts
expected = [
- @RM::Verbatim.new(' ', '1', "\n",
- ' ', '2', "\n",
+ @RM::Verbatim.new("1\n",
+ " 2\n",
"\n",
- ' ', '3'),
+ ' 3'),
]
assert_equal expected, @RMP.parse(" 1\n 2\n\n 3").parts
@@ -942,8 +1022,7 @@ the time
STR
expected = [
- [:BULLET, :BULLET, 0, 0],
- [:SPACE, 2, 0, 0],
+ [:BULLET, '*', 0, 0],
[:TEXT, 'l1', 2, 0],
[:NEWLINE, "\n", 4, 0],
]
@@ -958,13 +1037,10 @@ the time
STR
expected = [
- [:BULLET, :BULLET, 0, 0],
- [:SPACE, 2, 0, 0],
+ [:BULLET, '*', 0, 0],
[:TEXT, 'l1', 2, 0],
[:NEWLINE, "\n", 4, 0],
- [:INDENT, 2, 0, 1],
- [:BULLET, :BULLET, 2, 1],
- [:SPACE, 2, 2, 1],
+ [:BULLET, '*', 2, 1],
[:TEXT, 'l1.1', 4, 1],
[:NEWLINE, "\n", 8, 1],
]
@@ -1030,11 +1106,9 @@ the time
expected = [
[:LABEL, 'cat', 0, 0],
- [:SPACE, 6, 0, 0],
[:TEXT, 'l1', 6, 0],
[:NEWLINE, "\n", 8, 0],
[:LABEL, 'dog', 0, 1],
- [:SPACE, 6, 0, 1],
[:TEXT, 'l1.1', 6, 1],
[:NEWLINE, "\n", 10, 1],
]
@@ -1050,11 +1124,8 @@ the time
expected = [
[:LABEL, 'label', 0, 0],
- [:SPACE, 7, 0, 0],
[:NEWLINE, "\n", 7, 0],
- [:INDENT, 2, 0, 1],
[:NOTE, 'note', 2, 1],
- [:SPACE, 6, 2, 1],
[:NEWLINE, "\n", 8, 1],
]
@@ -1069,11 +1140,9 @@ b. l1.1
expected = [
[:LALPHA, 'a', 0, 0],
- [:SPACE, 3, 0, 0],
[:TEXT, 'l1', 3, 0],
[:NEWLINE, "\n", 5, 0],
[:LALPHA, 'b', 0, 1],
- [:SPACE, 3, 0, 1],
[:TEXT, 'l1.1', 3, 1],
[:NEWLINE, "\n", 7, 1],
]
@@ -1089,11 +1158,9 @@ dog:: l1.1
expected = [
[:NOTE, 'cat', 0, 0],
- [:SPACE, 6, 0, 0],
[:TEXT, 'l1', 6, 0],
[:NEWLINE, "\n", 8, 0],
[:NOTE, 'dog', 0, 1],
- [:SPACE, 6, 0, 1],
[:TEXT, 'l1.1', 6, 1],
[:NEWLINE, "\n", 10, 1],
]
@@ -1109,10 +1176,8 @@ dog::
expected = [
[:NOTE, 'cat', 0, 0],
- [:SPACE, 5, 0, 0],
[:NEWLINE, "\n", 5, 0],
[:NOTE, 'dog', 0, 1],
- [:SPACE, 5, 0, 1],
[:NEWLINE, "\n", 5, 1],
]
@@ -1140,11 +1205,9 @@ Cat::Dog
expected = [
[:NUMBER, '1', 0, 0],
- [:SPACE, 3, 0, 0],
[:TEXT, 'l1', 3, 0],
[:NEWLINE, "\n", 5, 0],
[:NUMBER, '2', 0, 1],
- [:SPACE, 3, 0, 1],
[:TEXT, 'l1.1', 3, 1],
[:NEWLINE, "\n", 7, 1],
]
@@ -1162,20 +1225,16 @@ Cat::Dog
expected = [
[:NUMBER, "1", 0, 0],
- [:SPACE, 3, 0, 0],
[:TEXT, "blah blah blah", 3, 0],
[:NEWLINE, "\n", 17, 0],
- [:INDENT, 3, 0, 1],
[:TEXT, "l.", 3, 1],
[:NEWLINE, "\n", 5, 1],
[:NUMBER, "2", 0, 2],
- [:SPACE, 3, 0, 2],
[:TEXT, "blah blah blah blah", 3, 2],
[:NEWLINE, "\n", 22, 2],
- [:INDENT, 3, 0, 3],
[:TEXT, "d.", 3, 3],
[:NEWLINE, "\n", 5, 3]
]
@@ -1193,24 +1252,18 @@ Cat::Dog
expected = [
[:NUMBER, "1", 0, 0],
- [:SPACE, 3, 0, 0],
[:TEXT, "blah blah blah", 3, 0],
[:NEWLINE, "\n", 17, 0],
- [:INDENT, 3, 0, 1],
[:LALPHA, "l", 3, 1],
- [:SPACE, 4, 3, 1],
[:TEXT, "more stuff", 7, 1],
[:NEWLINE, "\n", 17, 1],
[:NUMBER, "2", 0, 2],
- [:SPACE, 3, 0, 2],
[:TEXT, "blah blah blah blah", 3, 2],
[:NEWLINE, "\n", 22, 2],
- [:INDENT, 3, 0, 3],
[:LALPHA, "d", 3, 3],
- [:SPACE, 3, 3, 3],
[:TEXT, "other stuff", 6, 3],
[:NEWLINE, "\n", 17, 3]
]
@@ -1241,14 +1294,14 @@ for all
def test_tokenize_rule
str = <<-STR
----
+---
--- blah ---
STR
expected = [
[:RULE, 1, 0, 0],
- [:NEWLINE, "\n", 4, 0],
+ [:NEWLINE, "\n", 3, 0],
[:NEWLINE, "\n", 0, 1],
[:TEXT, "--- blah ---", 0, 2],
[:NEWLINE, "\n", 12, 2],
@@ -1265,11 +1318,9 @@ B. l1.1
expected = [
[:UALPHA, 'A', 0, 0],
- [:SPACE, 3, 0, 0],
[:TEXT, 'l1', 3, 0],
[:NEWLINE, "\n", 5, 0],
[:UALPHA, 'B', 0, 1],
- [:SPACE, 3, 0, 1],
[:TEXT, 'l1.1', 3, 1],
[:NEWLINE, "\n", 7, 1],
]
@@ -1288,7 +1339,6 @@ Example heading:
[:TEXT, 'Example heading:', 0, 0],
[:NEWLINE, "\n", 16, 0],
[:NEWLINE, "\n", 0, 1],
- [:INDENT, 3, 0, 2],
[:HEADER, 3, 3, 2],
[:TEXT, 'heading three', 7, 2],
[:NEWLINE, "\n", 20, 2],
@@ -1299,17 +1349,17 @@ Example heading:
# HACK move to Verbatim test case
def test_verbatim_normalize
- v = @RM::Verbatim.new ' ', 'foo', "\n", "\n", "\n", ' ', 'bar', "\n"
+ v = @RM::Verbatim.new "foo\n", "\n", "\n", "bar\n"
v.normalize
- assert_equal [' ', 'foo', "\n", "\n", ' ', 'bar', "\n"], v.parts
+ assert_equal ["foo\n", "\n", "bar\n"], v.parts
- v = @RM::Verbatim.new ' ', 'foo', "\n", "\n"
+ v = @RM::Verbatim.new "foo\n", "\n"
v.normalize
- assert_equal [' ', 'foo', "\n"], v.parts
+ assert_equal ["foo\n"], v.parts
end
def test_unget
diff --git a/test/rdoc/test_rdoc_markup_pre_process.rb b/test/rdoc/test_rdoc_markup_pre_process.rb
index cae4da9e05..b4db512ce3 100644
--- a/test/rdoc/test_rdoc_markup_pre_process.rb
+++ b/test/rdoc/test_rdoc_markup_pre_process.rb
@@ -1,7 +1,7 @@
require 'tempfile'
require 'rubygems'
require 'minitest/autorun'
-require 'rdoc/markup/preprocess'
+require 'rdoc/markup/pre_process'
require 'rdoc/code_objects'
class TestRDocMarkupPreProcess < MiniTest::Unit::TestCase
@@ -10,8 +10,7 @@ class TestRDocMarkupPreProcess < MiniTest::Unit::TestCase
RDoc::Markup::PreProcess.registered.clear
@tempfile = Tempfile.new 'test_rdoc_markup_pre_process'
- @tempfile.binmode
- @name = File.basename @tempfile.path
+ @file_name = File.basename @tempfile.path
@dir = File.dirname @tempfile.path
@pp = RDoc::Markup::PreProcess.new __FILE__, [@dir]
@@ -32,13 +31,18 @@ contents of a string.
@tempfile.flush
@tempfile.rewind
- content = @pp.include_file @name, ''
+ content = @pp.include_file @file_name, '', nil
expected = <<-EXPECTED
Regular expressions (<i>regexp</i>s) are patterns which describe the
contents of a string.
EXPECTED
+ # FIXME 1.9 fix on windoze
+ # preprocessor uses binread, so line endings are \r\n
+ expected.gsub!("\n", "\r\n") if
+ RUBY_VERSION =~ /^1.9/ && RUBY_PLATFORM =~ /mswin|mingw/
+
assert_equal expected, content
end
diff --git a/test/rdoc/test_rdoc_markup_to_ansi.rb b/test/rdoc/test_rdoc_markup_to_ansi.rb
index a8fab98d19..1334ac71c4 100644
--- a/test/rdoc/test_rdoc_markup_to_ansi.rb
+++ b/test/rdoc/test_rdoc_markup_to_ansi.rb
@@ -1,11 +1,12 @@
require 'rubygems'
-require 'rdoc/markup/formatter_test_case'
+require 'rdoc/markup/text_formatter_test_case'
require 'rdoc/markup/to_ansi'
require 'minitest/autorun'
-class TestRDocMarkupToAnsi < RDoc::Markup::FormatterTestCase
+class TestRDocMarkupToAnsi < RDoc::Markup::TextFormatterTestCase
add_visitor_tests
+ add_text_tests
def setup
super
@@ -62,7 +63,7 @@ class TestRDocMarkupToAnsi < RDoc::Markup::FormatterTestCase
end
def accept_list_item_end_label
- assert_equal "\e[0m\n", @to.res.join
+ assert_equal "\e[0m", @to.res.join
assert_equal 0, @to.indent, 'indent'
end
@@ -72,7 +73,7 @@ class TestRDocMarkupToAnsi < RDoc::Markup::FormatterTestCase
end
def accept_list_item_end_note
- assert_equal "\e[0m\n", @to.res.join
+ assert_equal "\e[0m", @to.res.join
assert_equal 0, @to.indent, 'indent'
end
@@ -191,8 +192,8 @@ class TestRDocMarkupToAnsi < RDoc::Markup::FormatterTestCase
assert_equal "\e[0m#{'-' * 78}\n", @to.res.join
end
- def accept_verbatim # FormatterTestCase doesn't set indent for ToAnsi
- assert_equal "\e[0m hi\n world\n\n", @to.res.join
+ def accept_verbatim
+ assert_equal "\e[0m hi\n world\n\n", @to.res.join
end
def end_accepting
@@ -207,214 +208,103 @@ class TestRDocMarkupToAnsi < RDoc::Markup::FormatterTestCase
assert_empty @to.list_width
end
- def test_accept_heading_1
- @to.start_accepting
- @to.accept_heading @RM::Heading.new(1, 'Hello')
-
+ def accept_heading_1
assert_equal "\e[0m\e[1;32mHello\e[m\n", @to.end_accepting
end
- def test_accept_heading_2
- @to.start_accepting
- @to.accept_heading @RM::Heading.new(2, 'Hello')
-
+ def accept_heading_2
assert_equal "\e[0m\e[4;32mHello\e[m\n", @to.end_accepting
end
- def test_accept_heading_3
- @to.start_accepting
- @to.accept_heading @RM::Heading.new(3, 'Hello')
-
+ def accept_heading_3
assert_equal "\e[0m\e[32mHello\e[m\n", @to.end_accepting
end
- def test_accept_heading_4
- @to.start_accepting
- @to.accept_heading @RM::Heading.new(4, 'Hello')
-
+ def accept_heading_4
assert_equal "\e[0mHello\n", @to.end_accepting
end
- def test_accept_heading_indent
- @to.start_accepting
- @to.indent = 3
- @to.accept_heading @RM::Heading.new(1, 'Hello')
-
+ def accept_heading_indent
assert_equal "\e[0m \e[1;32mHello\e[m\n", @to.end_accepting
end
- def test_accept_heading_b
- @to.start_accepting
- @to.indent = 3
- @to.accept_heading @RM::Heading.new(1, '*Hello*')
-
- assert_equal "\e[0m \e[1;32m\e[1mHello\e[m\e[m\n", @to.end_accepting
+ def accept_heading_b
+ assert_equal "\e[0m\e[1;32m\e[1mHello\e[m\e[m\n", @to.end_accepting
end
- def test_accept_list_item_start_note_2
- list = @RM::List.new(:NOTE,
- @RM::ListItem.new('<tt>teletype</tt>',
- @RM::Paragraph.new('teletype description')))
-
- @to.start_accepting
-
- list.accept @to
-
- expected = "\e[0m\e[7mteletype\e[m:\n teletype description\n\n"
-
- assert_equal expected, @to.end_accepting
+ def accept_heading_suppressed_crossref
+ assert_equal "\e[0m\e[1;32mHello\e[m\n", @to.end_accepting
end
- def test_accept_paragraph_b
- @to.start_accepting
- @to.accept_paragraph @RM::Paragraph.new('reg <b>bold words</b> reg')
-
- expected = "\e[0mreg \e[1mbold words\e[m reg\n"
-
- assert_equal expected, @to.end_accepting
+ def accept_list_item_start_note_2
+ assert_equal "\e[0m\e[7mteletype\e[m:\n teletype description\n\n",
+ @to.res.join
end
- def test_accept_paragraph_i
- @to.start_accepting
- @to.accept_paragraph @RM::Paragraph.new('reg <em>italic words</em> reg')
-
- expected = "\e[0mreg \e[4mitalic words\e[m reg\n"
-
- assert_equal expected, @to.end_accepting
+ def accept_paragraph_b
+ assert_equal "\e[0mreg \e[1mbold words\e[m reg\n", @to.end_accepting
end
- def test_accept_paragraph_indent
- @to.start_accepting
- @to.indent = 3
- @to.accept_paragraph @RM::Paragraph.new('words ' * 30)
+ def accept_paragraph_i
+ assert_equal "\e[0mreg \e[4mitalic words\e[m reg\n", @to.end_accepting
+ end
+ def accept_paragraph_indent
expected = <<-EXPECTED
\e[0m words words words words words words words words words words words words
words words words words words words words words words words words words
- words words words words words words
+ words words words words words words
EXPECTED
assert_equal expected, @to.end_accepting
end
- def test_accept_paragraph_plus
- @to.start_accepting
- @to.accept_paragraph @RM::Paragraph.new('regular +teletype+ regular')
-
- expected = "\e[0mregular \e[7mteletype\e[m regular\n"
-
- assert_equal expected, @to.end_accepting
+ def accept_paragraph_plus
+ assert_equal "\e[0mreg \e[7mteletype\e[m reg\n", @to.end_accepting
end
- def test_accept_paragraph_star
- @to.start_accepting
- @to.accept_paragraph @RM::Paragraph.new('regular *bold* regular')
-
- expected = "\e[0mregular \e[1mbold\e[m regular\n"
-
- assert_equal expected, @to.end_accepting
+ def accept_paragraph_star
+ assert_equal "\e[0mreg \e[1mbold\e[m reg\n", @to.end_accepting
end
- def test_accept_paragraph_underscore
- @to.start_accepting
- @to.accept_paragraph @RM::Paragraph.new('regular _italic_ regular')
-
- expected = "\e[0mregular \e[4mitalic\e[m regular\n"
-
- assert_equal expected, @to.end_accepting
+ def accept_paragraph_underscore
+ assert_equal "\e[0mreg \e[4mitalic\e[m reg\n", @to.end_accepting
end
- def test_accept_paragraph_wrap
- @to.start_accepting
- @to.accept_paragraph @RM::Paragraph.new('words ' * 30)
-
+ def accept_paragraph_wrap
expected = <<-EXPECTED
\e[0mwords words words words words words words words words words words words words
words words words words words words words words words words words words words
-words words words words
+words words words words
EXPECTED
assert_equal expected, @to.end_accepting
end
- def test_accept_rule_indent
- @to.start_accepting
- @to.indent = 3
-
- @to.accept_rule @RM::Rule.new(1)
-
+ def accept_rule_indent
assert_equal "\e[0m #{'-' * 75}\n", @to.end_accepting
end
- def test_accept_verbatim_indent
- @to.start_accepting
-
- @to.indent = 2
-
- @to.accept_verbatim @RM::Verbatim.new(' ', 'hi', "\n",
- ' ', 'world', "\n")
-
+ def accept_verbatim_indent
assert_equal "\e[0m hi\n world\n\n", @to.end_accepting
end
- def test_accept_verbatim_big_indent
- @to.start_accepting
-
- @to.indent = 2
-
- @to.accept_verbatim @RM::Verbatim.new(' ', 'hi', "\n",
- ' ', 'world', "\n")
-
+ def accept_verbatim_big_indent
assert_equal "\e[0m hi\n world\n\n", @to.end_accepting
end
- def test_attributes
- assert_equal 'Dog', @to.attributes("\\Dog")
- end
-
- def test_list_nested
- doc = @RM::Document.new(
- @RM::List.new(:BULLET,
- @RM::ListItem.new(nil,
- @RM::Paragraph.new('l1'),
- @RM::List.new(:BULLET,
- @RM::ListItem.new(nil,
- @RM::Paragraph.new('l1.1')))),
- @RM::ListItem.new(nil,
- @RM::Paragraph.new('l2'))))
-
- output = doc.accept @to
-
+ def list_nested
expected = <<-EXPECTED
\e[0m* l1
* l1.1
* l2
EXPECTED
- assert_equal expected, output
- end
-
- def test_list_verbatim # HACK overblown
- doc = @RM::Document.new(
- @RM::List.new(:BULLET,
- @RM::ListItem.new(nil,
- @RM::Paragraph.new('list', 'stuff'),
- @RM::BlankLine.new(),
- @RM::Verbatim.new(' ', '*', ' ', 'list', "\n",
- ' ', 'with', "\n",
- "\n",
- ' ', 'second', "\n",
- "\n",
- ' ', '1.', ' ', 'indented', "\n",
- ' ', '2.', ' ', 'numbered', "\n",
- "\n",
- ' ', 'third', "\n",
- "\n",
- ' ', '*', ' ', 'second', "\n"))))
-
- output = doc.accept @to
+ assert_equal expected, @to.end_accepting
+ end
- expected = <<-EXPECTED
+ def list_verbatim
+ expected = <<-EXPECTED # HACK overblown
\e[0m* list stuff
* list
@@ -431,7 +321,7 @@ words words words words
EXPECTED
- assert_equal expected, output
+ assert_equal expected, @to.end_accepting
end
end
diff --git a/test/rdoc/test_rdoc_markup_to_bs.rb b/test/rdoc/test_rdoc_markup_to_bs.rb
index c042452637..3d2e4da8de 100644
--- a/test/rdoc/test_rdoc_markup_to_bs.rb
+++ b/test/rdoc/test_rdoc_markup_to_bs.rb
@@ -1,11 +1,12 @@
require 'rubygems'
-require 'rdoc/markup/formatter_test_case'
+require 'rdoc/markup/text_formatter_test_case'
require 'rdoc/markup/to_bs'
require 'minitest/autorun'
-class TestRDocMarkupToBs < RDoc::Markup::FormatterTestCase
+class TestRDocMarkupToBs < RDoc::Markup::TextFormatterTestCase
add_visitor_tests
+ add_text_tests
def setup
super
@@ -63,6 +64,7 @@ class TestRDocMarkupToBs < RDoc::Markup::FormatterTestCase
end
def accept_list_item_end_label
+ assert_equal "\n", @to.res.join
assert_equal 0, @to.indent, 'indent'
end
@@ -72,6 +74,7 @@ class TestRDocMarkupToBs < RDoc::Markup::FormatterTestCase
end
def accept_list_item_end_note
+ assert_equal "\n", @to.res.join
assert_equal 0, @to.indent, 'indent'
end
@@ -190,8 +193,8 @@ class TestRDocMarkupToBs < RDoc::Markup::FormatterTestCase
assert_equal "#{'-' * 78}\n", @to.res.join
end
- def accept_verbatim # FormatterTestCase doesn't set indent for ToAnsi
- assert_equal " hi\n world\n\n", @to.res.join
+ def accept_verbatim
+ assert_equal " hi\n world\n\n", @to.res.join
end
def end_accepting
@@ -206,232 +209,115 @@ class TestRDocMarkupToBs < RDoc::Markup::FormatterTestCase
assert_empty @to.list_width
end
- def test_accept_heading_1
+ def accept_heading_1
skip "No String#chars, upgrade your ruby" unless ''.respond_to? :chars
- @to.start_accepting
- @to.accept_heading @RM::Heading.new(1, 'Hello')
-
assert_equal "= H\bHe\bel\bll\blo\bo\n", @to.end_accepting
end
- def test_accept_heading_2
+ def accept_heading_2
skip "No String#chars, upgrade your ruby" unless ''.respond_to? :chars
- @to.start_accepting
- @to.accept_heading @RM::Heading.new(2, 'Hello')
-
assert_equal "== H\bHe\bel\bll\blo\bo\n", @to.end_accepting
end
- def test_accept_heading_3
+ def accept_heading_3
skip "No String#chars, upgrade your ruby" unless ''.respond_to? :chars
- @to.start_accepting
- @to.accept_heading @RM::Heading.new(3, 'Hello')
-
assert_equal "=== H\bHe\bel\bll\blo\bo\n", @to.end_accepting
end
- def test_accept_heading_4
+ def accept_heading_4
skip "No String#chars, upgrade your ruby" unless ''.respond_to? :chars
- @to.start_accepting
- @to.accept_heading @RM::Heading.new(4, 'Hello')
-
assert_equal "==== H\bHe\bel\bll\blo\bo\n", @to.end_accepting
end
- def test_accept_heading_indent
+ def accept_heading_indent
skip "No String#chars, upgrade your ruby" unless ''.respond_to? :chars
- @to.start_accepting
- @to.indent = 3
- @to.accept_heading @RM::Heading.new(1, 'Hello')
-
assert_equal " = H\bHe\bel\bll\blo\bo\n", @to.end_accepting
end
- def test_accept_heading_b
+ def accept_heading_b
skip "No String#chars, upgrade your ruby" unless ''.respond_to? :chars
- @to.start_accepting
- @to.indent = 3
- @to.accept_heading @RM::Heading.new(1, '*Hello*')
-
- assert_equal " = H\bHe\bel\bll\blo\bo\n", @to.end_accepting
+ assert_equal "= H\bHe\bel\bll\blo\bo\n", @to.end_accepting
end
- def test_accept_heading_suppressed_crossref
+ def accept_heading_suppressed_crossref
skip "No String#chars, upgrade your ruby" unless ''.respond_to? :chars
- @to.start_accepting
- @to.accept_heading @RM::Heading.new(1, '\\Hello')
-
assert_equal "= H\bHe\bel\bll\blo\bo\n", @to.end_accepting
end
- def test_accept_list_item_start_note_2
- list = @RM::List.new(:NOTE,
- @RM::ListItem.new('<tt>teletype</tt>',
- @RM::Paragraph.new('teletype description')))
-
- @to.start_accepting
-
- list.accept @to
-
- expected = "teletype:\n teletype description\n\n"
-
- assert_equal expected, @to.end_accepting
+ def accept_list_item_start_note_2
+ assert_equal "teletype:\n teletype description\n\n", @to.res.join
end
- def test_accept_paragraph_b
+ def accept_paragraph_b
skip "No String#chars, upgrade your ruby" unless ''.respond_to? :chars
- @to.start_accepting
- @to.accept_paragraph @RM::Paragraph.new('reg <b>bold words</b> reg')
-
- expected = "reg b\bbo\bol\bld\bd \b w\bwo\bor\brd\bds\bs reg\n"
-
- assert_equal expected, @to.end_accepting
+ assert_equal "reg b\bbo\bol\bld\bd \b w\bwo\bor\brd\bds\bs reg\n",
+ @to.end_accepting
end
- def test_accept_paragraph_i
+ def accept_paragraph_i
skip "No String#chars, upgrade your ruby" unless ''.respond_to? :chars
- @to.start_accepting
- @to.accept_paragraph @RM::Paragraph.new('reg <em>italic words</em> reg')
-
- expected = "reg _\bi_\bt_\ba_\bl_\bi_\bc_\b _\bw_\bo_\br_\bd_\bs reg\n"
-
- assert_equal expected, @to.end_accepting
+ assert_equal "reg _\bi_\bt_\ba_\bl_\bi_\bc_\b _\bw_\bo_\br_\bd_\bs reg\n",
+ @to.end_accepting
end
- def test_accept_paragraph_indent
- @to.start_accepting
- @to.indent = 3
- @to.accept_paragraph @RM::Paragraph.new('words ' * 30)
-
+ def accept_paragraph_indent
expected = <<-EXPECTED
words words words words words words words words words words words words
words words words words words words words words words words words words
- words words words words words words
+ words words words words words words
EXPECTED
assert_equal expected, @to.end_accepting
end
- def test_accept_paragraph_plus
- @to.start_accepting
- @to.accept_paragraph @RM::Paragraph.new('regular +teletype+ regular')
-
- expected = "regular teletype regular\n"
-
- assert_equal expected, @to.end_accepting
+ def accept_paragraph_plus
+ assert_equal "reg teletype reg\n", @to.end_accepting
end
- def test_accept_paragraph_star
+ def accept_paragraph_star
skip "No String#chars, upgrade your ruby" unless ''.respond_to? :chars
- @to.start_accepting
- @to.accept_paragraph @RM::Paragraph.new('regular *bold* regular')
-
- expected = "regular b\bbo\bol\bld\bd regular\n"
-
- assert_equal expected, @to.end_accepting
+ assert_equal "reg b\bbo\bol\bld\bd reg\n", @to.end_accepting
end
- def test_accept_paragraph_underscore
+ def accept_paragraph_underscore
skip "No String#chars, upgrade your ruby" unless ''.respond_to? :chars
- @to.start_accepting
- @to.accept_paragraph @RM::Paragraph.new('regular _italic_ regular')
-
- expected = "regular _\bi_\bt_\ba_\bl_\bi_\bc regular\n"
-
- assert_equal expected, @to.end_accepting
+ assert_equal "reg _\bi_\bt_\ba_\bl_\bi_\bc reg\n", @to.end_accepting
end
- def test_accept_paragraph_wrap
- @to.start_accepting
- @to.accept_paragraph @RM::Paragraph.new('words ' * 30)
-
+ def accept_paragraph_wrap
expected = <<-EXPECTED
words words words words words words words words words words words words words
words words words words words words words words words words words words words
-words words words words
+words words words words
EXPECTED
assert_equal expected, @to.end_accepting
end
- def test_accept_rule_indent
- @to.start_accepting
- @to.indent = 3
-
- @to.accept_rule @RM::Rule.new(1)
-
+ def accept_rule_indent
assert_equal " #{'-' * 75}\n", @to.end_accepting
end
- def test_accept_verbatim_indent
- @to.start_accepting
-
- @to.indent = 2
-
- @to.accept_verbatim @RM::Verbatim.new(' ', 'hi', "\n",
- ' ', 'world', "\n")
-
+ def accept_verbatim_indent
assert_equal " hi\n world\n\n", @to.end_accepting
end
- def test_accept_verbatim_big_indent
- @to.start_accepting
-
- @to.indent = 2
-
- @to.accept_verbatim @RM::Verbatim.new(' ', 'hi', "\n",
- ' ', 'world', "\n")
-
+ def accept_verbatim_big_indent
assert_equal " hi\n world\n\n", @to.end_accepting
end
- def test_attributes
- assert_equal 'Dog', @to.attributes("\\Dog")
- end
-
- def test_list_nested
- doc = @RM::Document.new(
- @RM::List.new(:BULLET,
- @RM::ListItem.new(nil,
- @RM::Paragraph.new('l1'),
- @RM::List.new(:BULLET,
- @RM::ListItem.new(nil,
- @RM::Paragraph.new('l1.1')))),
- @RM::ListItem.new(nil,
- @RM::Paragraph.new('l2'))))
-
- output = doc.accept @to
-
+ def list_nested
expected = <<-EXPECTED
* l1
* l1.1
* l2
EXPECTED
- assert_equal expected, output
- end
-
- def test_list_verbatim # HACK overblown
- doc = @RM::Document.new(
- @RM::List.new(:BULLET,
- @RM::ListItem.new(nil,
- @RM::Paragraph.new('list', 'stuff'),
- @RM::BlankLine.new(),
- @RM::Verbatim.new(' ', '*', ' ', 'list', "\n",
- ' ', 'with', "\n",
- "\n",
- ' ', 'second', "\n",
- "\n",
- ' ', '1.', ' ', 'indented', "\n",
- ' ', '2.', ' ', 'numbered', "\n",
- "\n",
- ' ', 'third', "\n",
- "\n",
- ' ', '*', ' ', 'second', "\n"))))
-
- output = doc.accept @to
+ assert_equal expected, @to.end_accepting
+ end
- expected = <<-EXPECTED
+ def list_verbatim
+ expected = <<-EXPECTED # HACK overblown
* list stuff
* list
@@ -448,7 +334,7 @@ words words words words
EXPECTED
- assert_equal expected, output
+ assert_equal expected, @to.end_accepting
end
end
diff --git a/test/rdoc/test_rdoc_markup_to_html.rb b/test/rdoc/test_rdoc_markup_to_html.rb
index f6014391c8..8a5860fe25 100644
--- a/test/rdoc/test_rdoc_markup_to_html.rb
+++ b/test/rdoc/test_rdoc_markup_to_html.rb
@@ -31,49 +31,73 @@ class TestRDocMarkupToHtml < RDoc::Markup::FormatterTestCase
end
def accept_heading
- assert_equal "<h5>Hello</h5>\n", @to.res.join
+ assert_equal "\n<h5>Hello</h5>\n", @to.res.join
+ end
+
+ def accept_heading_1
+ assert_equal "\n<h1>Hello</h1>\n", @to.res.join
+ end
+
+ def accept_heading_2
+ assert_equal "\n<h2>Hello</h2>\n", @to.res.join
+ end
+
+ def accept_heading_3
+ assert_equal "\n<h3>Hello</h3>\n", @to.res.join
+ end
+
+ def accept_heading_4
+ assert_equal "\n<h4>Hello</h4>\n", @to.res.join
+ end
+
+ def accept_heading_b
+ assert_equal "\n<h1><b>Hello</b></h1>\n", @to.res.join
+ end
+
+ def accept_heading_suppressed_crossref
+ assert_equal "\n<h1>Hello</h1>\n", @to.res.join
end
def accept_list_end_bullet
assert_equal [], @to.list
assert_equal [], @to.in_list_entry
- assert_equal "<ul>\n</ul>\n", @to.res.join
+ assert_equal "<ul></ul>\n", @to.res.join
end
def accept_list_end_label
assert_equal [], @to.list
assert_equal [], @to.in_list_entry
- assert_equal "<dl>\n</dl>\n", @to.res.join
+ assert_equal "<dl></dl>\n", @to.res.join
end
def accept_list_end_lalpha
assert_equal [], @to.list
assert_equal [], @to.in_list_entry
- assert_equal "<ol style=\"display: lower-alpha\">\n</ol>\n", @to.res.join
+ assert_equal "<ol style=\"display: lower-alpha\"></ol>\n", @to.res.join
end
def accept_list_end_number
assert_equal [], @to.list
assert_equal [], @to.in_list_entry
- assert_equal "<ol>\n</ol>\n", @to.res.join
+ assert_equal "<ol></ol>\n", @to.res.join
end
def accept_list_end_note
assert_equal [], @to.list
assert_equal [], @to.in_list_entry
- assert_equal "<table>\n</table>\n", @to.res.join
+ assert_equal "<table class=\"rdoc-list\"></table>\n", @to.res.join
end
def accept_list_end_ualpha
assert_equal [], @to.list
assert_equal [], @to.in_list_entry
- assert_equal "<ol style=\"display: upper-alpha\">\n</ol>\n", @to.res.join
+ assert_equal "<ol style=\"display: upper-alpha\"></ol>\n", @to.res.join
end
def accept_list_item_end_bullet
@@ -101,73 +125,105 @@ class TestRDocMarkupToHtml < RDoc::Markup::FormatterTestCase
end
def accept_list_item_start_bullet
- assert_equal "<ul>\n<li>", @to.res.join
+ assert_equal "<ul><li>", @to.res.join
end
def accept_list_item_start_label
- assert_equal "<dl>\n<dt>cat</dt><dd>", @to.res.join
+ assert_equal "<dl><dt>cat</dt>\n<dd>", @to.res.join
end
def accept_list_item_start_lalpha
- assert_equal "<ol style=\"display: lower-alpha\">\n<li>", @to.res.join
+ assert_equal "<ol style=\"display: lower-alpha\"><li>", @to.res.join
end
def accept_list_item_start_note
- assert_equal "<table>\n<tr><td valign=\"top\">cat</td><td>", @to.res.join
+ assert_equal "<table class=\"rdoc-list\"><tr><td class=\"rdoc-term\"><p>cat</p></td>\n<td>",
+ @to.res.join
+ end
+
+ def accept_list_item_start_note_2
+ expected = <<-EXPECTED
+<table class="rdoc-list"><tr><td class="rdoc-term"><p><tt>teletype</tt></p></td>
+<td>
+<p>teletype description</p>
+</td></tr></table>
+ EXPECTED
+
+ assert_equal expected, @to.res.join
end
def accept_list_item_start_number
- assert_equal "<ol>\n<li>", @to.res.join
+ assert_equal "<ol><li>", @to.res.join
end
def accept_list_item_start_ualpha
- assert_equal "<ol style=\"display: upper-alpha\">\n<li>", @to.res.join
+ assert_equal "<ol style=\"display: upper-alpha\"><li>", @to.res.join
end
def accept_list_start_bullet
assert_equal [:BULLET], @to.list
assert_equal [false], @to.in_list_entry
- assert_equal "<ul>\n", @to.res.join
+ assert_equal "<ul>", @to.res.join
end
def accept_list_start_label
assert_equal [:LABEL], @to.list
assert_equal [false], @to.in_list_entry
- assert_equal "<dl>\n", @to.res.join
+ assert_equal "<dl>", @to.res.join
end
def accept_list_start_lalpha
assert_equal [:LALPHA], @to.list
assert_equal [false], @to.in_list_entry
- assert_equal "<ol style=\"display: lower-alpha\">\n", @to.res.join
+ assert_equal "<ol style=\"display: lower-alpha\">", @to.res.join
end
def accept_list_start_note
assert_equal [:NOTE], @to.list
assert_equal [false], @to.in_list_entry
- assert_equal "<table>\n", @to.res.join
+ assert_equal "<table class=\"rdoc-list\">", @to.res.join
end
def accept_list_start_number
assert_equal [:NUMBER], @to.list
assert_equal [false], @to.in_list_entry
- assert_equal "<ol>\n", @to.res.join
+ assert_equal "<ol>", @to.res.join
end
def accept_list_start_ualpha
assert_equal [:UALPHA], @to.list
assert_equal [false], @to.in_list_entry
- assert_equal "<ol style=\"display: upper-alpha\">\n", @to.res.join
+ assert_equal "<ol style=\"display: upper-alpha\">", @to.res.join
end
def accept_paragraph
- assert_equal "<p>\nhi\n</p>\n", @to.res.join
+ assert_equal "\n<p>hi</p>\n", @to.res.join
+ end
+
+ def accept_paragraph_b
+ assert_equal "\n<p>reg <b>bold words</b> reg</p>\n", @to.res.join
+ end
+
+ def accept_paragraph_i
+ assert_equal "\n<p>reg <em>italic words</em> reg</p>\n", @to.res.join
+ end
+
+ def accept_paragraph_plus
+ assert_equal "\n<p>reg <tt>teletype</tt> reg</p>\n", @to.res.join
+ end
+
+ def accept_paragraph_star
+ assert_equal "\n<p>reg <b>bold</b> reg</p>\n", @to.res.join
+ end
+
+ def accept_paragraph_underscore
+ assert_equal "\n<p>reg <em>italic</em> reg</p>\n", @to.res.join
end
def accept_raw
@@ -183,11 +239,11 @@ class TestRDocMarkupToHtml < RDoc::Markup::FormatterTestCase
end
def accept_rule
- assert_equal '<hr style="height: 4px"></hr>', @to.res.join
+ assert_equal "<hr style=\"height: 4px\">\n", @to.res.join
end
def accept_verbatim
- assert_equal "<pre>\n hi\n world\n</pre>\n", @to.res.join
+ assert_equal "\n<pre>hi\n world</pre>\n", @to.res.join
end
def end_accepting
@@ -200,54 +256,70 @@ class TestRDocMarkupToHtml < RDoc::Markup::FormatterTestCase
assert_equal [], @to.list
end
- def test_list_verbatim
- str = "* one\n verb1\n verb2\n* two\n"
-
+ def list_nested
expected = <<-EXPECTED
-<ul>
-<li><p>
-one
-</p>
-<pre>
- verb1
- verb2
-</pre>
-</li>
-<li><p>
-two
-</p>
-</li>
-</ul>
+<ul><li>
+<p>l1</p>
+<ul><li>
+<p>l1.1</p>
+</li></ul>
+</li><li>
+<p>l2</p>
+</li></ul>
EXPECTED
- assert_equal expected, @m.convert(str, @to)
+ assert_equal expected, @to.res.join
end
- def test_tt_formatting
- assert_equal "<p>\n<tt>--</tt> &#8212; <tt>cats'</tt> cats&#8217;\n</p>\n",
- util_format("<tt>--</tt> -- <tt>cats'</tt> cats'")
+ def list_verbatim
+ expected = <<-EXPECTED
+<ul><li>
+<p>list stuff</p>
+
+<pre>* list
+ with
+
+ second
+
+ 1. indented
+ 2. numbered
- assert_equal "<p>\n<b>&#8212;</b>\n</p>\n", util_format("<b>--</b>")
+ third
+
+* second</pre>
+</li></ul>
+ EXPECTED
+
+ assert_equal expected, @to.end_accepting
end
- def test_convert_string_fancy
- #
- # The HTML typesetting is broken in a number of ways, but I have fixed
- # the most glaring issues for single and double quotes. Note that
- # "strange" symbols (periods or dashes) need to be at the end of the
- # test case strings in order to suppress cross-references.
- #
- assert_equal "<p>\n&#8220;cats&#8221;.\n</p>\n", util_format("\"cats\".")
- assert_equal "<p>\n&#8216;cats&#8217;.\n</p>\n", util_format("\'cats\'.")
- assert_equal "<p>\ncat&#8217;s-\n</p>\n", util_format("cat\'s-")
+ def test_convert_string
+ assert_equal '&lt;&gt;', @to.convert_string('<>')
+ end
+
+ def test_list_verbatim_2
+ str = "* one\n verb1\n verb2\n* two\n"
+
+ expected = <<-EXPECTED
+<ul><li>
+<p>one</p>
+
+<pre>verb1
+verb2</pre>
+</li><li>
+<p>two</p>
+</li></ul>
+ EXPECTED
+
+ assert_equal expected, @m.convert(str, @to)
end
- def util_paragraph(text)
- RDoc::Markup::Paragraph.new text
+ def test_to_html
+ assert_equal "\n<p><tt>--</tt></p>\n", util_format("<tt>--</tt>")
end
- def util_format(text)
- paragraph = util_paragraph text
+ def util_format text
+ paragraph = RDoc::Markup::Paragraph.new text
@to.start_accepting
@to.accept_paragraph paragraph
diff --git a/test/rdoc/test_rdoc_markup_to_html_crossref.rb b/test/rdoc/test_rdoc_markup_to_html_crossref.rb
index 67dfc0cbc9..8c97941727 100644
--- a/test/rdoc/test_rdoc_markup_to_html_crossref.rb
+++ b/test/rdoc/test_rdoc_markup_to_html_crossref.rb
@@ -14,12 +14,11 @@ class TestRDocMarkupToHtmlCrossref < XrefTestCase
end
def assert_ref(path, ref)
- assert_equal "<p>\n<a href=\"#{path}\">#{ref}</a>\n</p>\n",
- @xref.convert(ref)
+ assert_equal "\n<p><a href=\"#{path}\">#{ref}</a></p>\n", @xref.convert(ref)
end
def refute_ref(body, ref)
- assert_equal "<p>\n#{body}\n</p>\n", @xref.convert(ref)
+ assert_equal "\n<p>#{body}</p>\n", @xref.convert(ref)
end
def test_handle_special_CROSSREF_C2
@@ -108,16 +107,16 @@ class TestRDocMarkupToHtmlCrossref < XrefTestCase
assert_ref 'C1.html#method-c-m', '::m'
assert_ref 'C1.html#method-i-m', 'C1#m'
- assert_ref 'C1.html#method-i-m', 'C1.m'
+ assert_ref 'C1.html#method-c-m', 'C1.m'
assert_ref 'C1.html#method-c-m', 'C1::m'
assert_ref 'C1.html#method-i-m', 'C1#m'
assert_ref 'C1.html#method-i-m', 'C1#m()'
assert_ref 'C1.html#method-i-m', 'C1#m(*)'
- assert_ref 'C1.html#method-i-m', 'C1.m'
- assert_ref 'C1.html#method-i-m', 'C1.m()'
- assert_ref 'C1.html#method-i-m', 'C1.m(*)'
+ assert_ref 'C1.html#method-c-m', 'C1.m'
+ assert_ref 'C1.html#method-c-m', 'C1.m()'
+ assert_ref 'C1.html#method-c-m', 'C1.m(*)'
assert_ref 'C1.html#method-c-m', 'C1::m'
assert_ref 'C1.html#method-c-m', 'C1::m()'
@@ -127,7 +126,8 @@ class TestRDocMarkupToHtmlCrossref < XrefTestCase
assert_ref 'C2/C3.html#method-i-m', 'C2::C3.m'
- assert_ref 'C2/C3/H1.html#method-i-m%3F', 'C2::C3::H1#m?'
+ # TODO stop escaping - HTML5 allows anything but space
+ assert_ref 'C2/C3/H1.html#method-i-m-3F', 'C2::C3::H1#m?'
assert_ref 'C2/C3.html#method-i-m', '::C2::C3#m'
assert_ref 'C2/C3.html#method-i-m', '::C2::C3#m()'
@@ -153,8 +153,15 @@ class TestRDocMarkupToHtmlCrossref < XrefTestCase
refute_ref '::C3::H1#n', '\::C3::H1#n'
end
+ def test_handle_special_CROSSREF_show_hash_false
+ @xref.show_hash = false
+
+ assert_equal "\n<p><a href=\"C1.html#method-i-m\">m</a></p>\n",
+ @xref.convert('#m')
+ end
+
def test_handle_special_CROSSREF_special
- assert_equal "<p>\n<a href=\"C2/C3.html\">C2::C3</a>;method(*)\n</p>\n",
+ assert_equal "\n<p><a href=\"C2/C3.html\">C2::C3</a>;method(*)</p>\n",
@xref.convert('C2::C3;method(*)')
end
diff --git a/test/rdoc/test_rdoc_markup_to_rdoc.rb b/test/rdoc/test_rdoc_markup_to_rdoc.rb
index ac6884ba58..20ff937c5a 100644
--- a/test/rdoc/test_rdoc_markup_to_rdoc.rb
+++ b/test/rdoc/test_rdoc_markup_to_rdoc.rb
@@ -1,11 +1,12 @@
require 'rubygems'
-require 'rdoc/markup/formatter_test_case'
+require 'rdoc/markup/text_formatter_test_case'
require 'rdoc/markup/to_rdoc'
require 'minitest/autorun'
-class TestRDocMarkupToRdoc < RDoc::Markup::FormatterTestCase
+class TestRDocMarkupToRDoc < RDoc::Markup::TextFormatterTestCase
add_visitor_tests
+ add_text_tests
def setup
super
@@ -191,8 +192,8 @@ class TestRDocMarkupToRdoc < RDoc::Markup::FormatterTestCase
assert_equal "#{'-' * 78}\n", @to.res.join
end
- def accept_verbatim # FormatterTestCase doesn't set indent for ToAnsi
- assert_equal " hi\n world\n\n", @to.res.join
+ def accept_verbatim
+ assert_equal " hi\n world\n\n", @to.res.join
end
def end_accepting
@@ -207,214 +208,102 @@ class TestRDocMarkupToRdoc < RDoc::Markup::FormatterTestCase
assert_empty @to.list_width
end
- def test_accept_heading_1
- @to.start_accepting
- @to.accept_heading @RM::Heading.new(1, 'Hello')
-
+ def accept_heading_1
assert_equal "= Hello\n", @to.end_accepting
end
- def test_accept_heading_2
- @to.start_accepting
- @to.accept_heading @RM::Heading.new(2, 'Hello')
-
+ def accept_heading_2
assert_equal "== Hello\n", @to.end_accepting
end
- def test_accept_heading_3
- @to.start_accepting
- @to.accept_heading @RM::Heading.new(3, 'Hello')
-
+ def accept_heading_3
assert_equal "=== Hello\n", @to.end_accepting
end
- def test_accept_heading_4
- @to.start_accepting
- @to.accept_heading @RM::Heading.new(4, 'Hello')
-
+ def accept_heading_4
assert_equal "==== Hello\n", @to.end_accepting
end
- def test_accept_heading_indent
- @to.start_accepting
- @to.indent = 3
- @to.accept_heading @RM::Heading.new(1, 'Hello')
-
+ def accept_heading_indent
assert_equal " = Hello\n", @to.end_accepting
end
- def test_accept_heading_b
- @to.start_accepting
- @to.indent = 3
- @to.accept_heading @RM::Heading.new(1, '*Hello*')
-
- assert_equal " = <b>Hello</b>\n", @to.end_accepting
+ def accept_heading_b
+ assert_equal "= <b>Hello</b>\n", @to.end_accepting
end
- def test_accept_list_item_start_note_2
- list = @RM::List.new(:NOTE,
- @RM::ListItem.new('<tt>teletype</tt>',
- @RM::Paragraph.new('teletype description')))
-
- @to.start_accepting
-
- list.accept @to
-
- expected = "<tt>teletype</tt>:\n teletype description\n\n"
-
- assert_equal expected, @to.end_accepting
+ def accept_heading_suppressed_crossref
+ assert_equal "= Hello\n", @to.end_accepting
end
- def test_accept_paragraph_b
- @to.start_accepting
- @to.accept_paragraph @RM::Paragraph.new('reg <b>bold words</b> reg')
-
- expected = "reg <b>bold words</b> reg\n"
-
- assert_equal expected, @to.end_accepting
+ def accept_list_item_start_note_2
+ assert_equal "<tt>teletype</tt>:\n teletype description\n\n", @to.res.join
end
- def test_accept_paragraph_i
- @to.start_accepting
- @to.accept_paragraph @RM::Paragraph.new('reg <em>italic words</em> reg')
-
- expected = "reg <em>italic words</em> reg\n"
-
- assert_equal expected, @to.end_accepting
+ def accept_paragraph_b
+ assert_equal "reg <b>bold words</b> reg\n", @to.end_accepting
end
- def test_accept_paragraph_indent
- @to.start_accepting
- @to.indent = 3
- @to.accept_paragraph @RM::Paragraph.new('words ' * 30)
+ def accept_paragraph_i
+ assert_equal "reg <em>italic words</em> reg\n", @to.end_accepting
+ end
+ def accept_paragraph_indent
expected = <<-EXPECTED
words words words words words words words words words words words words
words words words words words words words words words words words words
- words words words words words words
+ words words words words words words
EXPECTED
assert_equal expected, @to.end_accepting
end
- def test_accept_paragraph_plus
- @to.start_accepting
- @to.accept_paragraph @RM::Paragraph.new('regular +teletype+ regular')
-
- expected = "regular <tt>teletype</tt> regular\n"
-
- assert_equal expected, @to.end_accepting
+ def accept_paragraph_plus
+ assert_equal "reg <tt>teletype</tt> reg\n", @to.end_accepting
end
- def test_accept_paragraph_star
- @to.start_accepting
- @to.accept_paragraph @RM::Paragraph.new('regular *bold* regular')
-
- expected = "regular <b>bold</b> regular\n"
-
- assert_equal expected, @to.end_accepting
+ def accept_paragraph_star
+ assert_equal "reg <b>bold</b> reg\n", @to.end_accepting
end
- def test_accept_paragraph_underscore
- @to.start_accepting
- @to.accept_paragraph @RM::Paragraph.new('regular _italic_ regular')
-
- expected = "regular <em>italic</em> regular\n"
-
- assert_equal expected, @to.end_accepting
+ def accept_paragraph_underscore
+ assert_equal "reg <em>italic</em> reg\n", @to.end_accepting
end
- def test_accept_paragraph_wrap
- @to.start_accepting
- @to.accept_paragraph @RM::Paragraph.new('words ' * 30)
-
+ def accept_paragraph_wrap
expected = <<-EXPECTED
words words words words words words words words words words words words words
words words words words words words words words words words words words words
-words words words words
+words words words words
EXPECTED
assert_equal expected, @to.end_accepting
end
- def test_accept_rule_indent
- @to.start_accepting
- @to.indent = 3
-
- @to.accept_rule @RM::Rule.new(1)
-
+ def accept_rule_indent
assert_equal " #{'-' * 75}\n", @to.end_accepting
end
- def test_accept_verbatim_indent
- @to.start_accepting
-
- @to.indent = 2
-
- @to.accept_verbatim @RM::Verbatim.new(' ', 'hi', "\n",
- ' ', 'world', "\n")
-
+ def accept_verbatim_indent
assert_equal " hi\n world\n\n", @to.end_accepting
end
- def test_accept_verbatim_big_indent
- @to.start_accepting
-
- @to.indent = 2
-
- @to.accept_verbatim @RM::Verbatim.new(' ', 'hi', "\n",
- ' ', 'world', "\n")
-
+ def accept_verbatim_big_indent
assert_equal " hi\n world\n\n", @to.end_accepting
end
- def test_attributes
- assert_equal 'Dog', @to.attributes("\\Dog")
- end
-
- def test_list_nested
- doc = @RM::Document.new(
- @RM::List.new(:BULLET,
- @RM::ListItem.new(nil,
- @RM::Paragraph.new('l1'),
- @RM::List.new(:BULLET,
- @RM::ListItem.new(nil,
- @RM::Paragraph.new('l1.1')))),
- @RM::ListItem.new(nil,
- @RM::Paragraph.new('l2'))))
-
- output = doc.accept @to
-
+ def list_nested
expected = <<-EXPECTED
* l1
* l1.1
* l2
EXPECTED
- assert_equal expected, output
- end
-
- def test_list_verbatim # HACK overblown
- doc = @RM::Document.new(
- @RM::List.new(:BULLET,
- @RM::ListItem.new(nil,
- @RM::Paragraph.new('list', 'stuff'),
- @RM::BlankLine.new(),
- @RM::Verbatim.new(' ', '*', ' ', 'list', "\n",
- ' ', 'with', "\n",
- "\n",
- ' ', 'second', "\n",
- "\n",
- ' ', '1.', ' ', 'indented', "\n",
- ' ', '2.', ' ', 'numbered', "\n",
- "\n",
- ' ', 'third', "\n",
- "\n",
- ' ', '*', ' ', 'second', "\n"))))
-
- output = doc.accept @to
+ assert_equal expected, @to.end_accepting
+ end
- expected = <<-EXPECTED
+ def list_verbatim
+ expected = <<-EXPECTED # HACK overblown
* list stuff
* list
@@ -431,7 +320,7 @@ words words words words
EXPECTED
- assert_equal expected, output
+ assert_equal expected, @to.end_accepting
end
end
diff --git a/test/rdoc/test_rdoc_method_attr.rb b/test/rdoc/test_rdoc_method_attr.rb
new file mode 100644
index 0000000000..007a3f6b35
--- /dev/null
+++ b/test/rdoc/test_rdoc_method_attr.rb
@@ -0,0 +1,122 @@
+require File.expand_path '../xref_test_case', __FILE__
+
+class TestRDocMethodAttr < XrefTestCase
+
+ def test_block_params_equal
+
+ m = RDoc::MethodAttr.new(nil, 'foo')
+
+ m.block_params = ''
+ assert_equal '', m.block_params
+
+ m.block_params = 'a_var'
+ assert_equal 'a_var', m.block_params
+
+ m.block_params = '()'
+ assert_equal '', m.block_params
+
+ m.block_params = '(a_var, b_var)'
+ assert_equal 'a_var, b_var', m.block_params
+
+ m.block_params = '.to_s + "</#{element.upcase}>"'
+ assert_equal '', m.block_params
+
+ m.block_params = 'i.name'
+ assert_equal 'name', m.block_params
+
+ m.block_params = 'attr.expanded_name, attr.value'
+ assert_equal 'expanded_name, value', m.block_params
+
+ m.block_params = 'expanded_name, attr.value'
+ assert_equal 'expanded_name, value', m.block_params
+
+ m.block_params = 'attr.expanded_name, value'
+ assert_equal 'expanded_name, value', m.block_params
+
+ m.block_params = '(@base_notifier)'
+ assert_equal 'base_notifier', m.block_params
+
+ m.block_params = 'if @signal_status == :IN_LOAD'
+ assert_equal '', m.block_params
+
+ m.block_params = 'e if e.kind_of? Element'
+ assert_equal 'e', m.block_params
+
+ m.block_params = '(e, f) if e.kind_of? Element'
+ assert_equal 'e, f', m.block_params
+
+ m.block_params = 'back_path, back_name'
+ assert_equal 'back_path, back_name', m.block_params
+
+ m.block_params = '(*a[1..-1])'
+ assert_equal '*a', m.block_params
+
+ m.block_params = '@@context[:node] if defined? @@context[:node].namespace'
+ assert_equal 'context', m.block_params
+
+ m.block_params = '(result, klass.const_get(constant_name))'
+ assert_equal 'result, const', m.block_params
+
+ m.block_params = 'name.to_s if (bitmap & bit) != 0'
+ assert_equal 'name', m.block_params
+
+ m.block_params = 'line unless line.deleted'
+ assert_equal 'line', m.block_params
+
+ m.block_params = 'str + rs'
+ assert_equal 'str', m.block_params
+
+ m.block_params = 'f+rs'
+ assert_equal 'f', m.block_params
+
+ m.block_params = '[user, realm, hash[user]]'
+ assert_equal 'user, realm, hash', m.block_params
+
+ m.block_params = 'proc{|rc| rc == "rc" ? irbrc : irbrc+rc| ... }'
+ assert_equal 'proc', m.block_params
+
+ m.block_params = 'lambda { |x| x.to_i }'
+ assert_equal 'lambda', m.block_params
+
+ m.block_params = '$&'
+ assert_equal 'str', m.block_params
+
+ m.block_params = 'Inflections.instance'
+ assert_equal 'instance', m.block_params
+
+ m.block_params = 'self.class::STARTED'
+ assert_equal 'STARTED', m.block_params
+
+ m.block_params = 'Test::Unit::TestCase::STARTED'
+ assert_equal 'STARTED', m.block_params
+
+ m.block_params = 'ActiveSupport::OptionMerger.new(self, options)'
+ assert_equal 'option_merger', m.block_params
+
+ m.block_params = ', msg'
+ assert_equal '', m.block_params
+
+ m.block_params = '[size.to_s(16), term, chunk, term].join'
+ assert_equal '[size, term, chunk, term].join', m.block_params
+
+ m.block_params = 'YPath.new( path )'
+ assert_equal 'y_path', m.block_params
+
+ end
+
+ def test_find_method_or_attribute_recursive
+ inc = RDoc::Include.new 'M1', nil
+ @m1.add_include inc # M1 now includes itself
+
+ assert_nil @m1_m.find_method_or_attribute 'm'
+ end
+
+ def test_to_s
+ assert_equal 'RDoc::AnyMethod: C1#m', @c1_m.to_s
+ assert_equal 'RDoc::AnyMethod: C2#b', @c2_b.to_s
+ assert_equal 'RDoc::AnyMethod: C1::m', @c1__m.to_s
+ end
+
+
+end
+
diff --git a/test/rdoc/test_rdoc_normal_class.rb b/test/rdoc/test_rdoc_normal_class.rb
index b7471f654f..db07ecb9c7 100644
--- a/test/rdoc/test_rdoc_normal_class.rb
+++ b/test/rdoc/test_rdoc_normal_class.rb
@@ -10,7 +10,7 @@ class TestRDocNormalClass < XrefTestCase
sub_klass = klass.add_class RDoc::NormalClass, 'SubClass', 'Klass'
sub_klass.add_include incl
- assert_equal [incl, klass], sub_klass.ancestors
+ assert_equal [incl.name, klass], sub_klass.ancestors
end
end
diff --git a/test/rdoc/test_rdoc_normal_module.rb b/test/rdoc/test_rdoc_normal_module.rb
index da48d33f55..570b2765c6 100644
--- a/test/rdoc/test_rdoc_normal_module.rb
+++ b/test/rdoc/test_rdoc_normal_module.rb
@@ -15,7 +15,12 @@ class TestRDocNormalModule < XrefTestCase
mod.add_include incl
- assert_equal [incl], mod.ancestors
+ assert_equal [incl.name], mod.ancestors
+
+ mod2 = top_level.add_module RDoc::NormalModule, 'Inc2'
+ inc2 = RDoc::Include.new 'Inc2', ''
+ mod.add_include inc2
+ assert_equal [mod2, incl.name], mod.ancestors
end
def test_module_eh
diff --git a/test/rdoc/test_rdoc_options.rb b/test/rdoc/test_rdoc_options.rb
index f4a8d51c8e..3d07353c5f 100644
--- a/test/rdoc/test_rdoc_options.rb
+++ b/test/rdoc/test_rdoc_options.rb
@@ -2,12 +2,182 @@ require 'rubygems'
require 'minitest/autorun'
require 'rdoc/options'
+require 'fileutils'
+require 'tmpdir'
+
class TestRDocOptions < MiniTest::Unit::TestCase
def setup
@options = RDoc::Options.new
end
+ def test_check_files
+ out, err = capture_io do
+ Dir.mktmpdir do |dir|
+ Dir.chdir dir do
+ FileUtils.touch 'unreadable'
+ FileUtils.chmod 0, 'unreadable'
+
+ @options.files = %w[nonexistent unreadable]
+
+ @options.check_files
+ end
+ end
+ end
+
+ assert_empty @options.files
+
+ assert_equal '', out
+
+ expected = <<-EXPECTED
+file 'nonexistent' not found
+file 'unreadable' not readable
+ EXPECTED
+
+ assert_equal expected, err
+ end
+
+ def test_dry_run_default
+ refute @options.dry_run
+ end
+
+ def test_encoding_default
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ assert_equal Encoding.default_external, @options.encoding
+ end
+
+ def test_parse_dash_p
+ out, err = capture_io do
+ @options.parse %w[-p]
+ end
+
+ assert @options.pipe
+ refute_match %r%^Usage: %, err
+ refute_match %r%^invalid options%, err
+
+ assert_empty out
+ end
+
+ def test_parse_dash_p_files
+ out, err = capture_io do
+ @options.parse ['-p', File.expand_path(__FILE__)]
+ end
+
+ refute @options.pipe
+ refute_match %r%^Usage: %, err
+ assert_match %r%^invalid options: -p .with files.%, err
+
+ assert_empty out
+ end
+
+ def test_parse_default
+ @options.parse []
+
+ assert_equal RDoc::Generator::Darkfish, @options.generator
+ assert_equal 'darkfish', @options.template
+ assert_match %r%rdoc/generator/template/darkfish$%, @options.template_dir
+ end
+
+ def test_parse_deprecated
+ dep_hash = RDoc::Options::DEPRECATED
+ options = dep_hash.keys.sort
+
+ out, err = capture_io do
+ @options.parse options
+ end
+
+ dep_hash.each_pair do |opt, message|
+ assert_match %r%.*#{opt}.+#{message}%, err
+ end
+
+ assert_empty out
+ end
+
+ def test_parse_dry_run
+ @options.parse %w[--dry-run]
+
+ assert @options.dry_run
+ end
+
+ def test_parse_encoding
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ @options.parse %w[--encoding Big5]
+
+ assert_equal Encoding::Big5, @options.encoding
+ assert_equal 'Big5', @options.charset
+ end
+
+ def test_parse_encoding_invalid
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ out, err = capture_io do
+ @options.parse %w[--encoding invalid]
+ end
+
+ assert_match %r%^invalid options: --encoding invalid%, err
+
+ assert_empty out
+ end
+
+ def test_parse_formatter
+ e = assert_raises OptionParser::InvalidOption do
+ @options.parse %w[--format darkfish --format ri]
+ end
+
+ assert_equal 'invalid option: --format generator already set to darkfish',
+ e.message
+ end
+
+ def test_parse_formatter_ri
+ e = assert_raises OptionParser::InvalidOption do
+ @options.parse %w[--format darkfish --ri]
+ end
+
+ assert_equal 'invalid option: --ri generator already set to darkfish',
+ e.message
+
+ @options = RDoc::Options.new
+
+ e = assert_raises OptionParser::InvalidOption do
+ @options.parse %w[--format darkfish -r]
+ end
+
+ assert_equal 'invalid option: -r generator already set to darkfish',
+ e.message
+ end
+
+ def test_parse_formatter_ri_site
+ e = assert_raises OptionParser::InvalidOption do
+ @options.parse %w[--format darkfish --ri-site]
+ end
+
+ assert_equal 'invalid option: --ri-site generator already set to darkfish',
+ e.message
+
+ @options = RDoc::Options.new
+
+ e = assert_raises OptionParser::InvalidOption do
+ @options.parse %w[--format darkfish -R]
+ end
+
+ assert_equal 'invalid option: -R generator already set to darkfish',
+ e.message
+ end
+
+ def test_parse_help
+ out, = capture_io do
+ begin
+ @options.parse %w[--help]
+ rescue SystemExit
+ end
+ end
+
+ assert_equal 1, out.scan(/HTML generator options:/).length
+ assert_equal 1, out.scan(/ri generator options:/). length
+ end
+
def test_parse_ignore_invalid
out, err = capture_io do
@options.parse %w[--ignore-invalid --bogus]
@@ -15,6 +185,8 @@ class TestRDocOptions < MiniTest::Unit::TestCase
refute_match %r%^Usage: %, err
assert_match %r%^invalid options: --bogus%, err
+
+ assert_empty out
end
def test_parse_ignore_invalid_default
@@ -26,17 +198,21 @@ class TestRDocOptions < MiniTest::Unit::TestCase
assert_match %r%^invalid options: --bogus%, err
assert_equal 'BLAH', @options.main_page
+
+ assert_empty out
end
def test_parse_ignore_invalid_no
out, err = capture_io do
assert_raises SystemExit do
- @options.parse %w[--no-ignore-invalid --bogus]
+ @options.parse %w[--no-ignore-invalid --bogus=arg --bobogus --visibility=extended]
end
end
assert_match %r%^Usage: %, err
- assert_match %r%^invalid option: --bogus%, err
+ assert_match %r%^invalid options: --bogus=arg, --bobogus, --visibility=extended%, err
+
+ assert_empty out
end
def test_parse_main
@@ -50,24 +226,73 @@ class TestRDocOptions < MiniTest::Unit::TestCase
assert_equal 'MAIN', @options.main_page
end
- def test_parse_dash_p
+ def test_parse_template
out, err = capture_io do
- @options.parse %w[-p]
+ @options.parse %w[--template darkfish]
end
- assert @options.pipe
- refute_match %r%^Usage: %, err
- refute_match %r%^invalid options%, err
+ assert_empty out
+ assert_empty err
+
+ assert_equal 'darkfish', @options.template
+
+ assert_match %r%rdoc/generator/template/darkfish$%, @options.template_dir
end
- def test_parse_dash_p_files
+ def test_parse_template_nonexistent
out, err = capture_io do
- @options.parse %w[-p README]
+ @options.parse %w[--template NONEXISTENT]
end
- refute @options.pipe
- refute_match %r%^Usage: %, err
- assert_match %r%^invalid options: -p .with files.%, err
+ assert_empty out
+ assert_equal "could not find template NONEXISTENT\n", err
+
+ assert_equal 'darkfish', @options.template
+ assert_match %r%rdoc/generator/template/darkfish$%, @options.template_dir
+ end
+
+ def test_parse_template_load_path
+ orig_LOAD_PATH = $LOAD_PATH.dup
+
+ template_dir = nil
+
+ Dir.mktmpdir do |dir|
+ $LOAD_PATH << dir
+
+ template_dir = File.join dir, 'rdoc', 'generator', 'template', 'load_path'
+
+ FileUtils.mkdir_p template_dir
+
+ out, err = capture_io do
+ @options.parse %w[--template load_path]
+ end
+
+ assert_empty out
+ assert_empty err
+ end
+
+ assert_equal 'load_path', @options.template
+ assert_equal template_dir, @options.template_dir
+ ensure
+ $LOAD_PATH.replace orig_LOAD_PATH
+ end
+
+ def test_setup_generator
+ test_generator = Object.new
+ def test_generator.setup_options(op)
+ @op = op
+ end
+
+ def test_generator.op() @op end
+
+ RDoc::RDoc::GENERATORS['TestGenerator'] = test_generator
+
+ @options.setup_generator 'TestGenerator'
+
+ assert_equal test_generator, @options.generator
+ assert_equal [test_generator], @options.generator_options
+
+ assert_equal @options, test_generator.op
end
end
diff --git a/test/rdoc/test_rdoc_parser.rb b/test/rdoc/test_rdoc_parser.rb
index ebb520032d..2eb1ac1a67 100644
--- a/test/rdoc/test_rdoc_parser.rb
+++ b/test/rdoc/test_rdoc_parser.rb
@@ -11,25 +11,6 @@ class TestRDocParser < MiniTest::Unit::TestCase
@binary_dat = File.expand_path '../binary.dat', __FILE__
end
- def test_class_binary_eh_erb
- erb = File.join Dir.tmpdir, "test_rdoc_parser_#{$$}.erb"
- open erb, 'wb' do |io|
- io.write 'blah blah <%= stuff %> <% more stuff %>'
- end
-
- assert @RP.binary?(erb)
-
- erb_rb = File.join Dir.tmpdir, "test_rdoc_parser_#{$$}.erb.rb"
- open erb_rb, 'wb' do |io|
- io.write 'blah blah <%= stuff %>'
- end
-
- refute @RP.binary?(erb_rb)
- ensure
- File.unlink erb
- File.unlink erb_rb if erb_rb
- end
-
def test_class_binary_eh_marshal
marshal = File.join Dir.tmpdir, "test_rdoc_parser_#{$$}.marshal"
open marshal, 'wb' do |io|
@@ -42,6 +23,18 @@ class TestRDocParser < MiniTest::Unit::TestCase
File.unlink marshal
end
+ def test_class_binary_japanese_text
+ file_name = File.expand_path '../test.ja.txt', __FILE__
+ refute @RP.binary?(file_name)
+ end
+
+ def test_class_binary_japanese_rdoc
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ file_name = File.expand_path '../test.ja.rdoc', __FILE__
+ refute @RP.binary?(file_name)
+ end
+
def test_class_can_parse
assert_equal @RP.can_parse(__FILE__), @RP::Ruby
@@ -72,6 +65,10 @@ class TestRDocParser < MiniTest::Unit::TestCase
def test_class_for_binary
rp = @RP.dup
+ class << rp
+ alias old_can_parse can_parse
+ end
+
def rp.can_parse(*args) nil end
assert_nil @RP.for(nil, @binary_dat, nil, nil, nil)
diff --git a/test/rdoc/test_rdoc_parser_c.rb b/test/rdoc/test_rdoc_parser_c.rb
index 4f5d9e3171..c6cc42081c 100644
--- a/test/rdoc/test_rdoc_parser_c.rb
+++ b/test/rdoc/test_rdoc_parser_c.rb
@@ -1,12 +1,52 @@
require 'stringio'
require 'tempfile'
require 'rubygems'
-require 'minitest/unit'
+require 'minitest/autorun'
require 'rdoc/options'
require 'rdoc/parser/c'
+=begin
+ TODO: test call-seq parsing
+
+/*
+ * call-seq:
+ * ARGF.readlines(sep=$/) -> array
+ * ARGF.readlines(limit) -> array
+ * ARGF.readlines(sep, limit) -> array
+ *
+ * ARGF.to_a(sep=$/) -> array
+ * ARGF.to_a(limit) -> array
+ * ARGF.to_a(sep, limit) -> array
+ *
+ * Reads +ARGF+'s current file in its entirety, returning an +Array+ of its
+ * lines, one line per element. Lines are assumed to be separated by _sep_.
+ *
+ * lines = ARGF.readlines
+ * lines[0] #=> "This is line one\n"
+ */
+
+assert call-seq did not stop at first empty line
+
+/*
+ * call-seq:
+ *
+ * flt ** other -> float
+ *
+ * Raises <code>float</code> the <code>other</code> power.
+ *
+ * 2.0**3 #=> 8.0
+ */
+
+assert call-seq correct (bug: was empty)
+
+/* call-seq: flt ** other -> float */
+
+assert call-seq correct
+
+=end
+
class RDoc::Parser::C
- attr_accessor :classes
+ attr_accessor :classes, :singleton_classes
public :do_classes, :do_constants
end
@@ -30,6 +70,129 @@ class TestRDocParserC < MiniTest::Unit::TestCase
@tempfile.close
end
+ def test_do_attr_rb_attr
+ content = <<-EOF
+void Init_Blah(void) {
+ cBlah = rb_define_class("Blah", rb_cObject);
+
+ /*
+ * This is an accessor
+ */
+ rb_attr(cBlah, rb_intern("accessor"), 1, 1, Qfalse);
+
+ /*
+ * This is a reader
+ */
+ rb_attr(cBlah, rb_intern("reader"), 1, 0, Qfalse);
+
+ /*
+ * This is a writer
+ */
+ rb_attr(cBlah, rb_intern("writer"), 0, 1, Qfalse);
+}
+ EOF
+
+ klass = util_get_class content, 'cBlah'
+
+ attrs = klass.attributes
+ assert_equal 3, attrs.length, attrs.inspect
+
+ accessor = attrs.shift
+ assert_equal 'accessor', accessor.name
+ assert_equal 'RW', accessor.rw
+ assert_equal 'This is an accessor', accessor.comment
+
+ reader = attrs.shift
+ assert_equal 'reader', reader.name
+ assert_equal 'R', reader.rw
+ assert_equal 'This is a reader', reader.comment
+
+ writer = attrs.shift
+ assert_equal 'writer', writer.name
+ assert_equal 'W', writer.rw
+ assert_equal 'This is a writer', writer.comment
+ end
+
+ def test_do_attr_rb_define_attr
+ content = <<-EOF
+void Init_Blah(void) {
+ cBlah = rb_define_class("Blah", rb_cObject);
+
+ /*
+ * This is an accessor
+ */
+ rb_define_attr(cBlah, "accessor", 1, 1);
+}
+ EOF
+
+ klass = util_get_class content, 'cBlah'
+
+ attrs = klass.attributes
+ assert_equal 1, attrs.length, attrs.inspect
+
+ accessor = attrs.shift
+ assert_equal 'accessor', accessor.name
+ assert_equal 'RW', accessor.rw
+ assert_equal 'This is an accessor', accessor.comment
+ end
+
+ def test_do_aliases
+ content = <<-EOF
+/*
+ * This should show up as an alias with documentation
+ */
+VALUE blah(VALUE klass, VALUE year) {
+}
+
+void Init_Blah(void) {
+ cDate = rb_define_class("Date", rb_cObject);
+
+ rb_define_method(cDate, "blah", blah, 1);
+
+ rb_define_alias(cDate, "bleh", "blah");
+}
+ EOF
+
+ klass = util_get_class content, 'cDate'
+
+ methods = klass.method_list
+ assert_equal 2, methods.length
+ assert_equal 'bleh', methods.last.name
+ assert_equal 'blah', methods.last.is_alias_for.name
+ end
+
+ def test_do_aliases_singleton
+ content = <<-EOF
+/*
+ * This should show up as a method with documentation
+ */
+VALUE blah(VALUE klass, VALUE year) {
+}
+
+void Init_Blah(void) {
+ cDate = rb_define_class("Date", rb_cObject);
+ sDate = rb_singleton_class(cDate);
+
+ rb_define_method(sDate, "blah", blah, 1);
+
+ /*
+ * This should show up as an alias
+ */
+ rb_define_alias(sDate, "bleh", "blah");
+}
+ EOF
+
+ klass = util_get_class content, 'cDate'
+
+ methods = klass.method_list
+
+ assert_equal 2, methods.length
+ assert_equal 'bleh', methods.last.name
+ assert methods.last.singleton
+ assert_equal 'blah', methods.last.is_alias_for.name
+ assert_equal 'This should show up as an alias', methods.last.comment
+ end
+
def test_do_classes_boot_class
content = <<-EOF
/* Document-class: Foo
@@ -68,6 +231,17 @@ VALUE cFoo = rb_define_class("Foo", rb_cObject);
assert_equal "this is the Foo class", klass.comment
end
+ def test_do_classes_singleton
+ content = <<-EOF
+VALUE cFoo = rb_define_class("Foo", rb_cObject);
+VALUE cFooS = rb_singleton_class(cFoo);
+ EOF
+
+ util_get_class content, 'cFooS'
+
+ assert_equal 'Foo', @parser.singleton_classes['cFooS']
+ end
+
def test_do_classes_class_under
content = <<-EOF
/* Document-class: Kernel::Foo
@@ -201,6 +375,26 @@ Multiline comment goes here because this comment spans multiple lines.
assert constants.empty?, constants.inspect
end
+ def test_find_alias_comment
+ parser = util_parser ''
+
+ comment = parser.find_alias_comment 'C', '[]', 'index'
+
+ assert_equal '', comment
+
+ parser = util_parser <<-C
+/*
+ * comment
+ */
+
+rb_define_alias(C, "[]", "index");
+ C
+
+ comment = parser.find_alias_comment 'C', '[]', 'index'
+
+ assert_equal "/*\n * comment\n */\n\n", comment
+ end
+
def test_find_class_comment_include
@options.rdoc_include << File.dirname(__FILE__)
@@ -406,6 +600,113 @@ Init_Foo(void) {
assert_equal "a comment for bar", bar.comment
end
+ def test_find_modifiers_call_seq
+ comment = <<-COMMENT
+/* call-seq:
+ * commercial() -> Date <br />
+ * commercial(cwyear, cweek=41, cwday=5, sg=nil) -> Date [ruby 1.8] <br />
+ * commercial(cwyear, cweek=1, cwday=1, sg=nil) -> Date [ruby 1.9]
+ *
+ * If no arguments are given:
+ * * ruby 1.8: returns a +Date+ for 1582-10-15 (the Day of Calendar Reform in
+ * Italy)
+ * * ruby 1.9: returns a +Date+ for julian day 0
+ *
+ * Otherwise, returns a +Date+ for the commercial week year, commercial week,
+ * and commercial week day given. Ignores the 4th argument.
+ */
+
+ COMMENT
+
+ parser = util_parser ''
+ method_obj = RDoc::AnyMethod.new nil, 'blah'
+
+ parser.find_modifiers comment, method_obj
+
+ expected = <<-CALL_SEQ.chomp
+commercial() -> Date <br />
+commercial(cwyear, cweek=41, cwday=5, sg=nil) -> Date [ruby 1.8] <br />
+commercial(cwyear, cweek=1, cwday=1, sg=nil) -> Date [ruby 1.9]
+
+ CALL_SEQ
+
+ assert_equal expected, method_obj.call_seq
+ end
+
+ def test_find_modifiers_nodoc
+ comment = <<-COMMENT
+/* :nodoc:
+ *
+ * Blah
+ */
+
+ COMMENT
+
+ parser = util_parser ''
+ method_obj = RDoc::AnyMethod.new nil, 'blah'
+
+ parser.find_modifiers comment, method_obj
+
+ assert_equal nil, method_obj.document_self
+ end
+
+ def test_find_modifiers_yields
+ comment = <<-COMMENT
+/* :yields: a, b
+ *
+ * Blah
+ */
+
+ COMMENT
+
+ parser = util_parser ''
+ method_obj = RDoc::AnyMethod.new nil, 'blah'
+
+ parser.find_modifiers comment, method_obj
+
+ assert_equal 'a, b', method_obj.block_params
+
+ expected = <<-EXPECTED
+/*
+ *
+ * Blah
+ */
+
+ EXPECTED
+
+ assert_equal expected, comment
+ end
+
+ def test_handle_method
+ parser = util_parser "Document-method: BasicObject#==\n blah */"
+
+ parser.handle_method 'method', 'rb_cBasicObject', '==', 'rb_obj_equal', 1
+
+ bo = @top_level.find_module_named 'BasicObject'
+
+ assert_equal 1, bo.method_list.length
+
+ equals2 = bo.method_list.first
+
+ assert_equal '==', equals2.name
+ end
+
+ def test_handle_method_initialize
+ parser = util_parser "Document-method: BasicObject::new\n blah */"
+
+ parser.handle_method('private_method', 'rb_cBasicObject',
+ 'initialize', 'rb_obj_dummy', -1)
+
+ bo = @top_level.find_module_named 'BasicObject'
+
+ assert_equal 1, bo.method_list.length
+
+ new = bo.method_list.first
+
+ assert_equal 'new', new.name
+ assert_equal :public, new.visibility
+ end
+
def test_look_for_directives_in
parser = util_parser ''
@@ -442,6 +743,7 @@ Init_IO(void) {
read_method = klass.method_list.first
assert_equal "read", read_method.name
assert_equal "Method Comment! ", read_method.comment
+ assert read_method.singleton
end
def test_define_method_private
@@ -472,6 +774,65 @@ Init_IO(void) {
assert_equal "Method Comment! ", read_method.comment
end
+ def test_define_method_private_singleton
+ content = <<-EOF
+/*Method Comment! */
+static VALUE
+rb_io_s_read(argc, argv, io)
+ int argc;
+ VALUE *argv;
+ VALUE io;
+{
+}
+
+void
+Init_IO(void) {
+ /*
+ * a comment for class Foo on rb_define_class
+ */
+ VALUE rb_cIO = rb_define_class("IO", rb_cObject);
+ VALUE rb_cIO_s = rb_singleton_class(rb_cIO);
+ rb_define_private_method(rb_cIO_s, "read", rb_io_s_read, -1);
+}
+ EOF
+
+ klass = util_get_class content, 'rb_cIO'
+ read_method = klass.method_list.first
+ assert_equal "read", read_method.name
+ assert_equal "Method Comment! ", read_method.comment
+ assert_equal :private, read_method.visibility
+ assert read_method.singleton
+ end
+
+ def test_define_method_singleton
+ content = <<-EOF
+/*Method Comment! */
+static VALUE
+rb_io_s_read(argc, argv, io)
+ int argc;
+ VALUE *argv;
+ VALUE io;
+{
+}
+
+void
+Init_IO(void) {
+ /*
+ * a comment for class Foo on rb_define_class
+ */
+ VALUE rb_cIO = rb_define_class("IO", rb_cObject);
+ VALUE rb_cIO_s = rb_singleton_class(rb_cIO);
+ rb_define_method(rb_cIO_s, "read", rb_io_s_read, -1);
+}
+ EOF
+
+ klass = util_get_class content, 'rb_cIO'
+ read_method = klass.method_list.first
+ assert_equal "read", read_method.name
+ assert_equal "Method Comment! ", read_method.comment
+ assert read_method.singleton
+ end
+
def util_get_class(content, name)
@parser = util_parser content
@parser.scan
@@ -484,4 +845,3 @@ Init_IO(void) {
end
-MiniTest::Unit.autorun
diff --git a/test/rdoc/test_rdoc_parser_perl.rb b/test/rdoc/test_rdoc_parser_perl.rb
deleted file mode 100644
index ce4e7d2b65..0000000000
--- a/test/rdoc/test_rdoc_parser_perl.rb
+++ /dev/null
@@ -1,73 +0,0 @@
-require 'stringio'
-require 'tempfile'
-require 'rubygems'
-require 'minitest/autorun'
-require 'rdoc/options'
-require 'rdoc/parser/perl'
-
-class TestRdocParserPerlPOD < MiniTest::Unit::TestCase
-
- def setup
- @tempfile = Tempfile.new self.class.name
- filename = @tempfile.path
-
- @top_level = RDoc::TopLevel.new filename
- @fn = filename
- @options = RDoc::Options.new
- @stats = RDoc::Stats.new 0
- end
-
- def teardown
- @tempfile.close
- end
-
- def test_uncommented_perl
- content = <<-EOF
-while (<>) {
- tr/a-z/A-Z;
- print
-}
- EOF
-
- comment = util_get_comment content
- assert_equal "", comment
- end
-
- def test_perl_without_pod
- content = <<-EOF
-#!/usr/local/bin/perl
-#
-#This is a pointless perl program because it does -p.
-#
-while(<>) {print;}:
- EOF
-
- comment = util_get_comment content
- assert_equal "", comment
- end
-
- def test_simple_pod_no_structure
- content = <<-EOF
-=begin pod
-
-This just contains plain old documentation
-
-=end
- EOF
- comment = util_get_comment content
- assert_equal 'This just contains plain old documentation', comment
- end
-
- # Get the comment of the @top_level when it has processed the input.
- def util_get_comment(content)
- parser = util_parser content
- parser.scan.comment
- end
-
- # create a new parser with the supplied content.
- def util_parser(content)
- RDoc::Parser::PerlPOD.new @top_level, @fn, content, @options, @stats
- end
-
-end
-
diff --git a/test/rdoc/test_rdoc_parser_ruby.rb b/test/rdoc/test_rdoc_parser_ruby.rb
index 33ffded723..f2fa18c67b 100644
--- a/test/rdoc/test_rdoc_parser_ruby.rb
+++ b/test/rdoc/test_rdoc_parser_ruby.rb
@@ -22,6 +22,8 @@ class TestRDocParserRuby < MiniTest::Unit::TestCase
util_top_level
@options = RDoc::Options.new
@options.quiet = true
+ @options.option_parser = OptionParser.new
+
@stats = RDoc::Stats.new 0
end
@@ -30,6 +32,30 @@ class TestRDocParserRuby < MiniTest::Unit::TestCase
@tempfile2.close
end
+ def test_get_symbol_or_name
+ util_parser "* & | + 5 / 4"
+
+ assert_equal '*', @parser.get_symbol_or_name
+
+ @parser.skip_tkspace
+
+ assert_equal '&', @parser.get_symbol_or_name
+
+ @parser.skip_tkspace
+
+ assert_equal '|', @parser.get_symbol_or_name
+
+ @parser.skip_tkspace
+
+ assert_equal '+', @parser.get_symbol_or_name
+
+ @parser.skip_tkspace
+ @parser.get_tk
+ @parser.skip_tkspace
+
+ assert_equal '/', @parser.get_symbol_or_name
+ end
+
def test_look_for_directives_in_attr
util_parser ""
@@ -52,6 +78,61 @@ class TestRDocParserRuby < MiniTest::Unit::TestCase
assert_equal "# :attr_writer: my_method\n", comment
end
+ def test_remove_private_comments
+ util_parser ''
+
+ comment = <<-EOS
+# This is text
+#--
+# this is private
+ EOS
+
+ expected = <<-EOS
+# This is text
+ EOS
+
+ @parser.remove_private_comments(comment)
+
+ assert_equal expected, comment
+ end
+
+ def test_remove_private_comments_rule
+ util_parser ''
+
+ comment = <<-EOS
+# This is text with a rule:
+# ---
+# this is also text
+ EOS
+
+ expected = comment.dup
+
+ @parser.remove_private_comments(comment)
+
+ assert_equal expected, comment
+ end
+
+ def test_remove_private_comments_toggle
+ util_parser ''
+
+ comment = <<-EOS
+# This is text
+#--
+# this is private
+#++
+# This is text again.
+ EOS
+
+ expected = <<-EOS
+# This is text
+# This is text again.
+ EOS
+
+ @parser.remove_private_comments(comment)
+
+ assert_equal expected, comment
+ end
+
def test_look_for_directives_in_commented
util_parser ""
@@ -70,9 +151,9 @@ class TestRDocParserRuby < MiniTest::Unit::TestCase
def test_look_for_directives_in_enddoc
util_parser ""
- assert_throws :enddoc do
- @parser.look_for_directives_in @top_level, "# :enddoc:\n"
- end
+ @parser.look_for_directives_in @top_level, "# :enddoc:\n"
+
+ assert @top_level.done_documenting
end
def test_look_for_directives_in_main
@@ -105,13 +186,11 @@ class TestRDocParserRuby < MiniTest::Unit::TestCase
@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
@@ -166,10 +245,29 @@ class TestRDocParserRuby < MiniTest::Unit::TestCase
alas = @parser.parse_alias klass, RDoc::Parser::Ruby::NORMAL, tk, 'comment'
- assert_equal 'bar', alas.old_name
- assert_equal 'next=', alas.new_name
- assert_equal klass, alas.parent
- assert_equal 'comment', alas.comment
+ assert_equal 'bar', alas.old_name
+ assert_equal 'next=', alas.new_name
+ assert_equal klass, alas.parent
+ assert_equal 'comment', alas.comment
+ assert_equal @top_level, alas.file
+ end
+
+ def test_parse_alias_singleton
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ util_parser "alias :next= :bar"
+
+ tk = @parser.get_tk
+
+ alas = @parser.parse_alias klass, RDoc::Parser::Ruby::SINGLE, tk, 'comment'
+
+ assert_equal 'bar', alas.old_name
+ assert_equal 'next=', alas.new_name
+ assert_equal klass, alas.parent
+ assert_equal 'comment', alas.comment
+ assert_equal @top_level, alas.file
+ assert alas.singleton
end
def test_parse_alias_meta
@@ -202,6 +300,7 @@ class TestRDocParserRuby < MiniTest::Unit::TestCase
foo = klass.attributes.first
assert_equal 'foo', foo.name
assert_equal 'my attr', foo.comment
+ assert_equal @top_level, foo.file
end
def test_parse_attr_accessor
@@ -222,6 +321,7 @@ class TestRDocParserRuby < MiniTest::Unit::TestCase
assert_equal 'foo', foo.name
assert_equal 'RW', foo.rw
assert_equal 'my attr', foo.comment
+ assert_equal @top_level, foo.file
bar = klass.attributes.last
assert_equal 'bar', bar.name
@@ -229,6 +329,21 @@ class TestRDocParserRuby < MiniTest::Unit::TestCase
assert_equal 'my attr', bar.comment
end
+ def test_parse_attr_accessor_nodoc
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ comment = "##\n# my attr\n"
+
+ util_parser "attr_accessor :foo, :bar # :nodoc:"
+
+ tk = @parser.get_tk
+
+ @parser.parse_attr_accessor klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+
+ assert_equal 0, klass.attributes.length
+ end
+
def test_parse_attr_accessor_writer
klass = RDoc::NormalClass.new 'Foo'
klass.parent = @top_level
@@ -247,6 +362,7 @@ class TestRDocParserRuby < MiniTest::Unit::TestCase
assert_equal 'foo', foo.name
assert_equal 'W', foo.rw
assert_equal "my attr", foo.comment
+ assert_equal @top_level, foo.file
bar = klass.attributes.last
assert_equal 'bar', bar.name
@@ -271,6 +387,7 @@ class TestRDocParserRuby < MiniTest::Unit::TestCase
assert_equal 'foo', foo.name
assert_equal 'RW', foo.rw
assert_equal "my method", foo.comment
+ assert_equal @top_level, foo.file
end
def test_parse_meta_attr_accessor
@@ -290,6 +407,7 @@ class TestRDocParserRuby < MiniTest::Unit::TestCase
assert_equal 'foo', foo.name
assert_equal 'RW', foo.rw
assert_equal 'my method', foo.comment
+ assert_equal @top_level, foo.file
end
def test_parse_meta_attr_named
@@ -309,6 +427,7 @@ class TestRDocParserRuby < MiniTest::Unit::TestCase
assert_equal 'foo', foo.name
assert_equal 'RW', foo.rw
assert_equal 'my method', foo.comment
+ assert_equal @top_level, foo.file
end
def test_parse_meta_attr_reader
@@ -327,6 +446,7 @@ class TestRDocParserRuby < MiniTest::Unit::TestCase
assert_equal 'foo', foo.name
assert_equal 'R', foo.rw
assert_equal 'my method', foo.comment
+ assert_equal @top_level, foo.file
end
def test_parse_meta_attr_writer
@@ -345,12 +465,13 @@ class TestRDocParserRuby < MiniTest::Unit::TestCase
assert_equal 'foo', foo.name
assert_equal 'W', foo.rw
assert_equal "my method", foo.comment
+ assert_equal @top_level, foo.file
end
def test_parse_class
comment = "##\n# my method\n"
- util_parser 'class Foo; end'
+ util_parser "class Foo\nend"
tk = @parser.get_tk
@@ -379,11 +500,12 @@ end
blah = foo.method_list.first
assert_equal 'Foo#blah', blah.full_name
+ assert_equal @top_level, blah.file
end
def test_parse_class_nested_superclass
- foo = RDoc::NormalModule.new 'Foo'
- foo.parent = @top_level
+ util_top_level
+ foo = @top_level.add_module RDoc::NormalModule, 'Foo'
util_parser "class Bar < Super\nend"
@@ -398,7 +520,7 @@ end
def test_parse_module
comment = "##\n# my module\n"
- util_parser 'module Foo; end'
+ util_parser "module Foo\nend"
tk = @parser.get_tk
@@ -430,6 +552,8 @@ class A
class << B
end
class << d = Object.new
+ def foo; end
+ alias bar foo
end
end
CODE
@@ -439,7 +563,40 @@ end
@parser.parse_class @top_level, false, @parser.get_tk, ''
assert_equal %w[A], RDoc::TopLevel.classes.map { |c| c.full_name }
- assert_equal %w[A::B], RDoc::TopLevel.modules.map { |c| c.full_name }
+ assert_equal %w[A::B A::d], RDoc::TopLevel.modules.map { |c| c.full_name }
+
+ # make sure method/alias was not added to enclosing class/module
+ a = RDoc::TopLevel.all_classes_hash['A']
+ assert_empty a.method_list
+
+ # make sure non-constant-named module will be removed from documentation
+ d = RDoc::TopLevel.all_modules_hash['A::d']
+ assert d.remove_from_documentation?
+
+ end
+
+ # TODO this is really a Context#add_class test
+ def test_parse_class_object
+ code = <<-CODE
+module A
+ class B
+ end
+ class Object
+ end
+ class C < Object
+ end
+end
+ CODE
+
+ util_parser code
+
+ @parser.parse_module @top_level, false, @parser.get_tk, ''
+
+ assert_equal %w[A], RDoc::TopLevel.modules.map { |c| c.full_name }
+ assert_equal %w[A::B A::C A::Object], RDoc::TopLevel.classes.map { |c| c.full_name }.sort
+ assert_equal 'Object', RDoc::TopLevel.classes_hash['A::B'].superclass
+ assert_equal 'Object', RDoc::TopLevel.classes_hash['A::Object'].superclass
+ assert_equal 'A::Object', RDoc::TopLevel.classes_hash['A::C'].superclass.full_name
end
def test_parse_class_mistaken_for_module
@@ -447,7 +604,7 @@ end
# before Foo::Bar is encountered), but RDoc might encounter Foo::Bar
# before Foo if they live in different files.
- code = <<-EOF
+ code = <<-RUBY
class Foo::Bar
end
@@ -456,7 +613,7 @@ end
class Foo
end
- EOF
+ RUBY
util_parser code
@@ -542,18 +699,20 @@ EOF
@parser.parse_comment klass, tk, comment
foo = klass.attributes.first
- assert_equal 'foo', foo.name
- assert_equal 'RW', foo.rw
- assert_equal 'my attr', foo.comment
+ assert_equal 'foo', foo.name
+ assert_equal 'RW', foo.rw
+ assert_equal 'my attr', foo.comment
+ assert_equal @top_level, foo.file
+
+ assert_equal nil, foo.viewer
+ assert_equal true, foo.document_children
+ assert_equal true, foo.document_self
+ assert_equal false, foo.done_documenting
+ assert_equal false, foo.force_documentation
+ assert_equal klass, foo.parent
+ assert_equal :public, foo.visibility
+ assert_equal "\n", foo.text
- assert_equal nil, foo.viewer
- assert_equal true, foo.document_children
- assert_equal true, foo.document_self
- assert_equal false, foo.done_documenting
- assert_equal false, foo.force_documentation
- assert_equal klass, foo.parent
- assert_equal :public, foo.visibility
- assert_equal "\n", foo.text
assert_equal klass.current_section, foo.section
end
@@ -570,8 +729,9 @@ EOF
@parser.parse_comment klass, tk, comment
foo = klass.method_list.first
- assert_equal 'foo', foo.name
- assert_equal 'my method', foo.comment
+ assert_equal 'foo', foo.name
+ assert_equal 'my method', foo.comment
+ assert_equal @top_level, foo.file
assert_equal [], foo.aliases
assert_equal nil, foo.block_params
@@ -599,9 +759,23 @@ EOF
assert_equal stream, foo.token_stream
end
+ def test_parse_constant_attrasgn
+ util_top_level
+
+ klass = @top_level.add_class RDoc::NormalClass, 'Foo'
+
+ util_parser "A[k] = v"
+
+ tk = @parser.get_tk
+
+ @parser.parse_constant klass, tk, ''
+
+ assert klass.constants.empty?
+ end
+
def test_parse_constant_alias
- klass = RDoc::NormalClass.new 'Foo'
- klass.parent = @top_level
+ util_top_level
+ klass = @top_level.add_class RDoc::NormalClass, 'Foo'
cB = klass.add_class RDoc::NormalClass, 'B'
util_parser "A = B"
@@ -642,8 +816,9 @@ EOF
@parser.parse_meta_method klass, RDoc::Parser::Ruby::NORMAL, tk, comment
foo = klass.method_list.first
- assert_equal 'foo', foo.name
+ assert_equal 'foo', foo.name
assert_equal 'my method', foo.comment
+ assert_equal @top_level, foo.file
assert_equal [], foo.aliases
assert_equal nil, foo.block_params
@@ -691,8 +866,9 @@ EOF
@parser.parse_meta_method klass, RDoc::Parser::Ruby::NORMAL, tk, comment
foo = klass.method_list.first
- assert_equal 'woo_hoo!', foo.name
+ assert_equal 'woo_hoo!', foo.name
assert_equal 'my method', foo.comment
+ assert_equal @top_level, foo.file
end
def test_parse_meta_method_singleton
@@ -711,6 +887,7 @@ EOF
assert_equal 'foo', foo.name
assert_equal true, foo.singleton, 'singleton method'
assert_equal 'my method', foo.comment
+ assert_equal @top_level, foo.file
end
def test_parse_meta_method_singleton_name
@@ -729,6 +906,7 @@ EOF
assert_equal 'woo_hoo!', foo.name
assert_equal true, foo.singleton, 'singleton method'
assert_equal 'my method', foo.comment
+ assert_equal @top_level, foo.file
end
def test_parse_meta_method_string_name
@@ -744,6 +922,7 @@ EOF
foo = klass.method_list.first
assert_equal 'foo', foo.name
assert_equal 'my method', foo.comment
+ assert_equal @top_level, foo.file
end
def test_parse_meta_method_unknown
@@ -759,6 +938,7 @@ EOF
foo = klass.method_list.first
assert_equal 'unknown', foo.name
assert_equal 'my method', foo.comment
+ assert_equal @top_level, foo.file
end
def test_parse_method
@@ -774,8 +954,9 @@ EOF
@parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, comment
foo = klass.method_list.first
- assert_equal 'foo', foo.name
- assert_equal 'my method', foo.comment
+ assert_equal 'foo', foo.name
+ assert_equal 'my method', foo.comment
+ assert_equal @top_level, foo.file
assert_equal [], foo.aliases
assert_equal nil, foo.block_params
@@ -826,39 +1007,53 @@ EOF
assert klass.aliases.empty?
end
- def test_parse_method_utf8
+ def test_parse_method_false
+ util_parser "def false.foo() :bar end"
+
+ tk = @parser.get_tk
+
+ @parser.parse_method @top_level, RDoc::Parser::Ruby::NORMAL, tk, ''
+
+ klass = RDoc::TopLevel.find_class_named 'FalseClass'
+
+ foo = klass.method_list.first
+ assert_equal 'foo', foo.name
+ end
+
+ def test_parse_method_funky
klass = RDoc::NormalClass.new 'Foo'
klass.parent = @top_level
- comment = "##\n# my method\n"
+ util_parser "def (blah).foo() :bar end"
- method = "def ω() end"
+ tk = @parser.get_tk
- assert_equal Encoding::UTF_8, method.encoding if defined? ::Encoding
+ @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, ''
- util_parser method
+ assert klass.method_list.empty?
+ end
+
+ def test_parse_method_gvar
+ util_parser "def $stdout.foo() :bar end"
tk = @parser.get_tk
- @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+ @parser.parse_method @top_level, RDoc::Parser::Ruby::NORMAL, tk, ''
- omega = klass.method_list.first
- assert_equal "def \317\211", omega.text
+ assert @top_level.method_list.empty?
end
- def test_parse_method_funky
+ def test_parse_method_internal_gvar
klass = RDoc::NormalClass.new 'Foo'
klass.parent = @top_level
- comment = "##\n# my method\n"
-
- util_parser "def (blah).foo() :bar end"
+ util_parser "def foo() def $blah.bar() end end"
tk = @parser.get_tk
- @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+ @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, ''
- assert klass.method_list.empty?
+ assert_equal 1, klass.method_list.length
end
def test_parse_method_internal_ivar
@@ -887,33 +1082,43 @@ EOF
assert_equal 1, klass.method_list.length
end
+ def test_parse_method_nil
+ util_parser "def nil.foo() :bar end"
+
+ tk = @parser.get_tk
+
+ @parser.parse_method @top_level, RDoc::Parser::Ruby::NORMAL, tk, ''
+
+ klass = RDoc::TopLevel.find_class_named 'NilClass'
+
+ foo = klass.method_list.first
+ assert_equal 'foo', foo.name
+ end
+
def test_parse_method_no_parens
klass = RDoc::NormalClass.new 'Foo'
klass.parent = @top_level
- comment = "##\n# my method\n"
-
- util_parser "def foo arg1, arg2\nend"
+ util_parser "def foo arg1, arg2 = {}\nend"
tk = @parser.get_tk
- @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+ @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, ''
foo = klass.method_list.first
- assert_equal '(arg1, arg2)', foo.params
+ assert_equal '(arg1, arg2 = {})', foo.params
+ assert_equal @top_level, foo.file
end
def test_parse_method_parameters_comment
klass = RDoc::NormalClass.new 'Foo'
klass.parent = @top_level
- comment = "##\n# my method\n"
-
util_parser "def foo arg1, arg2 # some useful comment\nend"
tk = @parser.get_tk
- @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+ @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, ''
foo = klass.method_list.first
assert_equal '(arg1, arg2)', foo.params
@@ -923,13 +1128,11 @@ EOF
klass = RDoc::NormalClass.new 'Foo'
klass.parent = @top_level
- comment = "##\n# my method\n"
-
util_parser "def foo arg1, arg2, # some useful comment\narg3\nend"
tk = @parser.get_tk
- @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+ @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, ''
foo = klass.method_list.first
assert_equal '(arg1, arg2, arg3)', foo.params
@@ -938,18 +1141,17 @@ EOF
def test_parse_method_toplevel
klass = @top_level
- comment = "##\n# my method\n"
-
util_parser "def foo arg1, arg2\nend"
tk = @parser.get_tk
- @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, comment
+ @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, ''
object = RDoc::TopLevel.find_class_named 'Object'
foo = object.method_list.first
assert_equal 'Object#foo', foo.full_name
+ assert_equal @top_level, foo.file
end
def test_parse_method_toplevel_class
@@ -967,9 +1169,39 @@ EOF
assert_equal 'Object::foo', foo.full_name
end
- def test_parse_statements_class_if
- comment = "##\n# my method\n"
+ def test_parse_method_true
+ util_parser "def true.foo() :bar end"
+
+ tk = @parser.get_tk
+
+ @parser.parse_method @top_level, RDoc::Parser::Ruby::NORMAL, tk, ''
+
+ klass = RDoc::TopLevel.find_class_named 'TrueClass'
+
+ foo = klass.method_list.first
+ assert_equal 'foo', foo.name
+ end
+
+ def test_parse_method_utf8
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ method = "def ω() end"
+ assert_equal Encoding::UTF_8, method.encoding if
+ Object.const_defined? :Encoding
+
+ util_parser method
+
+ tk = @parser.get_tk
+
+ @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, ''
+
+ omega = klass.method_list.first
+ assert_equal "def \317\211", omega.text
+ end
+
+ def test_parse_statements_class_if
util_parser <<-CODE
module Foo
X = if TRUE then
@@ -1024,7 +1256,12 @@ end
end
def test_parse_statements_identifier_alias_method
- content = "class Foo def foo() end; alias_method :foo2, :foo end"
+ content = <<-RUBY
+class Foo
+ def foo() end
+ alias_method :foo2, :foo
+end
+ RUBY
util_parser content
@@ -1078,10 +1315,25 @@ EOF
assert_equal 'foo4', foo4.name
assert_equal 'foo', foo4.is_alias_for.name
- assert_equal 'unknown', @top_level.classes.first.aliases[0].old_name
+ assert_equal 'unknown', @top_level.classes.first.external_aliases[0].old_name
end
def test_parse_statements_identifier_constant
+
+ sixth_constant = <<-EOF
+Class.new do
+ rule :file do
+ all(x, y, z) {
+ def value
+ find(:require).each {|r| require r.value }
+ find(:grammar).map {|g| g.value }
+ end
+ def min; end
+ }
+ end
+end
+ EOF
+
content = <<-EOF
class Foo
FIRST_CONSTANT = 5
@@ -1103,6 +1355,10 @@ class Foo
end
FIFTH_CONSTANT = SECOND_CONSTANT.map { |element| element + 1 }
+
+ SIXTH_CONSTANT = #{sixth_constant}
+
+ SEVENTH_CONSTANT = proc { |i| begin i end }
end
EOF
@@ -1115,26 +1371,43 @@ EOF
constant = constants[0]
assert_equal 'FIRST_CONSTANT', constant.name
assert_equal '5', constant.value
+ assert_equal @top_level, constant.file
constant = constants[1]
assert_equal 'SECOND_CONSTANT', constant.name
- assert_equal '[ 1, 2, 3 ]', constant.value
+ assert_equal "[\n1,\n2,\n3\n]", constant.value
+ assert_equal @top_level, constant.file
constant = constants[2]
assert_equal 'THIRD_CONSTANT', constant.name
- assert_equal "{ :foo => 'bar', :x => 'y' }", constant.value
+ assert_equal "{\n:foo => 'bar',\n:x => 'y'\n}", constant.value
+ assert_equal @top_level, constant.file
constant = constants[3]
assert_equal 'FOURTH_CONSTANT', constant.name
- assert_equal 'SECOND_CONSTANT.map do |element| element + 1 element + 2 end', constant.value
+ assert_equal "SECOND_CONSTANT.map do |element|\nelement + 1\nelement + 2\nend", constant.value
+ assert_equal @top_level, constant.file
- constant = constants.last
+ constant = constants[4]
assert_equal 'FIFTH_CONSTANT', constant.name
assert_equal 'SECOND_CONSTANT.map { |element| element + 1 }', constant.value
+ assert_equal @top_level, constant.file
+
+ # TODO: parse as class
+ constant = constants[5]
+ assert_equal 'SIXTH_CONSTANT', constant.name
+ assert_equal sixth_constant.lines.map(&:strip).join("\n"), constant.value
+ assert_equal @top_level, constant.file
+
+ # TODO: parse as method
+ constant = constants[6]
+ assert_equal 'SEVENTH_CONSTANT', constant.name
+ assert_equal "proc { |i| begin i end }", constant.value
+ assert_equal @top_level, constant.file
end
def test_parse_statements_identifier_attr
- content = "class Foo; attr :foo; end"
+ content = "class Foo\nattr :foo\nend"
util_parser content
@@ -1146,7 +1419,7 @@ EOF
end
def test_parse_statements_identifier_attr_accessor
- content = "class Foo; attr_accessor :foo; end"
+ content = "class Foo\nattr_accessor :foo\nend"
util_parser content
@@ -1158,7 +1431,7 @@ EOF
end
def test_parse_statements_identifier_include
- content = "class Foo; include Bar; end"
+ content = "class Foo\ninclude Bar\nend"
util_parser content
@@ -1170,24 +1443,24 @@ EOF
end
def test_parse_statements_identifier_module_function
- content = "module Foo def foo() end; module_function :foo; end"
+ content = "module Foo\ndef foo() end\nmodule_function :foo\nend"
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 'foo', foo.name, 'instance method name'
assert_equal :private, foo.visibility, 'instance method visibility'
- assert_equal false, foo.singleton, 'instance method singleton'
+ assert_equal false, foo.singleton, 'instance method singleton'
- assert_equal 'foo', s_foo.name, 'module function name'
+ 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'
+ assert_equal true, s_foo.singleton, 'module function singleton'
end
def test_parse_statements_identifier_private
- content = "class Foo private; def foo() end end"
+ content = "class Foo\nprivate\ndef foo() end\nend"
util_parser content
@@ -1235,6 +1508,24 @@ end
assert_equal 'A#b', m_b.full_name
end
+ def test_parse_symbol_in_arg
+ util_parser ':blah "blah" "#{blah}" blah'
+
+ assert_equal 'blah', @parser.parse_symbol_in_arg
+
+ @parser.skip_tkspace
+
+ assert_equal 'blah', @parser.parse_symbol_in_arg
+
+ @parser.skip_tkspace
+
+ assert_equal nil, @parser.parse_symbol_in_arg
+
+ @parser.skip_tkspace
+
+ assert_equal nil, @parser.parse_symbol_in_arg
+ end
+
def test_parse_top_level_statements_alias_method
content = <<-CONTENT
class A
@@ -1252,6 +1543,20 @@ end
@parser.parse_statements @top_level
end
+ def test_parse_yield_in_braces_with_parens
+ klass = RDoc::NormalClass.new 'Foo'
+ klass.parent = @top_level
+
+ util_parser "def foo\nn.times { |i| yield nth(i) }\nend"
+
+ tk = @parser.get_tk
+
+ @parser.parse_method klass, RDoc::Parser::Ruby::NORMAL, tk, ''
+
+ foo = klass.method_list.first
+ assert_equal 'nth(i)', foo.block_params
+ end
+
def test_sanity_integer
util_parser '1'
assert_equal '1', @parser.get_tk.text
@@ -1272,7 +1577,6 @@ end
# If you're writing code like this you're doing it wrong
def test_sanity_interpolation_crazy
- last_tk = nil
util_parser '"#{"#{"a")}" if b}"'
assert_equal '"#{"#{"a")}" if b}"', @parser.get_tk.text
@@ -1280,7 +1584,6 @@ end
end
def test_sanity_interpolation_curly
- last_tk = nil
util_parser '%{ #{} }'
assert_equal '%{ #{} }', @parser.get_tk.text
@@ -1290,13 +1593,39 @@ end
def test_sanity_interpolation_format
util_parser '"#{stftime("%m-%d")}"'
- while tk = @parser.get_tk do end
+ while @parser.get_tk do end
end
def test_sanity_symbol_interpolation
util_parser ':"#{bar}="'
- while tk = @parser.get_tk do end
+ while @parser.get_tk do end
+ end
+
+ def test_stopdoc_after_comment
+
+ util_parser <<-EOS
+ module Bar
+ # hello
+ module Foo
+ # :stopdoc:
+ end
+ # there
+ class Baz
+ # :stopdoc:
+ end
+ end
+ EOS
+
+ @parser.parse_statements @top_level, RDoc::Parser::Ruby::NORMAL, nil, ''
+
+ foo = @top_level.modules.first.modules.first
+ assert_equal 'Foo', foo.name
+ assert_equal 'hello', foo.comment
+
+ baz = @top_level.modules.first.classes.first
+ assert_equal 'Baz', baz.name
+ assert_equal 'there', baz.comment
end
def tk(klass, line, char, name, text)
diff --git a/test/rdoc/test_rdoc_parser_simple.rb b/test/rdoc/test_rdoc_parser_simple.rb
index d09cced5a4..8cedfaa2fc 100644
--- a/test/rdoc/test_rdoc_parser_simple.rb
+++ b/test/rdoc/test_rdoc_parser_simple.rb
@@ -41,7 +41,6 @@ contents of a string.
parser.scan
expected = <<-TEXT.strip
-
Regular expressions (<i>regexp</i>s) are patterns which describe the
contents of a string.
TEXT
@@ -49,6 +48,31 @@ contents of a string.
assert_equal expected, @top_level.comment
end
+ # RDoc stops processing comments if it finds a comment line CONTAINING
+ # '<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 '<tt>#++</tt>'.
+ #
+ # I've seen guys that comment their code like this:
+ # # This method....
+ # #-----------------
+ # def method
+ #
+ # => either we do it only in ruby code, or we require the leading #
+ # (to avoid conflict with rules).
+ #
+ # TODO: require the leading #, to provide the feature in simple text files.
+ # Note: in ruby & C code, we require '#--' & '#++' or '*--' & '*++',
+ # to allow rules:
+ #
+ # # this is a comment
+ # #---
+ # # private text
+ # #+++
+ # # this is a rule:
+ # # ---
+
def test_remove_private_comments
parser = util_parser ''
text = "foo\n\n--\nbar\n++\n\nbaz\n"
diff --git a/test/rdoc/test_rdoc_rdoc.rb b/test/rdoc/test_rdoc_rdoc.rb
index b02ef2ae1a..d65e2f3427 100644
--- a/test/rdoc/test_rdoc_rdoc.rb
+++ b/test/rdoc/test_rdoc_rdoc.rb
@@ -1,15 +1,21 @@
-require 'tempfile'
-require 'tmpdir'
require 'rubygems'
require 'minitest/autorun'
require 'rdoc/rdoc'
+require 'fileutils'
+require 'tempfile'
+require 'tmpdir'
+
class TestRDocRDoc < MiniTest::Unit::TestCase
def setup
@rdoc = RDoc::RDoc.new
+ @rdoc.options = RDoc::Options.new
+
+ @stats = RDoc::Stats.new 0, 0
+ @rdoc.instance_variable_set :@stats, @stats
+
@tempfile = Tempfile.new 'test_rdoc_rdoc'
- @tempfile.binmode
end
def teardown
@@ -39,48 +45,6 @@ class TestRDocRDoc < MiniTest::Unit::TestCase
assert_empty files
end
- def test_read_file_contents
- @tempfile.write "hi everybody"
- @tempfile.flush
-
- assert_equal "hi everybody", @rdoc.read_file_contents(@tempfile.path)
- end
-
- def test_read_file_contents_encoding
- skip "Encoding not implemented" unless defined? ::Encoding
-
- @tempfile.write "# coding: utf-8\nhi everybody"
- @tempfile.flush
-
- contents = @rdoc.read_file_contents @tempfile.path
- assert_equal "# coding: utf-8\nhi everybody", contents
- assert_equal Encoding::UTF_8, contents.encoding
- end
-
- def test_read_file_contents_encoding_fancy
- skip "Encoding not implemented" unless defined? ::Encoding
-
- @tempfile.write "# -*- coding: utf-8; fill-column: 74 -*-\nhi everybody"
- @tempfile.flush
-
- contents = @rdoc.read_file_contents @tempfile.path
- assert_equal("# -*- coding: utf-8; fill-column: 74 -*-\nhi everybody",
- contents)
- assert_equal Encoding::UTF_8, contents.encoding
- end
-
- def test_read_file_contents_encoding_with_signature
- skip "Encoding not implemented" unless defined? ::Encoding
-
- @tempfile.write "\xEF\xBB\xBF""hi everybody"
- @tempfile.flush
-
- bug3360 = '[ruby-dev:41452]'
- contents = @rdoc.read_file_contents @tempfile.path
- assert_equal "hi everybody", contents, bug3360
- assert_equal Encoding::UTF_8, contents.encoding, bug3360
- end
-
def test_remove_unparsable
file_list = %w[
blah.class
@@ -108,6 +72,20 @@ class TestRDocRDoc < MiniTest::Unit::TestCase
}
end
+ def test_setup_output_dir_dry_run
+ skip "No Dir::mktmpdir, upgrade your ruby" unless Dir.respond_to? :mktmpdir
+
+ @rdoc.options.dry_run = true
+
+ Dir.mktmpdir do |d|
+ path = File.join d, 'testdir'
+
+ @rdoc.setup_output_dir path, false
+
+ refute File.exist? path
+ end
+ end
+
def test_setup_output_dir_exists
skip "No Dir::mktmpdir, upgrade your ruby" unless Dir.respond_to? :mktmpdir
@@ -161,5 +139,26 @@ class TestRDocRDoc < MiniTest::Unit::TestCase
end
end
+ def test_update_output_dir
+ skip "No Dir::mktmpdir, upgrade your ruby" unless Dir.respond_to? :mktmpdir
+
+ Dir.mktmpdir do |d|
+ @rdoc.update_output_dir d, Time.now, {}
+
+ assert File.exist? "#{d}/created.rid"
+ end
+ end
+
+ def test_update_output_dir_dry_run
+ skip "No Dir::mktmpdir, upgrade your ruby" unless Dir.respond_to? :mktmpdir
+
+ Dir.mktmpdir do |d|
+ @rdoc.options.dry_run = true
+ @rdoc.update_output_dir d, Time.now, {}
+
+ refute File.exist? "#{d}/created.rid"
+ end
+ end
+
end
diff --git a/test/rdoc/test_rdoc_ri_driver.rb b/test/rdoc/test_rdoc_ri_driver.rb
index 9ff89077b6..75ecfefe8e 100644
--- a/test/rdoc/test_rdoc_ri_driver.rb
+++ b/test/rdoc/test_rdoc_ri_driver.rb
@@ -53,7 +53,7 @@ class TestRDocRIDriver < MiniTest::Unit::TestCase
def test_self_dump
util_store
- out, err = capture_io do
+ out, = capture_io do
RDoc::RI::Driver.dump @store.cache_path
end
@@ -83,8 +83,8 @@ class TestRDocRIDriver < MiniTest::Unit::TestCase
expected = @RM::Document.new(
@RM::Rule.new(1),
@RM::Paragraph.new('Also found in:'),
- @RM::Verbatim.new(' ', 'ruby core', "\n",
- ' ', '~/.ri', "\n"))
+ @RM::Verbatim.new("ruby core\n",
+ "~/.ri\n"))
assert_equal expected, out
end
@@ -143,7 +143,7 @@ class TestRDocRIDriver < MiniTest::Unit::TestCase
@RM::BlankLine.new,
@RM::Paragraph.new("Include thingy"),
@RM::BlankLine.new,
- @RM::Verbatim.new(' ', 'Enumerable', "\n"))
+ @RM::Verbatim.new("Enumerable\n"))
assert_equal expected, out
end
@@ -163,8 +163,8 @@ class TestRDocRIDriver < MiniTest::Unit::TestCase
@RM::Rule.new(1),
@RM::Heading.new(1, "Includes:"),
@RM::Paragraph.new("(from #{@store.friendly_path})"),
- @RM::Verbatim.new(' ', 'Inc', "\n",
- ' ', 'Enumerable', "\n"))
+ @RM::Verbatim.new("Inc\n",
+ "Enumerable\n"))
assert_equal expected, out
end
@@ -195,7 +195,7 @@ class TestRDocRIDriver < MiniTest::Unit::TestCase
expected = @RM::Document.new(
@RM::Heading.new(1, 'Class methods:'),
@RM::BlankLine.new,
- @RM::Verbatim.new(' ', 'new'),
+ @RM::Verbatim.new('new'),
@RM::BlankLine.new)
assert_equal expected, out
@@ -285,7 +285,7 @@ class TestRDocRIDriver < MiniTest::Unit::TestCase
doc = @RM::Document.new(
@RM::Paragraph.new('hi'))
- out, err = capture_io do
+ out, = capture_io do
@driver.display doc
end
@@ -295,12 +295,12 @@ class TestRDocRIDriver < MiniTest::Unit::TestCase
def test_display_class
util_store
- out, err = capture_io do
+ out, = capture_io do
@driver.display_class 'Foo::Bar'
end
assert_match %r%^= Foo::Bar%, out
- assert_match %r%^\(from%, out # )
+ assert_match %r%^\(from%, out
assert_match %r%^= Class methods:%, out
assert_match %r%^ new%, out
@@ -315,7 +315,7 @@ class TestRDocRIDriver < MiniTest::Unit::TestCase
def test_display_class_ambiguous
util_multi_store
- out, err = capture_io do
+ out, = capture_io do
@driver.display_class 'Ambiguous'
end
@@ -325,7 +325,7 @@ class TestRDocRIDriver < MiniTest::Unit::TestCase
def test_display_class_multi_no_doc
util_multi_store
- out, err = capture_io do
+ out, = capture_io do
@driver.display_class 'Foo::Baz'
end
@@ -339,7 +339,7 @@ class TestRDocRIDriver < MiniTest::Unit::TestCase
def test_display_class_superclass
util_multi_store
- out, err = capture_io do
+ out, = capture_io do
@driver.display_class 'Bar'
end
@@ -349,7 +349,7 @@ class TestRDocRIDriver < MiniTest::Unit::TestCase
def test_display_class_module
util_store
- out, err = capture_io do
+ out, = capture_io do
@driver.display_class 'Inc'
end
@@ -359,7 +359,7 @@ class TestRDocRIDriver < MiniTest::Unit::TestCase
def test_display_method
util_store
- out, err = capture_io do
+ out, = capture_io do
@driver.display_method 'Foo::Bar#blah'
end
@@ -371,7 +371,7 @@ class TestRDocRIDriver < MiniTest::Unit::TestCase
def test_display_method_attribute
util_store
- out, err = capture_io do
+ out, = capture_io do
@driver.display_method 'Foo::Bar#attr'
end
@@ -382,7 +382,7 @@ class TestRDocRIDriver < MiniTest::Unit::TestCase
def test_display_method_inherited
util_multi_store
- out, err = capture_io do
+ out, = capture_io do
@driver.display_method 'Bar#inherit'
end
@@ -393,7 +393,7 @@ class TestRDocRIDriver < MiniTest::Unit::TestCase
def test_display_name_not_found_class
util_store
- out, err = capture_io do
+ out, = capture_io do
assert_equal false, @driver.display_name('Foo::B')
end
@@ -410,7 +410,7 @@ Foo::Baz
def test_display_name_not_found_method
util_store
- out, err = capture_io do
+ out, = capture_io do
assert_equal false, @driver.display_name('Foo::Bar#b')
end
@@ -427,7 +427,7 @@ Foo::Bar#bother
def test_display_method_params
util_store
- out, err = capture_io do
+ out, = capture_io do
@driver.display_method 'Foo::Bar#bother'
end
@@ -496,24 +496,34 @@ Foo::Bar#bother
end
def test_formatter
- driver = RDoc::RI::Driver.new
-
- io = Object.new
- def io.tty?; false; end
+ tty = Object.new
+ def tty.tty?() true; end
- assert_instance_of @RM::ToBs, driver.formatter(io)
+ driver = RDoc::RI::Driver.new
- def io.tty?; true; end
+ assert_instance_of @RM::ToAnsi, driver.formatter(tty)
- assert_instance_of @RM::ToAnsi, driver.formatter(io)
+ assert_instance_of @RM::ToBs, driver.formatter(StringIO.new)
driver.instance_variable_set :@paging, true
- assert_instance_of @RM::ToBs, driver.formatter(io)
+ assert_instance_of @RM::ToBs, driver.formatter(StringIO.new)
driver.instance_variable_set :@formatter_klass, @RM::ToHtml
- assert_instance_of @RM::ToHtml, driver.formatter(io)
+ assert_instance_of @RM::ToHtml, driver.formatter(tty)
+ end
+
+ def test_in_path_eh
+ path = ENV['PATH']
+
+ refute @driver.in_path?('/nonexistent')
+
+ ENV['PATH'] = File.expand_path '..', __FILE__
+
+ assert @driver.in_path?(File.basename(__FILE__))
+ ensure
+ ENV['PATH'] = path
end
def test_method_type
@@ -526,8 +536,8 @@ Foo::Bar#bother
def test_list_known_classes
util_store
- out, err = capture_io do
- @driver.list_known_classes
+ out, = capture_io do
+ @driver.list_known_classes
end
assert_equal "Ambiguous\nFoo\nFoo::Bar\nFoo::Baz\nInc\n", out
diff --git a/test/rdoc/test_rdoc_ri_store.rb b/test/rdoc/test_rdoc_ri_store.rb
index 4a52ded989..7c9f4ec80b 100644
--- a/test/rdoc/test_rdoc_ri_store.rb
+++ b/test/rdoc/test_rdoc_ri_store.rb
@@ -65,6 +65,10 @@ class TestRDocRIStore < MiniTest::Unit::TestCase
assert File.file?(path), "#{path} is not a file"
end
+ def refute_file path
+ refute File.exist?(path), "#{path} exists"
+ end
+
def test_attributes
@s.cache[:attributes]['Object'] = %w[attr]
@@ -94,6 +98,14 @@ class TestRDocRIStore < MiniTest::Unit::TestCase
@s.class_path('Object::SubClass')
end
+ def test_dry_run
+ refute @s.dry_run
+
+ @s.dry_run = true
+
+ assert @s.dry_run
+ end
+
def test_friendly_path
@s.path = @tmpdir
@s.type = nil
@@ -198,7 +210,7 @@ class TestRDocRIStore < MiniTest::Unit::TestCase
:instance_methods => { 'Object' => %w[method] },
:modules => %w[Object Object::SubClass],
:ancestors => {
- 'Object' => %w[Object],
+ 'Object' => %w[],
'Object::SubClass' => %w[Incl Object],
},
}
@@ -210,6 +222,19 @@ class TestRDocRIStore < MiniTest::Unit::TestCase
end
end
+ def test_save_cache_dry_run
+ @s.dry_run = true
+
+ @s.save_class @klass
+ @s.save_method @klass, @meth
+ @s.save_method @klass, @cmeth
+ @s.save_class @nest_klass
+
+ @s.save_cache
+
+ refute_file File.join(@tmpdir, 'cache.ri')
+ end
+
def test_save_cache_duplicate_methods
@s.save_method @klass, @meth
@s.save_method @klass, @meth
@@ -226,7 +251,7 @@ class TestRDocRIStore < MiniTest::Unit::TestCase
assert_file File.join(@tmpdir, 'Object', 'cdesc-Object.ri')
assert_cache({}, {}, { 'Object' => ['attr_accessor attr'] }, %w[Object],
- 'Object' => %w[Object])
+ 'Object' => %w[])
assert_equal @klass, @s.load_class('Object')
end
@@ -245,6 +270,15 @@ class TestRDocRIStore < MiniTest::Unit::TestCase
assert_equal @klass, @s.load_class('Object')
end
+ def test_save_class_dry_run
+ @s.dry_run = true
+
+ @s.save_class @klass
+
+ refute_file File.join(@tmpdir, 'Object')
+ refute_file File.join(@tmpdir, 'Object', 'cdesc-Object.ri')
+ end
+
def test_save_class_merge
@s.save_class @klass
@@ -270,7 +304,7 @@ class TestRDocRIStore < MiniTest::Unit::TestCase
assert_file File.join(@tmpdir, 'Object', 'cdesc-Object.ri')
assert_cache({}, {}, { 'Object' => ['attr_accessor attr'] }, %w[Object],
- 'Object' => %w[Object])
+ 'Object' => %w[])
assert_equal @klass, @s.load_class('Object')
end
@@ -296,6 +330,15 @@ class TestRDocRIStore < MiniTest::Unit::TestCase
assert_equal @meth, @s.load_method('Object', '#method')
end
+ def test_save_method_dry_run
+ @s.dry_run = true
+
+ @s.save_method @klass, @meth
+
+ refute_file File.join(@tmpdir, 'Object')
+ refute_file File.join(@tmpdir, 'Object', 'method-i.ri')
+ end
+
def test_save_method_nested
@s.save_method @nest_klass, @nest_meth
diff --git a/test/rdoc/test_rdoc_task.rb b/test/rdoc/test_rdoc_task.rb
index e95ac46d4c..8eaff1f25b 100644
--- a/test/rdoc/test_rdoc_task.rb
+++ b/test/rdoc/test_rdoc_task.rb
@@ -42,7 +42,7 @@ class TestRDocTask < MiniTest::Unit::TestCase
end
def test_tasks_creation_with_custom_name_hash_will_use_default_if_an_option_isnt_given
- rd = RDoc::Task.new(:clobber_rdoc => "rdoc:clean")
+ RDoc::Task.new(:clobber_rdoc => "rdoc:clean")
assert Rake::Task[:rdoc]
assert Rake::Task[:"rdoc:clean"]
assert Rake::Task[:rerdoc]
diff --git a/test/rdoc/test_rdoc_text.rb b/test/rdoc/test_rdoc_text.rb
index 7e0f2cf0ba..600de30b0b 100644
--- a/test/rdoc/test_rdoc_text.rb
+++ b/test/rdoc/test_rdoc_text.rb
@@ -1,3 +1,5 @@
+# coding: utf-8
+
require 'rubygems'
require 'minitest/autorun'
require 'rdoc'
@@ -9,6 +11,15 @@ class TestRDocText < MiniTest::Unit::TestCase
include RDoc::Text
+ def test_self_encode_fallback
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ assert_equal '…',
+ RDoc::Text::encode_fallback('…', Encoding::UTF_8, '...')
+ assert_equal '...',
+ RDoc::Text::encode_fallback('…', Encoding::US_ASCII, '...')
+ end
+
def test_expand_tabs
assert_equal("hello\n dave",
expand_tabs("hello\n dave"), 'spaces')
@@ -46,9 +57,9 @@ class TestRDocText < MiniTest::Unit::TestCase
def test_flush_left
text = <<-TEXT
-
+
we don't worry too much.
-
+
The comments associated with
TEXT
@@ -65,7 +76,7 @@ The comments associated with
def test_markup
def formatter() RDoc::Markup::ToHtml.new end
- assert_equal "<p>\nhi\n</p>\n", markup('hi')
+ assert_equal "<p>hi</p>", markup('hi').gsub("\n", '')
end
def test_normalize_comment
@@ -114,9 +125,9 @@ The comments associated with
TEXT
expected = <<-EXPECTED
-
+
we don't worry too much.
-
+
The comments associated with
EXPECTED
@@ -143,15 +154,98 @@ The comments associated with
TEXT
expected = <<-EXPECTED
-
+
* we don't worry too much.
-
+
The comments associated with
-
EXPECTED
assert_equal expected, strip_stars(text)
end
+ def test_to_html_apostrophe
+ assert_equal '‘a', to_html("'a")
+ assert_equal 'a’', to_html("a'")
+
+ assert_equal '‘a’ ‘', to_html("'a' '")
+ end
+
+ def test_to_html_backslash
+ assert_equal 'S', to_html('\\S')
+ end
+
+ def test_to_html_copyright
+ assert_equal '©', to_html('(c)')
+ end
+
+ def test_to_html_dash
+ assert_equal '-', to_html('-')
+ assert_equal '–', to_html('--')
+ assert_equal '—', to_html('---')
+ assert_equal '—-', to_html('----')
+ end
+
+ def test_to_html_double_backtick
+ assert_equal '“a', to_html('``a')
+ assert_equal '“a“', to_html('``a``')
+ end
+
+ def test_to_html_double_quote
+ assert_equal '“a', to_html('"a')
+ assert_equal '“a”', to_html('"a"')
+ end
+
+ def test_to_html_double_quote_quot
+ assert_equal '“a', to_html('&quot;a')
+ assert_equal '“a”', to_html('&quot;a&quot;')
+ end
+
+ def test_to_html_double_tick
+ assert_equal '”a', to_html("''a")
+ assert_equal '”a”', to_html("''a''")
+ end
+
+ def test_to_html_ellipsis
+ assert_equal '..', to_html('..')
+ assert_equal '…', to_html('...')
+ assert_equal '.…', to_html('....')
+ end
+
+ def test_to_html_encoding
+ skip "Encoding not implemented" unless Object.const_defined? :Encoding
+
+ s = '...(c)'.encode Encoding::Shift_JIS
+
+ html = to_html s
+
+ assert_equal Encoding::Shift_JIS, html.encoding
+
+ expected = '…(c)'.encode Encoding::Shift_JIS
+
+ assert_equal expected, html
+ end
+
+ def test_to_html_html_tag
+ assert_equal '<a href="http://example">hi’s</a>',
+ to_html('<a href="http://example">hi\'s</a>')
+ end
+
+ def test_to_html_registered_trademark
+ assert_equal '®', to_html('(r)')
+ end
+
+ def test_to_html_tt_tag
+ assert_equal '<tt>hi\'s</tt>', to_html('<tt>hi\'s</tt>')
+ assert_equal '<tt>hi\\\'s</tt>', to_html('<tt>hi\\\\\'s</tt>')
+ end
+
+ def test_to_html_tt_tag_mismatch
+ _, err = capture_io do
+ assert_equal '<tt>hi', to_html('<tt>hi')
+ end
+
+ assert_equal "mismatched <tt> tag\n", err
+ end
+
end
diff --git a/test/rdoc/test_rdoc_top_level.rb b/test/rdoc/test_rdoc_top_level.rb
index b96b74c182..f40a42b3f6 100644
--- a/test/rdoc/test_rdoc_top_level.rb
+++ b/test/rdoc/test_rdoc_top_level.rb
@@ -27,6 +27,17 @@ class TestRDocTopLevel < XrefTestCase
assert_equal expected, RDoc::TopLevel.classes.map { |m| m.full_name }.sort
end
+ def test_class_complete
+ @c2.add_module_alias @c2_c3, 'A1'
+
+ RDoc::TopLevel.complete :public
+
+ a1 = @xref_data.find_class_or_module 'C2::A1'
+
+ assert_equal 'C2::A1', a1.full_name
+ refute_empty a1.aliases
+ end
+
def test_class_files
assert_equal %w[path/top_level.rb xref_data.rb],
RDoc::TopLevel.files.map { |m| m.full_name }.sort
@@ -94,13 +105,11 @@ class TestRDocTopLevel < XrefTestCase
end
def test_last_modified
- assert_equal 'Unknown', @top_level.last_modified
-
+ assert_equal nil, @top_level.last_modified
stat = Object.new
def stat.mtime() 0 end
@top_level.file_stat = stat
-
- assert_equal '0', @top_level.last_modified
+ assert_equal 0, @top_level.last_modified
end
def test_name
diff --git a/test/rdoc/xref_data.rb b/test/rdoc/xref_data.rb
index ac1c39a915..3afb06bd13 100644
--- a/test/rdoc/xref_data.rb
+++ b/test/rdoc/xref_data.rb
@@ -13,9 +13,15 @@ class C1
def m foo
end
+
end
class C2
+ def b
+ end
+
+ alias a b
+
class C3
def m
end
@@ -46,6 +52,8 @@ class C5
end
module M1
+ def m
+ end
end
module M1::M2
diff --git a/test/rdoc/xref_test_case.rb b/test/rdoc/xref_test_case.rb
index b40956684b..307e8f3350 100644
--- a/test/rdoc/xref_test_case.rb
+++ b/test/rdoc/xref_test_case.rb
@@ -38,7 +38,11 @@ class XrefTestCase < MiniTest::Unit::TestCase
@c1_m = @c1.method_list.last # C1#m
@c1__m = @c1.method_list.first # C1::m
+
@c2 = @xref_data.find_module_named 'C2'
+ @c2_a = @c2.method_list.last
+ @c2_b = @c2.method_list.first
+
@c2_c3 = @xref_data.find_module_named 'C2::C3'
@c3 = @xref_data.find_module_named 'C3'
@c4 = @xref_data.find_module_named 'C4'
@@ -48,6 +52,8 @@ class XrefTestCase < MiniTest::Unit::TestCase
@c3_h2 = @xref_data.find_module_named 'C3::H2'
@m1 = @xref_data.find_module_named 'M1'
+ @m1_m = @m1.method_list.first
+
@m1_m2 = @xref_data.find_module_named 'M1::M2'
end