diff options
author | drbrain <drbrain@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2011-02-02 00:32:30 +0000 |
---|---|---|
committer | drbrain <drbrain@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2011-02-02 00:32:30 +0000 |
commit | cc2a16d94d744d14d4a5eb06eca22137f8a9b79e (patch) | |
tree | 2907a20e2d9ae3a2831707056bb3fe2d384b066d /lib | |
parent | 918f625a5eeba35b9b191cb39c1d634b4cc7efee (diff) |
Import RDoc 3.5.1
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@30760 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib')
32 files changed, 758 insertions, 232 deletions
diff --git a/lib/rdoc.rb b/lib/rdoc.rb index ef02b43027..e3885b1cbb 100644 --- a/lib/rdoc.rb +++ b/lib/rdoc.rb @@ -95,7 +95,7 @@ module RDoc ## # RDoc version you are using - VERSION = '3.1' + VERSION = '3.5.1' ## # Method visibilities diff --git a/lib/rdoc/anon_class.rb b/lib/rdoc/anon_class.rb index 0a684f2dfb..63c09e11f1 100644 --- a/lib/rdoc/anon_class.rb +++ b/lib/rdoc/anon_class.rb @@ -4,6 +4,8 @@ require 'rdoc/class_module' # An anonymous class like: # # c = Class.new do end +# +# AnonClass is currently not used. class RDoc::AnonClass < RDoc::ClassModule end diff --git a/lib/rdoc/any_method.rb b/lib/rdoc/any_method.rb index f993621f8b..5877ec9662 100644 --- a/lib/rdoc/any_method.rb +++ b/lib/rdoc/any_method.rb @@ -14,6 +14,11 @@ class RDoc::AnyMethod < RDoc::MethodAttr attr_accessor :dont_rename_initialize ## + # The C function that implements this method (if it was defined in a C file) + + attr_accessor :c_function + + ## # Different ways to call this method attr_accessor :call_seq @@ -31,8 +36,9 @@ class RDoc::AnyMethod < RDoc::MethodAttr def initialize text, name super + @c_function = nil @dont_rename_initialize = false - @token_stream = nil + @token_stream = nil end ## @@ -140,12 +146,41 @@ class RDoc::AnyMethod < RDoc::MethodAttr end ## - # Pretty parameter list for this method + # A list of this method's method and yield parameters. +call-seq+ params + # are preferred over parsed method and block params. + + def param_list + if @call_seq then + params = @call_seq.split("\n").last + params = params.sub(/.*?\((.*)\)/, '\1') + params = params.sub(/(\{|do)\s*\|([^|]*)\|.*/, ',\2') + elsif @params then + params = @params.sub(/\((.*)\)/, '\1') + + params << ",#{@block_params}" if @block_params + elsif @block_params then + params = @block_params + else + return [] + end + + params.gsub(/\s+/, '').split ',' + end + + ## + # Pretty parameter list for this method. If the method's parameters were + # given by +call-seq+ it is preferred over the parsed values. def param_seq - params = @params.gsub(/\s*\#.*/, '') - params = params.tr("\n", " ").squeeze(" ") - params = "(#{params})" unless params[0] == ?( + if @call_seq then + params = @call_seq.split("\n").last + params = params.sub(/[^( ]+/, '') + params = params.sub(/(\|[^|]+\|)\s*\.\.\.\s*(end|\})/, '\1 \2') + else + params = @params.gsub(/\s*\#.*/, '') + params = params.tr("\n", " ").squeeze(" ") + params = "(#{params})" unless params[0] == ?( + end if @block_params then # If this method has explicit block parameters, remove any explicit diff --git a/lib/rdoc/class_module.rb b/lib/rdoc/class_module.rb index 64ccfdabd6..3f2d04488e 100644 --- a/lib/rdoc/class_module.rb +++ b/lib/rdoc/class_module.rb @@ -151,7 +151,7 @@ class RDoc::ClassModule < RDoc::Context end ## - # Finds a class or module with +name+ in this namespace or its descendents + # Finds a class or module with +name+ in this namespace or its descendants def find_class_named name return self if full_name == name @@ -308,7 +308,7 @@ class RDoc::ClassModule < RDoc::Context ## # Name to use to generate the url: # modules and classes that are aliases for another - # module or classe return the name of the latter. + # module or class return the name of the latter. def name_for_path is_alias_for ? is_alias_for.full_name : full_name diff --git a/lib/rdoc/code_object.rb b/lib/rdoc/code_object.rb index 02b2f2fcf6..3a8adfab67 100644 --- a/lib/rdoc/code_object.rb +++ b/lib/rdoc/code_object.rb @@ -61,11 +61,23 @@ class RDoc::CodeObject attr_reader :force_documentation ## + # Line in #file where this CodeObject was defined + + attr_accessor :line + + ## # Hash of arbitrary metadata for this CodeObject attr_reader :metadata ## + # Offset in #file where this CodeObject was defined + #-- + # TODO character or byte? + + attr_accessor :offset + + ## # Our parent CodeObject attr_accessor :parent @@ -115,6 +127,12 @@ class RDoc::CodeObject if comment and not comment.empty? then normalize_comment comment else + # TODO is this sufficient? + # HACK correct fix is to have #initialize create @comment + # with the correct encoding + if Object.const_defined? :Encoding and @comment.empty? then + @comment.force_encoding comment.encoding + end @comment end end diff --git a/lib/rdoc/context.rb b/lib/rdoc/context.rb index c424ef1676..3c12d10058 100644 --- a/lib/rdoc/context.rb +++ b/lib/rdoc/context.rb @@ -188,7 +188,6 @@ class RDoc::Context < RDoc::CodeObject @in_files = [] @name ||= "unknown" - @comment ||= "" @parent = nil @visibility = :public @@ -440,10 +439,13 @@ class RDoc::Context < RDoc::CodeObject # 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" + + if known then known.comment = constant.comment if known.comment.empty? - known.value = constant.value if known.value.nil? or known.value.strip.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 @@ -495,9 +497,10 @@ class RDoc::Context < RDoc::CodeObject end ## - # Adds an alias from +from+ (a class or module) to +name+. + # Adds an alias from +from+ (a class or module) to +name+ which was defined + # in +file+. - def add_module_alias from, name + def add_module_alias from, name, file return from if @done_documenting to_name = child_name(name) @@ -519,7 +522,8 @@ class RDoc::Context < RDoc::CodeObject # 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 = RDoc::Constant.new name, nil, '' + const.record_location file const.is_alias_for = from add_constant const diff --git a/lib/rdoc/encoding.rb b/lib/rdoc/encoding.rb index 73ae505daf..8483aabbc6 100644 --- a/lib/rdoc/encoding.rb +++ b/lib/rdoc/encoding.rb @@ -78,4 +78,3 @@ module RDoc::Encoding end - diff --git a/lib/rdoc/generator.rb b/lib/rdoc/generator.rb index d02a7538c0..2fa891f533 100644 --- a/lib/rdoc/generator.rb +++ b/lib/rdoc/generator.rb @@ -22,7 +22,7 @@ require 'rdoc' # 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 +# You can extend the RDoc::Options instance with additional accessors for your # generator. # # = Generator Instantiation diff --git a/lib/rdoc/generator/markup.rb b/lib/rdoc/generator/markup.rb index 482fd2b2a3..dd7c73044d 100644 --- a/lib/rdoc/generator/markup.rb +++ b/lib/rdoc/generator/markup.rb @@ -1,7 +1,11 @@ +# This file is loaded by generators. It allows RDoc's CodeObject tree to +# avoid loading generator code to increase startup time (for ri). + require 'rdoc/text' require 'rdoc/code_objects' require 'rdoc/generator' require 'rdoc/markup/to_html_crossref' +require 'rdoc/ruby_token' ## # Handle common RDoc::Markup tasks for various CodeObjects diff --git a/lib/rdoc/generator/template/darkfish/classpage.rhtml b/lib/rdoc/generator/template/darkfish/classpage.rhtml index 7d0cad0eed..4a1bdcdea9 100644 --- a/lib/rdoc/generator/template/darkfish/classpage.rhtml +++ b/lib/rdoc/generator/template/darkfish/classpage.rhtml @@ -228,7 +228,7 @@ <% if method.call_seq %> <% method.call_seq.strip.split("\n").each_with_index do |call_seq, i| %> <div class="method-heading"> - <span class="method-callseq"><%= call_seq.strip.gsub(/->/, '→').gsub( /^\w.+\./m, '') %></span> + <span class="method-callseq"><%= call_seq.strip.gsub(/->/, '→').gsub( /^\w+\./m, '') %></span> <% if i == 0 %> <span class="method-click-advice">click to toggle source</span> <% end %> diff --git a/lib/rdoc/generator/template/darkfish/rdoc.css b/lib/rdoc/generator/template/darkfish/rdoc.css index 231f9b7f04..59f568ae47 100644 --- a/lib/rdoc/generator/template/darkfish/rdoc.css +++ b/lib/rdoc/generator/template/darkfish/rdoc.css @@ -97,6 +97,11 @@ body.file p { margin: 1em 0; } +.indexpage ol, +.file #documentation ol { + line-height: 160%; +} + .indexpage ul, .file #documentation ul { line-height: 160%; @@ -110,6 +115,20 @@ body.file p { .indexpage li, .file #documentation li { padding-left: 20px; +} + +.indexpage ol, +.file #documentation ol { + margin-left: 20px; +} + +.indexpage ol > li, +.file #documentation ol > li { + padding-left: 0; +} + +.indexpage ul > li, +.file #documentation ul > li { background: url(images/bullet_black.png) no-repeat left 4px; } .indexpage li.module { @@ -389,9 +408,14 @@ ul.link-list .type { #documentation .method-description, #documentation .aliases { margin: 0 20px; - line-height: 1.2em; color: #666; } + +#documentation .method-description p, +#documentation .aliases p { + line-height: 1.2em; +} + #documentation .aliases { padding-top: 4px; font-style: italic; diff --git a/lib/rdoc/markup.rb b/lib/rdoc/markup.rb index c8914fea11..9a2e7f2ca8 100644 --- a/lib/rdoc/markup.rb +++ b/lib/rdoc/markup.rb @@ -483,7 +483,7 @@ require 'rdoc' # [+: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. +# position, it can be escaped 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 @@ -537,8 +537,8 @@ class RDoc::Markup # structure (paragraphs, lists, and so on). Invoke an event handler as we # identify significant chunks. - def initialize - @attribute_manager = RDoc::Markup::AttributeManager.new + def initialize attribute_manager = nil + @attribute_manager = attribute_manager || RDoc::Markup::AttributeManager.new @output = nil end diff --git a/lib/rdoc/markup/attribute_manager.rb b/lib/rdoc/markup/attribute_manager.rb index 2ee243ab0b..d2402f1b1d 100644 --- a/lib/rdoc/markup/attribute_manager.rb +++ b/lib/rdoc/markup/attribute_manager.rb @@ -74,7 +74,6 @@ class RDoc::Markup::AttributeManager add_html "code", :TT end - ## # Return an attribute object with the given turn_on and turn_off bits set diff --git a/lib/rdoc/markup/heading.rb b/lib/rdoc/markup/heading.rb index 081d637729..3bda77a1e1 100644 --- a/lib/rdoc/markup/heading.rb +++ b/lib/rdoc/markup/heading.rb @@ -4,7 +4,7 @@ class RDoc::Markup::Heading < Struct.new :level, :text ## - # Calls #accept_heading on +wisitor+ + # Calls #accept_heading on +visitor+ def accept visitor visitor.accept_heading self diff --git a/lib/rdoc/markup/inline.rb b/lib/rdoc/markup/inline.rb index f5bf98a071..932ed536b7 100644 --- a/lib/rdoc/markup/inline.rb +++ b/lib/rdoc/markup/inline.rb @@ -29,7 +29,7 @@ class RDoc::Markup end ## - # Returns a string reperesentation of +bitmap+ + # Returns a string representation of +bitmap+ def self.as_string(bitmap) return "none" if bitmap.zero? diff --git a/lib/rdoc/markup/parser.rb b/lib/rdoc/markup/parser.rb index ea02ee3c5b..c94b900154 100644 --- a/lib/rdoc/markup/parser.rb +++ b/lib/rdoc/markup/parser.rb @@ -321,7 +321,7 @@ class RDoc::Markup::Parser next end - # indentation change: break or verbattim + # indentation change: break or verbatim if column < indent then unget break diff --git a/lib/rdoc/markup/to_html.rb b/lib/rdoc/markup/to_html.rb index 66f5c1986d..d587a8abbc 100644 --- a/lib/rdoc/markup/to_html.rb +++ b/lib/rdoc/markup/to_html.rb @@ -130,7 +130,7 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter end ## - # Here's a hypedlink where the label is different to the URL + # Here's a hyperlink where the label is different to the URL # <label>[url] or {long label}[url] def handle_special_TIDYLINK(special) diff --git a/lib/rdoc/markup/to_html_crossref.rb b/lib/rdoc/markup/to_html_crossref.rb index a3feb848a2..026defb862 100644 --- a/lib/rdoc/markup/to_html_crossref.rb +++ b/lib/rdoc/markup/to_html_crossref.rb @@ -34,10 +34,10 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml # A::B::C.meth #{CLASS_REGEXP_STR}(?:[.#]|::)#{METHOD_REGEXP_STR} - # Stand-alone method (preceeded by a #) + # Stand-alone method (preceded by a #) | \\?\##{METHOD_REGEXP_STR} - # Stand-alone method (preceeded by ::) + # Stand-alone method (preceded by ::) | ::#{METHOD_REGEXP_STR} # A::B::C @@ -51,7 +51,7 @@ 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, sentence + # after the cross-reference is a space, sentence # punctuation, tag start character, or attribute # marker. | #{CLASS_REGEXP_STR}(?=[\s\)\.\?\!\,\;<\000]|\z) diff --git a/lib/rdoc/markup/to_rdoc.rb b/lib/rdoc/markup/to_rdoc.rb index b1ac59e5b0..b10af036d9 100644 --- a/lib/rdoc/markup/to_rdoc.rb +++ b/lib/rdoc/markup/to_rdoc.rb @@ -217,7 +217,7 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter end ## - # Removes preceeding \\ from the suppressed crossref +special+ + # Removes preceding \\ from the suppressed crossref +special+ def handle_special_SUPPRESSED_CROSSREF special text = special.text diff --git a/lib/rdoc/normal_class.rb b/lib/rdoc/normal_class.rb index 1ed8eaf974..f67380e783 100644 --- a/lib/rdoc/normal_class.rb +++ b/lib/rdoc/normal_class.rb @@ -12,6 +12,13 @@ class RDoc::NormalClass < RDoc::ClassModule superclass ? super + [superclass] : super end + ## + # The definition of this class, <tt>class MyClassName</tt> + + def definition + "class #{full_name}" + end + def inspect # :nodoc: superclass = @superclass ? " < #{@superclass}" : nil "<%s:0x%x class %s%s includes: %p attributes: %p methods: %p aliases: %p>" % [ diff --git a/lib/rdoc/normal_module.rb b/lib/rdoc/normal_module.rb index 74a31f2668..0fa7223547 100644 --- a/lib/rdoc/normal_module.rb +++ b/lib/rdoc/normal_module.rb @@ -13,6 +13,13 @@ class RDoc::NormalModule < RDoc::ClassModule end ## + # The definition of this module, <tt>module MyModuleName</tt> + + def definition + "module #{full_name}" + end + + ## # This is a module, returns true def module? diff --git a/lib/rdoc/options.rb b/lib/rdoc/options.rb index 3d76569a7e..c5f3b36b8d 100644 --- a/lib/rdoc/options.rb +++ b/lib/rdoc/options.rb @@ -71,7 +71,7 @@ class RDoc::Options attr_accessor :formatter ## - # Description of the output generator (set with the <tt>-fmt</tt> option) + # Description of the output generator (set with the <tt>--fmt</tt> option) attr_accessor :generator @@ -149,6 +149,11 @@ class RDoc::Options attr_accessor :title ## + # Should RDoc update the timestamps in the output dir? + + attr_accessor :update_output_dir + + ## # Verbosity, zero means quiet attr_accessor :verbosity @@ -188,6 +193,7 @@ class RDoc::Options @template = nil @template_dir = nil @title = nil + @update_output_dir = true @verbosity = 1 @visibility = :protected @webcvs = nil @@ -241,6 +247,35 @@ class RDoc::Options end ## + # Completes any unfinished option setup business such as filtering for + # existent files, creating a regexp for #exclude and setting a default + # #template. + + def finish + @op_dir ||= 'doc' + + @rdoc_include << "." if @rdoc_include.empty? + + if @exclude.empty? then + @exclude = nil + else + @exclude = Regexp.new(@exclude.join("|")) + end + + check_files + + # If no template was specified, use the default template for the output + # formatter + + unless @template then + @template = @generator_name + @template_dir = template_dir_for @template + end + + self + end + + ## # Returns a properly-space list of generators and their descriptions. def generator_descriptions @@ -267,7 +302,7 @@ class RDoc::Options end ## - # Parse command line options. + # Parses command line options. def parse(argv) ignore_invalid = true @@ -449,9 +484,11 @@ Usage: #{opt.program_name} [options] [names...] opt.separator nil - opt.on("--[no-]coverage-report", "--[no-]dcov", "-C", + opt.on("--[no-]coverage-report=[LEVEL]", "--[no-]dcov", "-C", Integer, "Prints a report on undocumented items.", "Does not generate files.") do |value| + value = 0 if value.nil? # Integer converts -C to nil + @coverage_report = value @force_update = true if value end @@ -669,26 +706,9 @@ Usage: #{opt.program_name} [options] [names...] end end - @op_dir ||= 'doc' @files = argv.dup - @rdoc_include << "." if @rdoc_include.empty? - - if @exclude.empty? then - @exclude = nil - else - @exclude = Regexp.new(@exclude.join("|")) - end - - check_files - - # If no template was specified, use the default template for the output - # formatter - - unless @template then - @template = @generator_name - @template_dir = template_dir_for @template - end + finish end ## @@ -725,7 +745,10 @@ Usage: #{opt.program_name} [options] [names...] @generator_name = generator_name @generator_options << @generator - @generator.setup_options self if @generator.respond_to? :setup_options + if @generator.respond_to? :setup_options then + @option_parser ||= OptionParser.new + @generator.setup_options self + end end ## diff --git a/lib/rdoc/parser.rb b/lib/rdoc/parser.rb index d218aba62a..4644324fc7 100644 --- a/lib/rdoc/parser.rb +++ b/lib/rdoc/parser.rb @@ -45,7 +45,7 @@ class RDoc::Parser class << self ## - # A Hash that maps file exetensions regular expressions to parsers that + # A Hash that maps file extensions regular expressions to parsers that # will consume them. # # Use parse_files_matching to register a parser's file extensions. diff --git a/lib/rdoc/parser/c.rb b/lib/rdoc/parser/c.rb index db37985b6d..bb015690bd 100644 --- a/lib/rdoc/parser/c.rb +++ b/lib/rdoc/parser/c.rb @@ -71,7 +71,7 @@ require 'rdoc/known_classes' # 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, +# Because C source doesn't give descriptive names to Ruby-level parameters, # you need to document the calling sequence explicitly # # In addition, RDoc assumes by default that the C method implementing a @@ -156,6 +156,7 @@ class RDoc::Parser::C < RDoc::Parser comment = strip_stars comment al.comment = comment + al.record_location @top_level class_obj.add_alias al @stats.add_alias al end @@ -284,7 +285,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, function, param_count, source_file| # Ignore top-object and weird struct.c dynamic stuff next if var_name == "ruby_top_self" @@ -293,7 +294,7 @@ class RDoc::Parser::C < RDoc::Parser next if var_name == "argf" # it'd be nice to handle this one var_name = "rb_cObject" if var_name == "rb_mKernel" - handle_method(type, var_name, meth_name, meth_body, param_count, + handle_method(type, var_name, meth_name, function, param_count, source_file) end @@ -302,18 +303,19 @@ class RDoc::Parser::C < RDoc::Parser \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?, \s*(-?\w+)\s*\) (?:;\s*/[*/]\s+in\s+(\w+?\.[cy]))? - %xm) do |meth_name, meth_body, param_count, source_file| - handle_method("method", "rb_mKernel", meth_name, - meth_body, param_count, source_file) + %xm) do |meth_name, function, param_count, source_file| + handle_method("method", "rb_mKernel", meth_name, function, 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*(-?\w+)\s*\)/xm) do |meth_name, function, 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) + handle_method("method", "rb_mFileTest", meth_name, function, param_count) + handle_method("singleton_method", "rb_cFile", meth_name, function, + param_count) end end @@ -369,21 +371,20 @@ class RDoc::Parser::C < RDoc::Parser ## # Find the C code corresponding to a Ruby method - def find_body(class_name, meth_name, meth_obj, body, quiet = false) - case body + def find_body class_name, meth_name, meth_obj, file_content, quiet = false + case file_content when %r%((?>/\*.*?\*/\s*)?) ((?:(?:static|SWIGINTERN)\s+)? (?:intern\s+)?VALUE\s+#{meth_name} - \s*(\([^)]*\))([^;]|$))%xm then + \s*(\(.*?\))([^;]|$))%xm then comment = $1 - body_text = $2 + body = $2 + offset = $~.offset(2).first remove_private_comments comment if comment - # see if we can find the whole body - - re = Regexp.escape(body_text) + '[^(]*^\{.*?^\}' - body_text = $& if /#{re}/m =~ body + # try to find the whole body + body = $& if /#{Regexp.escape body}[^(]*?\{.*?^\}/m =~ file_content # The comment block may have been overridden with a 'Document-method' # block. This happens in the interpreter when multiple methods are @@ -399,38 +400,53 @@ class RDoc::Parser::C < RDoc::Parser #meth_obj.params = params meth_obj.start_collecting_tokens tk = RDoc::RubyToken::Token.new nil, 1, 1 - tk.set_text body_text + tk.set_text body meth_obj.add_token tk meth_obj.comment = strip_stars comment - when %r%((?>/\*.*?\*/\s*))^\s*(\#\s*define\s+#{meth_name}\s+(\w+))%m + meth_obj.offset = offset + meth_obj.line = file_content[0, offset].count("\n") + 1 + + body + when %r%((?>/\*.*?\*/\s*))^\s*(\#\s*define\s+#{meth_name}\s+(\w+))%m then comment = $1 - body_text = $2 - find_body class_name, $3, meth_obj, body, true + body = $2 + offset = $~.offset(2).first + + find_body class_name, $3, meth_obj, file_content, true find_modifiers comment, meth_obj meth_obj.start_collecting_tokens tk = RDoc::RubyToken::Token.new nil, 1, 1 - tk.set_text body_text + tk.set_text body 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 - unless find_body(class_name, $1, meth_obj, body, true) - warn "No definition for #{meth_name}" if @options.verbosity > 1 - return false - end + meth_obj.offset = offset + meth_obj.line = file_content[0, offset].count("\n") + 1 + + body + when %r%^\s*\#\s*define\s+#{meth_name}\s+(\w+)%m then + # with no comment we hope the aliased definition has it and use it's + # definition + + body = find_body(class_name, $1, meth_obj, file_content, true) + + return body if body + + warn "No definition for #{meth_name}" if @options.verbosity > 1 + false else # No body, but might still have an override comment comment = find_override_comment class_name, meth_obj.name - if comment + if comment then find_modifiers comment, meth_obj meth_obj.comment = strip_stars comment + + '' else warn "No definition for #{meth_name}" if @options.verbosity > 1 - return false + false end end - - true end ## @@ -618,6 +634,7 @@ class RDoc::Parser::C < RDoc::Parser attr = RDoc::Attr.new '', name, rw, comment + attr.record_location @top_level class_obj.add_attribute attr @stats.add_attribute attr end @@ -732,6 +749,7 @@ class RDoc::Parser::C < RDoc::Parser con = RDoc::Constant.new const_name, definition, comment end + con.record_location @top_level @stats.add_constant con class_obj.add_constant con end @@ -748,7 +766,7 @@ class RDoc::Parser::C < RDoc::Parser # 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, + def handle_method(type, var_name, meth_name, function, param_count, source_file = nil) singleton = false class_name = @known_classes[var_name] @@ -770,33 +788,37 @@ class RDoc::Parser::C < RDoc::Parser end meth_obj = RDoc::AnyMethod.new '', meth_name + meth_obj.c_function = function meth_obj.singleton = singleton || %w[singleton_method module_function].include?(type) p_count = Integer(param_count) rescue -1 - if p_count < 0 then - meth_obj.params = "(...)" - elsif p_count == 0 - meth_obj.params = "()" - else - meth_obj.params = "(" + (1..p_count).map{|i| "p#{i}"}.join(", ") + ")" - end - if source_file then file_name = File.join @file_dir, source_file if File.exist? file_name then - body = (@@known_bodies[file_name] ||= File.read(file_name)) + file_content = (@@known_bodies[file_name] ||= File.read(file_name)) else warn "unknown source #{source_file} for #{meth_name} in #{@file_name}" end else - body = @content + file_content = @content end - if find_body(class_name, meth_body, meth_obj, body) and - meth_obj.document_self then + body = find_body class_name, function, meth_obj, file_content + + if body and meth_obj.document_self then + meth_obj.params = if p_count < -1 then # -2 is Array + '(*args)' + elsif p_count == -1 then # argc, argv + rb_scan_args body + else + "(#{(1..p_count).map { |i| "p#{i}" }.join ', '})" + end + + + meth_obj.record_location @top_level class_obj.add_method meth_obj @stats.add_method meth_obj meth_obj.visibility = :private if 'private_method' == type @@ -857,6 +879,90 @@ class RDoc::Parser::C < RDoc::Parser end ## + # Extracts parameters from the +method_body+ and returns a method + # parameter string. Follows 1.9.3dev's scan-arg-spec, see README.EXT + + def rb_scan_args method_body + method_body =~ /rb_scan_args\((.*?)\)/m + return '(*args)' unless $1 + + $1.split(/,/)[2] =~ /"(.*?)"/ # format argument + format = $1.split(//) + + lead = opt = trail = 0 + + if format.first =~ /\d/ then + lead = $&.to_i + format.shift + if format.first =~ /\d/ then + opt = $&.to_i + format.shift + if format.first =~ /\d/ then + trail = $&.to_i + format.shift + block_arg = true + end + end + end + + if format.first == '*' and not block_arg then + var = true + format.shift + if format.first =~ /\d/ then + trail = $&.to_i + format.shift + end + end + + if format.first == ':' then + hash = true + format.shift + end + + if format.first == '&' then + block = true + format.shift + end + + # if the format string is not empty there's a bug in the C code, ignore it + + args = [] + position = 1 + + (1...(position + lead)).each do |index| + args << "p#{index}" + end + + position += lead + + (position...(position + opt)).each do |index| + args << "p#{index} = v#{index}" + end + + position += opt + + if var then + args << '*args' + position += 1 + end + + (position...(position + trail)).each do |index| + args << "p#{index}" + end + + position += trail + + if hash then + args << "p#{position} = {}" + position += 1 + end + + args << '&block' if block + + "(#{args.join ', '})" + end + + ## # Removes lines that are commented out that might otherwise get picked up # when scanning for classes and methods diff --git a/lib/rdoc/parser/ruby.rb b/lib/rdoc/parser/ruby.rb index bec88bc52d..bcf0ac1150 100644 --- a/lib/rdoc/parser/ruby.rb +++ b/lib/rdoc/parser/ruby.rb @@ -174,6 +174,9 @@ class RDoc::Parser::Ruby < RDoc::Parser @scanner.exception_on_syntax_error = false @prev_seek = nil + @encoding = nil + @encoding = @options.encoding if Object.const_defined? :Encoding + reset end @@ -183,6 +186,7 @@ class RDoc::Parser::Ruby < RDoc::Parser def collect_first_comment skip_tkspace comment = '' + comment.force_encoding @encoding if @encoding first_line = true tk = get_tk @@ -449,6 +453,9 @@ class RDoc::Parser::Ruby < RDoc::Parser # +comment+. def parse_attr(context, single, tk, comment) + offset = tk.seek + line_no = tk.line_no + args = parse_symbol_arg 1 if args.size > 0 then name = args[0] @@ -464,6 +471,8 @@ class RDoc::Parser::Ruby < RDoc::Parser att = RDoc::Attr.new get_tkread, name, rw, comment, single == SINGLE att.record_location @top_level + att.offset = offset + att.line = line_no read_documentation_modifiers att, RDoc::ATTR_MODIFIERS @@ -480,6 +489,9 @@ class RDoc::Parser::Ruby < RDoc::Parser # comment for each to +comment+. def parse_attr_accessor(context, single, tk, comment) + offset = tk.seek + line_no = tk.line_no + args = parse_symbol_arg rw = "?" @@ -498,6 +510,8 @@ class RDoc::Parser::Ruby < RDoc::Parser for name in args att = RDoc::Attr.new get_tkread, name, rw, comment, single == SINGLE att.record_location @top_level + att.offset = offset + att.line = line_no context.add_attribute att @stats.add_attribute att @@ -508,6 +522,9 @@ class RDoc::Parser::Ruby < RDoc::Parser # Parses an +alias+ in +context+ with +comment+ def parse_alias(context, single, tk, comment) + offset = tk.seek + line_no = tk.line_no + skip_tkspace if TkLPAREN === peek_tk then @@ -534,6 +551,8 @@ class RDoc::Parser::Ruby < RDoc::Parser al = RDoc::Alias.new(get_tkread, old_name, new_name, comment, single == SINGLE) al.record_location @top_level + al.offset = offset + al.line = line_no read_documentation_modifiers al, RDoc::ATTR_MODIFIERS context.add_alias al if al.document_self @@ -586,6 +605,9 @@ class RDoc::Parser::Ruby < RDoc::Parser # Parses a class in +context+ with +comment+ def parse_class(container, single, tk, comment) + offset = tk.seek + line_no = tk.line_no + declaration_context = container container, name_t, given_name = get_class_or_module container @@ -606,6 +628,9 @@ class RDoc::Parser::Ruby < RDoc::Parser read_documentation_modifiers cls, RDoc::CLASS_MODIFIERS cls.record_location @top_level + cls.offset = offset + cls.line = line_no + cls.comment = comment if cls.document_self @top_level.add_to_classes_or_modules cls @@ -622,6 +647,9 @@ class RDoc::Parser::Ruby < RDoc::Parser unless other then other = container.add_module RDoc::NormalModule, name other.record_location @top_level + other.offset = offset + other.line = line_no + other.comment = comment end @@ -639,7 +667,6 @@ class RDoc::Parser::Ruby < RDoc::Parser read_documentation_modifiers other, RDoc::CLASS_MODIFIERS parse_statements(other, SINGLE) end - else warn("Expected class name or '<<'. Got #{name_t.class}: #{name_t.text.inspect}") end @@ -649,6 +676,9 @@ class RDoc::Parser::Ruby < RDoc::Parser # Parses a constant in +context+ with +comment+ def parse_constant(container, tk, comment) + offset = tk.seek + line_no = tk.line_no + name = tk.name skip_tkspace false @@ -658,7 +688,7 @@ class RDoc::Parser::Ruby < RDoc::Parser unless TkASSIGN === eq_tk then unget_tk eq_tk - return + return false end nest = 0 @@ -669,7 +699,7 @@ class RDoc::Parser::Ruby < RDoc::Parser if TkGT === tk then unget_tk tk unget_tk eq_tk - return + return false end rhs_name = '' @@ -698,7 +728,7 @@ class RDoc::Parser::Ruby < RDoc::Parser container.find_module_named rhs_name end - container.add_module_alias mod, name if mod + container.add_module_alias mod, name, @top_level if mod get_tk # TkNL break end @@ -721,10 +751,13 @@ class RDoc::Parser::Ruby < RDoc::Parser con = RDoc::Constant.new name, res, comment con.record_location @top_level + con.offset = offset + con.line = line_no read_documentation_modifiers con, RDoc::CONSTANT_MODIFIERS @stats.add_constant con container.add_constant con if con.document_self + true end ## @@ -732,8 +765,9 @@ class RDoc::Parser::Ruby < RDoc::Parser # :method: or :attr: directives in +comment+. def parse_comment(container, tk, comment) - line_no = tk.line_no column = tk.char_no + offset = tk.seek + line_no = tk.line_no singleton = !!comment.sub!(/(^# +:?)(singleton-)(method:)/, '\1\3') @@ -744,6 +778,8 @@ class RDoc::Parser::Ruby < RDoc::Parser meth = RDoc::GhostMethod.new get_tkread, name meth.record_location @top_level meth.singleton = singleton + meth.offset = offset + meth.line = line_no meth.start_collecting_tokens indent = TkSPACE.new nil, 1, 1 @@ -764,7 +800,7 @@ class RDoc::Parser::Ruby < RDoc::Parser meth.comment = comment @stats.add_method meth - elsif comment.sub!(/# +:?(attr(_reader|_writer|_accessor)?:) *(\S*).*?\n/i, '') then + elsif comment.sub!(/# +:?(attr(_reader|_writer|_accessor)?): *(\S*).*?\n/i, '') then rw = case $1 when 'attr_reader' then 'R' when 'attr_writer' then 'W' @@ -776,6 +812,8 @@ class RDoc::Parser::Ruby < RDoc::Parser # TODO authorize 'singleton-attr...'? att = RDoc::Attr.new get_tkread, name, rw, comment att.record_location @top_level + att.offset = offset + att.line = line_no container.add_attribute att @@ -870,8 +908,9 @@ class RDoc::Parser::Ruby < RDoc::Parser # Parses a meta-programmed method def parse_meta_method(container, single, tk, comment) - line_no = tk.line_no column = tk.char_no + offset = tk.seek + line_no = tk.line_no start_collecting_tokens add_token tk @@ -903,6 +942,8 @@ class RDoc::Parser::Ruby < RDoc::Parser meth = RDoc::MetaMethod.new get_tkread, name meth.record_location @top_level + meth.offset = offset + meth.line = line_no meth.singleton = singleton remove_token_listener self @@ -933,6 +974,10 @@ class RDoc::Parser::Ruby < RDoc::Parser break unless last_tk and TkCOMMA === last_tk when TkSPACE then # expression continues + when TkDO then + unget_tk tk + parse_statements container, single, meth + break else last_tk = tk end @@ -951,8 +996,9 @@ class RDoc::Parser::Ruby < RDoc::Parser added_container = nil meth = nil name = nil - line_no = tk.line_no column = tk.char_no + offset = tk.seek + line_no = tk.line_no start_collecting_tokens add_token tk @@ -1044,6 +1090,8 @@ class RDoc::Parser::Ruby < RDoc::Parser end meth.record_location @top_level + meth.offset = offset + meth.line = line_no meth.start_collecting_tokens indent = TkSPACE.new nil, 1, 1 @@ -1216,6 +1264,8 @@ class RDoc::Parser::Ruby < RDoc::Parser def parse_statements(container, single = NORMAL, current_method = nil, comment = '') + comment.force_encoding @encoding if @encoding + nest = 1 save_visibility = container.visibility @@ -1223,6 +1273,7 @@ class RDoc::Parser::Ruby < RDoc::Parser while tk = get_tk do keep_comment = false + try_parse_comment = false non_comment_seen = true unless TkCOMMENT === tk @@ -1238,6 +1289,7 @@ class RDoc::Parser::Ruby < RDoc::Parser comment.empty? comment = '' + comment.force_encoding @encoding if @encoding end while TkCOMMENT === tk do @@ -1287,7 +1339,9 @@ class RDoc::Parser::Ruby < RDoc::Parser when TkCONSTANT then if container.document_self then - parse_constant container, tk, comment + if not parse_constant container, tk, comment then + try_parse_comment = true + end end when TkALIAS then @@ -1365,13 +1419,20 @@ class RDoc::Parser::Ruby < RDoc::Parser return end else + try_parse_comment = nest == 1 + end + + if try_parse_comment then non_comment_seen = parse_comment container, tk, comment unless comment.empty? - comment = '' + keep_comment = false end - comment = '' unless keep_comment + unless keep_comment then + comment = '' + comment.force_encoding @encoding if @encoding + end begin get_tkread @@ -1584,8 +1645,11 @@ class RDoc::Parser::Ruby < RDoc::Parser # Removes private comments from +comment+ def remove_private_comments(comment) - comment.gsub!(/^#--\n.*?^#\+\+\n?/m, '') - comment.sub!(/^#--\n.*\n?/m, '') + empty = '' + empty.force_encoding comment.encoding if Object.const_defined? :Encoding + + comment.gsub!(/^#--.*?^#\+\+\n?/m, empty) + comment.sub!(/^#--.*/m, '') end ## diff --git a/lib/rdoc/rdoc.rb b/lib/rdoc/rdoc.rb index c57b1d42d3..c66e66a87c 100644 --- a/lib/rdoc/rdoc.rb +++ b/lib/rdoc/rdoc.rb @@ -197,6 +197,7 @@ option) end unless @options.force_output else FileUtils.mkdir_p dir + FileUtils.touch output_flag_file dir end last @@ -206,7 +207,7 @@ option) # Update the flag file in an output directory. def update_output_dir(op_dir, time, last = {}) - return if @options.dry_run + return if @options.dry_run or not @options.update_output_dir open output_flag_file(op_dir), "w" do |f| f.puts time.rfc2822 @@ -353,12 +354,12 @@ The internal error was: def parse_files files file_list = gather_files files + @stats = RDoc::Stats.new file_list.size, @options.verbosity return [] if file_list.empty? file_info = [] - @stats = RDoc::Stats.new file_list.size, @options.verbosity @stats.begin_adding file_info = file_list.map do |filename| @@ -381,21 +382,30 @@ The internal error was: end ## - # Format up one or more files according to the given arguments. + # Generates documentation or a coverage report depending upon the settings + # in +options+. + # + # +options+ can be either an RDoc::Options instance or an array of strings + # equivalent to the strings that would be passed on the command line like + # <tt>%w[-q -o doc -t My\ Doc\ Title]</tt>. #document will automatically + # call RDoc::Options#finish if an options instance was given. # - # 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 <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. - - def document(argv) + # For a list of options, see either RDoc::Options or <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. + + def document options RDoc::TopLevel.reset RDoc::Parser::C.reset - @options = RDoc::Options.new - @options.parse argv + if RDoc::Options === options then + @options = options + @options.finish + else + @options = RDoc::Options.new + @options.parse options + end if @options.pipe then handle_pipe @@ -416,10 +426,13 @@ The internal error was: RDoc::TopLevel.complete @options.visibility + @stats.coverage_level = @options.coverage_report + if @options.coverage_report then puts + puts @stats.report - elsif file_info.empty? + elsif file_info.empty? then $stderr.puts "\nNo newer files." unless @options.quiet else gen_klass = @options.generator @@ -474,6 +487,7 @@ begin load extension rescue => e warn "error loading #{extension.inspect}: #{e.message} (#{e.class})" + warn "\t#{e.backtrace.join "\n\t"}" if $DEBUG end end end diff --git a/lib/rdoc/ri/driver.rb b/lib/rdoc/ri/driver.rb index 112d65399c..9a336f7f3f 100644 --- a/lib/rdoc/ri/driver.rb +++ b/lib/rdoc/ri/driver.rb @@ -651,12 +651,14 @@ Options may also be set in the 'RI' environment variable. raise NotFoundError, name if found.empty? + filtered = filter_methods found, name + out = RDoc::Markup::Document.new out << RDoc::Markup::Heading.new(1, name) out << RDoc::Markup::BlankLine.new - found.each do |store, methods| + filtered.each do |store, methods| methods.each do |method| out << RDoc::Markup::Paragraph.new("(from #{store.friendly_path})") @@ -754,6 +756,21 @@ Options may also be set in the 'RI' environment variable. end ## + # Filters the methods in +found+ trying to find a match for +name+. + + def filter_methods found, name + regexp = name_regexp name + + filtered = found.find_all do |store, methods| + methods.any? { |method| method.full_name =~ regexp } + end + + return filtered unless filtered.empty? + + found + end + + ## # Yields items matching +name+ including the store they were found in, the # class being searched for, the class they were found in (an ancestor) the # types of methods to look up (from #method_type), and the method name being @@ -948,10 +965,10 @@ Options may also be set in the 'RI' environment variable. methods = [] methods << load_method(store, :class_methods, ancestor, '::', method) if - types == :class or types == :both + [:class, :both].include? types methods << load_method(store, :instance_methods, ancestor, '#', method) if - types == :instance or types == :both + [:instance, :both].include? types found << [store, methods.compact] end @@ -971,6 +988,21 @@ Options may also be set in the 'RI' environment variable. end ## + # Returns a regular expression for +name+ that will match an + # RDoc::AnyMethod's name. + + def name_regexp name + klass, type, name = parse_name name + + case type + when '#', '::' then + /^#{klass}#{type}#{name}$/ + else + /^#{klass}(#|::)#{name}$/ + end + end + + ## # Paginates output through a pager program. def page @@ -996,7 +1028,7 @@ Options may also be set in the 'RI' environment variable. end ## - # Extract the class, selector and method name parts from +name+ like + # Extracts the class, selector and method name parts from +name+ like # Foo::Bar#baz. # # NOTE: Given Foo::Bar, Bar is considered a class even though it may be a diff --git a/lib/rdoc/ruby_lex.rb b/lib/rdoc/ruby_lex.rb index 7e84a4ad8e..e4f2445438 100644 --- a/lib/rdoc/ruby_lex.rb +++ b/lib/rdoc/ruby_lex.rb @@ -401,13 +401,15 @@ class RDoc::RubyLex |op, io| @ltype = "=" res = '' - until (ch = getc) == "\n" do res << ch end + nil until (ch = getc) == "\n" + until peek_equal?("=end") && peek(4) =~ /\s/ do until (ch = getc) == "\n" do res << ch end end - res << gets + gets # consume =end + @ltype = nil - Token(TkRD_COMMENT, res) + Token(TkCOMMENT, res) end @OP.def_rule("\n") do |op, io| diff --git a/lib/rdoc/single_class.rb b/lib/rdoc/single_class.rb index 60336a759b..e48758d9c8 100644 --- a/lib/rdoc/single_class.rb +++ b/lib/rdoc/single_class.rb @@ -5,10 +5,19 @@ require 'rdoc/class_module' class RDoc::SingleClass < RDoc::ClassModule + ## # Adds the superclass to the included modules. + def ancestors superclass ? super + [superclass] : super end + ## + # The definition of this singleton class, <tt>class << MyClassName</tt> + + def definition + "class << #{full_name}" + end + end diff --git a/lib/rdoc/stats.rb b/lib/rdoc/stats.rb index 70e361feaa..c46a610d45 100644 --- a/lib/rdoc/stats.rb +++ b/lib/rdoc/stats.rb @@ -7,6 +7,11 @@ require 'rdoc' class RDoc::Stats ## + # Output level for the coverage report + + attr_reader :coverage_level + + ## # Count of files parsed during parsing attr_reader :files_so_far @@ -23,10 +28,14 @@ class RDoc::Stats def initialize num_files, verbosity = 1 @files_so_far = 0 @num_files = num_files + + @coverage_level = 0 + @doc_items = nil @fully_documented = nil + @num_params = 0 @percent_doc = nil - @start = Time.now + @undoc_params = 0 @display = case verbosity when 0 then Quiet.new num_files @@ -93,10 +102,11 @@ class RDoc::Stats end ## - # Calculates documentation totals and percentages + # Calculates documentation totals and percentages for classes, modules, + # constants, attributes and methods. def calculate - return if @percent_doc + return if @doc_items ucm = RDoc::TopLevel.unique_classes_and_modules constants = [] @@ -119,20 +129,31 @@ class RDoc::Stats @num_classes + @num_constants + @num_methods + - @num_modules + @num_modules + + @num_params @undoc_items = @undoc_attributes + @undoc_classes + @undoc_constants + @undoc_methods + - @undoc_modules + @undoc_modules + + @undoc_params @doc_items = @num_items - @undoc_items + end - @fully_documented = (@num_items - @doc_items) == 0 + ## + # Sets coverage report level. Accepted values are: + # + # false or nil:: No report + # 0:: Classes, modules, constants, attributes, methods + # 1:: Level 0 + method parameters - @percent_doc = @doc_items.to_f / @num_items * 100 if @num_items.nonzero? + def coverage_level= level + level = -1 unless level + + @coverage_level = level end ## @@ -160,96 +181,185 @@ class RDoc::Stats end ## + # A report that says you did a great job! + + def great_job + report = [] + report << '100% documentation!' + report << nil + report << 'Great Job!' + + report.join "\n" + end + + ## + # Calculates the percentage of items documented. + + def percent_doc + return @percent_doc if @percent_doc + + @fully_documented = (@num_items - @doc_items) == 0 + + @percent_doc = @doc_items.to_f / @num_items * 100 if @num_items.nonzero? + @percent_doc ||= 0 + + @percent_doc + end + + ## # Returns a report on which items are not documented def report + if @coverage_level > 0 then + require 'rdoc/markup/to_tt_only' + require 'rdoc/generator/markup' + require 'rdoc/text' + extend RDoc::Text + end + report = [] - calculate + if @coverage_level.zero? then + calculate + + return great_job if @num_items == @doc_items + end + + ucm = RDoc::TopLevel.unique_classes_and_modules + + ucm.sort.each do |cm| + report << report_class_module(cm) { + [ + report_constants(cm), + report_attributes(cm), + report_methods(cm), + ].compact + } + end + + if @coverage_level > 0 then + calculate + + return great_job if @num_items == @doc_items + end + + report.unshift nil + report.unshift 'The following items are not documented:' + + report.join "\n" + end + + ## + # Returns a report on undocumented attributes in ClassModule +cm+ + + def report_attributes cm + return if cm.attributes.empty? + + report = [] + + cm.each_attribute do |attr| + next if attr.documented? + report << " #{attr.definition} :#{attr.name} " \ + "# in file #{attr.file.full_name}" + end + + report + end + + ## + # Returns a report on undocumented items in ClassModule +cm+ + + def report_class_module cm + return if cm.fully_documented? and @coverage_level.zero? + + report = [] + + if cm.in_files.empty? then + report << "# #{cm.definition} is referenced but empty." + report << '#' + report << '# It probably came from another project. ' \ + "I'm sorry I'm holding it against you." + report << nil + + return report + elsif cm.documented? then + documented = true + report << "#{cm.definition} # is documented" + else + report << '# in files:' + + cm.in_files.each do |file| + report << "# #{file.full_name}" + end - if @num_items == @doc_items then - report << '100% documentation!' report << nil - report << 'Great Job!' - return report.join "\n" + report << "#{cm.definition}" end - report << 'The following items are not documented:' + body = yield.flatten # HACK remove #flatten + + return if body.empty? and documented + + report << nil << body unless body.empty? + + report << 'end' report << nil - ucm = RDoc::TopLevel.unique_classes_and_modules + report + end - 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:' - - cm.in_files.each do |file| - report << "# #{file.full_name}" - end + ## + # Returns a report on undocumented constants in ClassModule +cm+ - report << nil + def report_constants cm + return if cm.constants.empty? - report << "#{type} #{cm.full_name}" - end + report = [] - unless cm.constants.empty? then - report << nil + cm.each_constant do |constant| + # TODO constant aliases are listed in the summary but not reported + # figure out what to do here + next if constant.documented? || constant.is_alias_for + report << " # in file #{constant.file.full_name}" + report << " #{constant.name} = nil" + end - cm.each_constant do |constant| - # TODO constant aliases are listed in the summary but not reported - # figure out what to do here - next if constant.documented? || constant.is_alias_for - report << " # in file #{constant.file.full_name}" - report << " #{constant.name} = nil" - end - end + report + end + + ## + # Returns a report on undocumented methods in ClassModule +cm+ - unless cm.attributes.empty? then - report << nil + def report_methods cm + return if cm.method_list.empty? - cm.each_attribute do |attr| - next if attr.documented? - report << " #{attr.definition} #{attr.name} " \ - "# in file #{attr.file.full_name}" - end - end + report = [] + + cm.each_method do |method| + next if method.documented? and @coverage_level.zero? + + if @coverage_level > 0 then + params, undoc = undoc_params method - unless cm.method_list.empty? then - report << nil + @num_params += params - 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 + unless undoc.empty? then + @undoc_params += undoc.length + + undoc = undoc.map do |param| "+#{param}+" end + param_report = " # #{undoc.join ', '} is not documented" end end - report << 'end' + next if method.documented? and not param_report + report << " # in file #{method.file.full_name}" + report << param_report if param_report + report << " def #{method.name}#{method.params}; end" report << nil end - report.join "\n" + report end ## @@ -259,13 +369,14 @@ class RDoc::Stats calculate num_width = [@num_files, @num_items].max.to_s.length - nodoc_width = [ + undoc_width = [ @undoc_attributes, @undoc_classes, @undoc_constants, @undoc_items, @undoc_methods, @undoc_modules, + @undoc_params, ].max.to_s.length report = [] @@ -274,28 +385,51 @@ class RDoc::Stats report << nil report << 'Classes: %*d (%*d undocumented)' % [ - num_width, @num_classes, nodoc_width, @undoc_classes] + num_width, @num_classes, undoc_width, @undoc_classes] report << 'Modules: %*d (%*d undocumented)' % [ - num_width, @num_modules, nodoc_width, @undoc_modules] + num_width, @num_modules, undoc_width, @undoc_modules] report << 'Constants: %*d (%*d undocumented)' % [ - num_width, @num_constants, nodoc_width, @undoc_constants] + num_width, @num_constants, undoc_width, @undoc_constants] report << 'Attributes: %*d (%*d undocumented)' % [ - num_width, @num_attributes, nodoc_width, @undoc_attributes] + num_width, @num_attributes, undoc_width, @undoc_attributes] report << 'Methods: %*d (%*d undocumented)' % [ - num_width, @num_methods, nodoc_width, @undoc_methods] + num_width, @num_methods, undoc_width, @undoc_methods] + report << 'Parameters: %*d (%*d undocumented)' % [ + num_width, @num_params, undoc_width, @undoc_params] if + @coverage_level > 0 report << nil report << 'Total: %*d (%*d undocumented)' % [ - num_width, @num_items, nodoc_width, @undoc_items] + num_width, @num_items, undoc_width, @undoc_items] - report << '%6.2f%% documented' % @percent_doc if @percent_doc + report << '%6.2f%% documented' % percent_doc report << nil report << 'Elapsed: %0.1fs' % (Time.now - @start) report.join "\n" end + ## + # Determines which parameters in +method+ were not documented. Returns a + # total parameter count and an Array of undocumented methods. + + def undoc_params method + @formatter ||= RDoc::Markup::ToTtOnly.new + + params = method.param_list + + return 0, [] if params.empty? + + document = parse method.comment + + tts = document.accept @formatter + + undoc = params - tts + + [params.length, undoc] + end + autoload :Quiet, 'rdoc/stats/quiet' autoload :Normal, 'rdoc/stats/normal' autoload :Verbose, 'rdoc/stats/verbose' diff --git a/lib/rdoc/task.rb b/lib/rdoc/task.rb index 4a702e3fa8..ebf8ccc188 100644 --- a/lib/rdoc/task.rb +++ b/lib/rdoc/task.rb @@ -149,17 +149,45 @@ class RDoc::Task < Rake::TaskLib # 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 + def initialize name = :rdoc # :yield: self + defaults + + check_names name @name = name + + yield self if block_given? + + define + end + + ## + # Ensures that +names+ only includes names for the :rdoc, :clobber_rdoc and + # :rerdoc. If other names are given an ArgumentError is raised. + + def check_names names + return unless Hash === names + + invalid_options = + names.keys.map { |k| k.to_sym } - [:rdoc, :clobber_rdoc, :rerdoc] + + unless invalid_options.empty? then + raise ArgumentError, "invalid options: #{invalid_options.join ', '}" + end + end + + ## + # Task description for the clobber rdoc task or its renamed equivalent + + def clobber_task_description + "Remove RDoc HTML files" + end + + ## + # Sets default task values + + def defaults + @name = :rdoc @rdoc_files = Rake::FileList.new @rdoc_dir = 'html' @main = nil @@ -167,14 +195,12 @@ class RDoc::Task < Rake::TaskLib @template = nil @generator = nil @options = [] - yield self if block_given? - define end ## # All source is inline now. This method is deprecated - def inline_source() # :nodoc: + def inline_source # :nodoc: warn "RDoc::Task#inline_source is deprecated" true end @@ -190,13 +216,13 @@ class RDoc::Task < Rake::TaskLib # Create the tasks defined by this task lib. def define - desc "Build RDoc HTML files" + desc rdoc_task_description task rdoc_task_name - desc "Rebuild RDoc HTML files" + desc rerdoc_task_description task rerdoc_task_name => [clobber_task_name, rdoc_task_name] - desc "Remove RDoc HTML files" + desc clobber_task_description task clobber_task_name do rm_r @rdoc_dir rescue nil end @@ -215,11 +241,9 @@ class RDoc::Task < Rake::TaskLib @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 + $stderr.puts "rdoc #{args.join ' '}" if Rake.application.options.trace require 'rdoc/rdoc' - RDoc::RDoc.new.document(args) + RDoc::RDoc.new.document args end self @@ -247,6 +271,20 @@ class RDoc::Task < Rake::TaskLib @before_running_rdoc = block end + ## + # Task description for the rdoc task or its renamed equivalent + + def rdoc_task_description + 'Build RDoc HTML files' + end + + ## + # Task description for the rerdoc task or its renamed description + + def rerdoc_task_description + "Rebuild RDoc HTML files" + end + private def rdoc_target diff --git a/lib/rdoc/text.rb b/lib/rdoc/text.rb index 20cd8a258a..6dec4217e6 100644 --- a/lib/rdoc/text.rb +++ b/lib/rdoc/text.rb @@ -97,7 +97,8 @@ module RDoc::Text text = strip_hashes text text = expand_tabs text text = flush_left text - strip_newlines text + text = strip_newlines text + text end ## @@ -139,14 +140,18 @@ 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 '#',' ' }.gsub(/^\s+$/, '') + + empty = '' + empty.force_encoding text.encoding if Object.const_defined? :Encoding + + text.gsub(/^\s*(#+)/) { $1.tr '#', ' ' }.gsub(/^\s+$/, empty) end ## # Strips leading and trailing \n characters from +text+ def strip_newlines text - text.gsub(/\A\n*(.*?)\n*\z/m, '\1') + text.gsub(/\A\n*(.*?)\n*\z/m) do $1 end # block preserves String encoding end ## |