diff --git a/ChangeLog b/ChangeLog index ca35e495d2..1a4d97ee8a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +Thu Jun 16 13:25:25 2011 Eric Hodel + + * lib/rdoc*: Import RDoc 3.7 release candidate + Thu Jun 16 11:35:09 2011 Shugo Maeda * lib/net/imap.rb (search_response): parses SEARCH responses from diff --git a/lib/rdoc.rb b/lib/rdoc.rb index aee6da5049..3aa15dfa52 100644 --- a/lib/rdoc.rb +++ b/lib/rdoc.rb @@ -27,7 +27,8 @@ $DEBUG_RDOC = nil # * If you want to use RDoc to create documentation for your Ruby source files, # see RDoc::Markup and refer to rdoc --help for command line # usage. -# * If you want to generate documentation for extensions written in C, see +# * If you want to write documentation for Ruby files see RDoc::Parser::Ruby +# * If you want to write documentation for extensions written in C see # RDoc::Parser::C # * If you want to generate documentation using rake see RDoc::Task. # * If you want to drive RDoc programmatically, see RDoc::RDoc. @@ -103,7 +104,7 @@ module RDoc ## # RDoc version you are using - VERSION = '3.6.1' + VERSION = '3.7' ## # Method visibilities diff --git a/lib/rdoc/any_method.rb b/lib/rdoc/any_method.rb index f691598910..c008edfe95 100644 --- a/lib/rdoc/any_method.rb +++ b/lib/rdoc/any_method.rb @@ -6,7 +6,7 @@ require 'rdoc/token_stream' class RDoc::AnyMethod < RDoc::MethodAttr - MARSHAL_VERSION = 0 # :nodoc: + MARSHAL_VERSION = 1 # :nodoc: ## # Don't rename \#initialize to \::new @@ -44,7 +44,7 @@ class RDoc::AnyMethod < RDoc::MethodAttr ## # Adds +an_alias+ as an alias for this method in +context+. - def add_alias(an_alias, context = nil ) + def add_alias an_alias, context = nil method = self.class.new an_alias.text, an_alias.new_name method.record_location an_alias.file @@ -54,7 +54,7 @@ class RDoc::AnyMethod < RDoc::MethodAttr method.comment = an_alias.comment method.is_alias_for = self @aliases << method - context.add_method( method ) if context + context.add_method method if context method end @@ -83,7 +83,7 @@ class RDoc::AnyMethod < RDoc::MethodAttr def marshal_dump aliases = @aliases.map do |a| - [a.full_name, parse(a.comment)] + [a.name, parse(a.comment)] end [ MARSHAL_VERSION, @@ -96,6 +96,7 @@ class RDoc::AnyMethod < RDoc::MethodAttr @block_params, aliases, @params, + @file.absolute_name, ] end @@ -112,6 +113,7 @@ class RDoc::AnyMethod < RDoc::MethodAttr @token_stream = nil @aliases = [] + version = array[0] @name = array[1] @full_name = array[2] @singleton = array[3] @@ -119,6 +121,11 @@ class RDoc::AnyMethod < RDoc::MethodAttr @comment = array[5] @call_seq = array[6] @block_params = array[7] + + array[8].each do |new_name, comment| + add_alias RDoc::Alias.new(nil, @name, new_name, comment, @singleton) + end + @params = array[9] @parent_name = if @full_name =~ /#/ then @@ -129,9 +136,7 @@ class RDoc::AnyMethod < RDoc::MethodAttr name.join '::' end - array[8].each do |new_name, comment| - add_alias RDoc::Alias.new(nil, @name, new_name, comment, @singleton) - end + @file = RDoc::TopLevel.new array[10] if version > 0 end ## diff --git a/lib/rdoc/attr.rb b/lib/rdoc/attr.rb index f04fe168b1..5d9bc17831 100644 --- a/lib/rdoc/attr.rb +++ b/lib/rdoc/attr.rb @@ -6,7 +6,7 @@ require 'rdoc/method_attr' class RDoc::Attr < RDoc::MethodAttr - MARSHAL_VERSION = 1 # :nodoc: + MARSHAL_VERSION = 2 # :nodoc: ## # Is the attribute readable ('R'), writable ('W') or both ('RW')? @@ -92,6 +92,7 @@ class RDoc::Attr < RDoc::MethodAttr @visibility, parse(@comment), singleton, + @file.absolute_name, ] end @@ -103,6 +104,7 @@ class RDoc::Attr < RDoc::MethodAttr # * #parent_name def marshal_load array + version = array[0] @name = array[1] @full_name = array[2] @rw = array[3] @@ -110,9 +112,22 @@ class RDoc::Attr < RDoc::MethodAttr @comment = array[5] @singleton = array[6] || false # MARSHAL_VERSION == 0 + @file = RDoc::TopLevel.new array[7] if version > 1 + @parent_name = @full_name end + def pretty_print q # :nodoc: + q.group 2, "[#{self.class.name} #{full_name} #{rw} #{visibility}", "]" do + unless comment.empty? then + q.breakable + q.text "comment:" + q.breakable + q.pp @comment + end + end + end + def to_s # :nodoc: "#{definition} #{name} in: #{parent}" end diff --git a/lib/rdoc/class_module.rb b/lib/rdoc/class_module.rb index 89441b3e68..d7a14b73eb 100644 --- a/lib/rdoc/class_module.rb +++ b/lib/rdoc/class_module.rb @@ -6,13 +6,26 @@ require 'rdoc/context' class RDoc::ClassModule < RDoc::Context - MARSHAL_VERSION = 0 # :nodoc: + ## + # 1:: + # RDoc 3.7 + # * Added visibility, singleton and file to attributes + # * Added file to constants + # * Added file to includes + # * Added file to methods + + MARSHAL_VERSION = 1 # :nodoc: ## # Constants that are aliases for this class or module attr_accessor :constant_aliases + ## + # Comment and the location it came from. Use #add_comment to add comments + + attr_reader :comment_location + attr_accessor :diagram # :nodoc: ## @@ -23,10 +36,16 @@ class RDoc::ClassModule < RDoc::Context ## # Return a RDoc::ClassModule of class +class_type+ that is a copy # of module +module+. Used to promote modules to classes. + #-- + # TODO move to RDoc::NormalClass (I think) + + def self.from_module class_type, mod + klass = class_type.new mod.name + + mod.comment_location.each do |comment, location| + klass.add_comment comment, location + end - 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 @@ -85,9 +104,27 @@ class RDoc::ClassModule < RDoc::Context @is_alias_for = nil @name = name @superclass = superclass + @comment_location = [] # [[comment, location]] + super() end + ## + # Adds +comment+ to this ClassModule's list of comments at +location+. This + # method is preferred over #comment= since it allows ri data to be updated + # across multiple runs. + + def add_comment comment, location + return if comment.empty? + + original = comment + + comment = normalize_comment comment + @comment_location << [comment, location] + + self.comment = original + end + ## # Ancestors list for this ClassModule: the list of included modules # (classes will add their superclass if any). @@ -112,6 +149,8 @@ class RDoc::ClassModule < RDoc::Context end ## + # This method is deprecated, use #add_comment instead. + # # Appends +comment+ to the current comment, but separated by a rule. Works # more like +=. @@ -119,10 +158,9 @@ class RDoc::ClassModule < RDoc::Context return if comment.empty? comment = normalize_comment comment - comment = "#{@comment}\n---\n#{comment}" unless - @comment.empty? + comment = "#{@comment}\n---\n#{comment}" unless @comment.empty? - super + super comment end ## @@ -185,15 +223,16 @@ class RDoc::ClassModule < RDoc::Context end def marshal_dump # :nodoc: - # TODO must store the singleton attribute attrs = attributes.sort.map do |attr| - [attr.name, attr.rw] + [ attr.name, attr.rw, + attr.visibility, attr.singleton, attr.file_name, + ] end method_types = methods_by_type.map do |type, visibilities| visibilities = visibilities.map do |visibility, methods| method_names = methods.map do |method| - method.name + [method.name, method.file_name] end [visibility, method_names.uniq] @@ -206,51 +245,67 @@ class RDoc::ClassModule < RDoc::Context @name, full_name, @superclass, - parse(@comment), + parse(@comment_location), attrs, constants.map do |const| - [const.name, parse(const.comment)] + [const.name, parse(const.comment), const.file_name] end, includes.map do |incl| - [incl.name, parse(incl.comment)] + [incl.name, parse(incl.comment), incl.file_name] end, method_types, ] 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 + @current_section = nil + @document_self = true + @done_documenting = false + @parent = nil + @temporary_section = nil + @visibility = nil @name = array[1] @full_name = array[2] @superclass = array[3] @comment = array[4] - array[5].each do |name, rw| - add_attribute RDoc::Attr.new(nil, name, rw, nil) + @comment_location = if RDoc::Markup::Document === @comment.parts.first then + @comment + else + RDoc::Markup::Document.new @comment + end + + array[5].each do |name, rw, visibility, singleton, file| + singleton ||= false + visibility ||= :public + + attr = RDoc::Attr.new nil, name, rw, nil, singleton + + add_attribute attr + attr.visibility = visibility + attr.record_location RDoc::TopLevel.new file end - array[6].each do |name, comment| - add_constant RDoc::Constant.new(name, nil, comment) + array[6].each do |name, comment, file| + const = add_constant RDoc::Constant.new(name, nil, comment) + const.record_location RDoc::TopLevel.new file end - array[7].each do |name, comment| - add_include RDoc::Include.new(name, comment) + array[7].each do |name, comment, file| + incl = add_include RDoc::Include.new(name, comment) + incl.record_location RDoc::TopLevel.new file end array[8].each do |type, visibilities| visibilities.each do |visibility, methods| @visibility = visibility - methods.each do |name| + methods.each do |name, file| method = RDoc::AnyMethod.new nil, name method.singleton = true if type == 'class' + method.record_location RDoc::TopLevel.new file add_method method end end @@ -258,37 +313,73 @@ class RDoc::ClassModule < RDoc::Context end ## - # Merges +class_module+ into this ClassModule + # Merges +class_module+ into this ClassModule. + # + # The data in +class_module+ is preferred over the receiver. def merge class_module - comment = class_module.comment + other_document = parse class_module.comment_location - if comment then - document = parse @comment + if other_document then + document = parse @comment_location - comment.parts.concat document.parts + document = document.merge other_document - @comment = comment + @comment = @comment_location = document end - class_module.each_attribute do |attr| - if match = attributes.find { |a| a.name == attr.name } then - match.rw = [match.rw, attr.rw].compact.uniq.join - else + merge_collections attributes, class_module.attributes do |add, attr| + if add then add_attribute attr + else + @attributes.delete attr + @methods_hash.delete attr.pretty_name end end - class_module.each_constant do |const| - add_constant const + merge_collections constants, class_module.constants do |add, const| + if add then + add_constant const + else + @constants.delete const + @constants_hash.delete const.name + end end - class_module.each_include do |incl| - add_include incl + merge_collections includes, class_module.includes do |add, incl| + if add then + add_include incl + else + @includes.delete incl + end end - class_module.each_method do |meth| - add_method meth + merge_collections method_list, class_module.method_list do |add, meth| + if add then + add_method meth + else + @method_list.delete meth + @methods_hash.delete meth.pretty_name + end + end + + self + end + + ## + # Merges collection +mine+ with +other+ preferring other. + + def merge_collections mine, other, &block # :nodoc: + my_things = mine. group_by { |thing| thing.file } + other_things = other.group_by { |thing| thing.file } + + other_things.each do |file, things| + my_things[file].each { |thing| yield false, thing } if + my_things.include? file + + things.each do |thing| + yield true, thing + end end end @@ -308,6 +399,29 @@ class RDoc::ClassModule < RDoc::Context @name = new_name end + ## + # Parses +comment_location+ into an RDoc::Markup::Document composed of + # multiple RDoc::Markup::Documents with their file set. + + def parse comment_location + case comment_location + when String then + super + when Array then + docs = comment_location.map do |comment, location| + doc = super comment + doc.file = location.absolute_name + doc + end + + RDoc::Markup::Document.new(*docs) + when RDoc::Markup::Document then + return comment_location + else + raise ArgumentError, "unknown comment class #{comment_location.class}" + end + end + ## # Path to this class or module diff --git a/lib/rdoc/code_object.rb b/lib/rdoc/code_object.rb index 63f86afc64..606fd8ff49 100644 --- a/lib/rdoc/code_object.rb +++ b/lib/rdoc/code_object.rb @@ -130,7 +130,8 @@ class RDoc::CodeObject # 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 + if String === @comment and + Object.const_defined? :Encoding and @comment.empty? then @comment.force_encoding comment.encoding end @comment @@ -194,6 +195,12 @@ class RDoc::CodeObject self end + def file_name + return unless @file + + @file.absolute_name + end + ## # Force the documentation of this object unless documentation # has been turned off by :endoc: diff --git a/lib/rdoc/context.rb b/lib/rdoc/context.rb index f0168585ec..d2552c647c 100644 --- a/lib/rdoc/context.rb +++ b/lib/rdoc/context.rb @@ -30,9 +30,9 @@ class RDoc::Context < RDoc::CodeObject attr_reader :constants ## - # Current section of documentation + # Sets the current documentation section of documentation - attr_accessor :current_section + attr_writer :current_section ## # Files this context is found in @@ -59,6 +59,11 @@ class RDoc::Context < RDoc::CodeObject attr_reader :requires + ## + # Use this section for the next method, attribute or constant added. + + attr_accessor :temporary_section + ## # Hash old_name => [aliases], for aliases # that haven't (yet) been resolved to a method/attribute. @@ -186,10 +191,7 @@ class RDoc::Context < RDoc::CodeObject end def inspect # :nodoc: - "#<%s:0x%x %s %p>" % [ - self.class, object_id, - @sequence, title - ] + "#<%s:0x%x %p>" % [self.class, object_id, title] end ## @@ -216,6 +218,7 @@ class RDoc::Context < RDoc::CodeObject @current_section = Section.new self, nil, nil @sections = { nil => @current_section } + @temporary_section = nil @classes = {} @modules = {} @@ -287,22 +290,32 @@ class RDoc::Context < RDoc::CodeObject # TODO find a policy for 'attr_reader :foo' + 'def foo=()' register = false - if attribute.rw.index('R') then + key = nil + + 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? + elsif registered = @methods_hash[attribute.pretty_name << '='] and + RDoc::Attr === registered then + registered.rw = 'RW' else @methods_hash[key] = attribute register = true end end - if attribute.rw.index('W') + if attribute.rw.index 'W' then key = attribute.pretty_name << '=' known = @methods_hash[key] + if known then known.comment = attribute.comment if known.comment.empty? + elsif registered = @methods_hash[attribute.pretty_name] and + RDoc::Attr === registered then + registered.rw = 'RW' else @methods_hash[key] = attribute register = true @@ -314,6 +327,8 @@ class RDoc::Context < RDoc::CodeObject add_to @attributes, attribute resolve_aliases attribute end + + attribute end ## @@ -444,8 +459,8 @@ class RDoc::Context < RDoc::CodeObject # to +self+, and its #section to #current_section. Returns +mod+. def add_class_or_module mod, self_hash, all_hash - mod.section = @current_section # TODO declaring context? something is - # wrong here... + mod.section = current_section # TODO declaring context? something is + # wrong here... mod.parent = self unless @done_documenting then @@ -462,7 +477,7 @@ class RDoc::Context < RDoc::CodeObject # 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) + def add_constant constant return constant unless @document_self # HACK: avoid duplicate 'PI' & 'E' in math.c (1.8.7 source code) @@ -480,28 +495,32 @@ class RDoc::Context < RDoc::CodeObject @constants_hash[constant.name] = constant add_to @constants, constant end + + constant end ## # Adds included module +include+ which should be an RDoc::Include - def add_include(include) - add_to @includes, include unless @includes.map { |i| i.full_name }.include?( include.full_name ) + def add_include include + add_to @includes, include unless + @includes.map { |i| i.full_name }.include? include.full_name + + include end ## # Adds +method+ if not already there. If it is (as method or attribute), # updates the comment if it was empty. - def add_method(method) + def add_method method 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)}" + + if known then known.comment = method.comment if known.comment.empty? else @methods_hash[key] = method @@ -509,6 +528,8 @@ class RDoc::Context < RDoc::CodeObject add_to @method_list, method resolve_aliases method end + + method end ## @@ -572,13 +593,32 @@ class RDoc::Context < RDoc::CodeObject end end + ## + # Returns a section with +title+, creating it if it doesn't already exist. + # +comment+ will be appended to the section's comment. + # + # A section with a +title+ of +nil+ will return the default section. + # + # See also RDoc::Context::Section + + def add_section title, comment + if section = @sections[title] then + section.comment = comment + else + section = Section.new self, title, comment + @sections[title] = section + end + + section + end + ## # Adds +thing+ to the collection +array+ def add_to(array, thing) array << thing if @document_self thing.parent = self - thing.section = @current_section + thing.section = current_section end ## @@ -648,6 +688,20 @@ class RDoc::Context < RDoc::CodeObject @classes end + ## + # The current documentation section that new items will be added to. If + # temporary_section is available it will be used. + + def current_section + if section = @temporary_section then + @temporary_section = nil + else + section = @current_section + end + + section + end + ## # Is part of this thing was defined in +file+? @@ -1082,18 +1136,10 @@ class RDoc::Context < RDoc::CodeObject end ## - # Creates a new section with +title+ and +comment+ + # Sets the current section to a section with +title+. See also #add_section - def set_current_section(title, comment) - if @sections.key? title then - @current_section = @sections[title] - @current_section.comment = comment - else - @current_section = Section.new self, title, comment - @sections[title] = @current_section - end - - @current_section + def set_current_section title, comment + @current_section = add_section title, comment end ## diff --git a/lib/rdoc/generator/markup.rb b/lib/rdoc/generator/markup.rb index 1919a62ec8..c267bb1c13 100644 --- a/lib/rdoc/generator/markup.rb +++ b/lib/rdoc/generator/markup.rb @@ -42,7 +42,9 @@ module RDoc::Generator::Markup 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, hyperlink_all + + @formatter = RDoc::Markup::ToHtmlCrossref.new(this.path, this, show_hash, + hyperlink_all) end ## diff --git a/lib/rdoc/generator/ri.rb b/lib/rdoc/generator/ri.rb index e02805e40f..939a165cfb 100644 --- a/lib/rdoc/generator/ri.rb +++ b/lib/rdoc/generator/ri.rb @@ -18,11 +18,12 @@ class RDoc::Generator::RI def initialize options #:not-new: @options = options - @store = RDoc::RI::Store.new '.' @old_siginfo = nil @current = nil - @store.dry_run = @options.dry_run + @store = RDoc::RI::Store.new '.' + @store.dry_run = @options.dry_run + @store.encoding = @options.encoding if @options.respond_to? :encoding end ## diff --git a/lib/rdoc/generator/template/darkfish/rdoc.css b/lib/rdoc/generator/template/darkfish/rdoc.css index c5a93ff150..ea91421837 100644 --- a/lib/rdoc/generator/template/darkfish/rdoc.css +++ b/lib/rdoc/generator/template/darkfish/rdoc.css @@ -97,6 +97,10 @@ body.file p { margin: 1em 0; } +.indexpage .rdoc-list p, .file .rdoc-list p { + margin: 0em 0; +} + .indexpage ol, .file #documentation ol { line-height: 160%; diff --git a/lib/rdoc/known_classes.rb b/lib/rdoc/known_classes.rb index eb90185f53..3feb31eae1 100644 --- a/lib/rdoc/known_classes.rb +++ b/lib/rdoc/known_classes.rb @@ -36,7 +36,7 @@ module RDoc "rb_eArgError" => "ArgError", "rb_eEOFError" => "EOFError", "rb_eException" => "Exception", - "rb_eFatal" => "Fatal", + "rb_eFatal" => "fatal", "rb_eFloatDomainError" => "FloatDomainError", "rb_eIOError" => "IOError", "rb_eIndexError" => "IndexError", @@ -49,7 +49,7 @@ module RDoc "rb_eRuntimeError" => "RuntimeError", "rb_eScriptError" => "ScriptError", "rb_eSecurityError" => "SecurityError", - "rb_eSignal" => "Signal", + "rb_eSignal" => "SignalException", "rb_eStandardError" => "StandardError", "rb_eSyntaxError" => "SyntaxError", "rb_eSystemCallError" => "SystemCallError", diff --git a/lib/rdoc/markup.rb b/lib/rdoc/markup.rb index 73de2d06f8..bc28522615 100644 --- a/lib/rdoc/markup.rb +++ b/lib/rdoc/markup.rb @@ -48,13 +48,13 @@ require 'rdoc' # end # end # -# m = RDoc::Markup.new -# m.add_word_pair("{", "}", :STRIKE) -# m.add_html("no", :STRIKE) +# markup = RDoc::Markup.new +# markup.add_word_pair("{", "}", :STRIKE) +# markup.add_html("no", :STRIKE) # -# m.add_special(/\b([A-Z][a-z]+[A-Z]\w+)/, :WIKIWORD) +# markup.add_special(/\b([A-Z][a-z]+[A-Z]\w+)/, :WIKIWORD) # -# wh = WikiHtml.new +# wh = WikiHtml.new markup # wh.add_tag(:STRIKE, "", "") # # puts "#{wh.convert ARGF.read}" @@ -498,15 +498,53 @@ require 'rdoc' # [+:main:+ _name_] # Equivalent to the --main command line parameter. # +# [:category: section] +# Adds this item to the named +section+ overriding the current section. Use +# this to group methods by section in RDoc output while maintaining a +# sensible ordering (like alphabetical). +# +# # :category: Utility Methods +# # +# # CGI escapes +text+ +# +# def convert_string text +# CGI.escapeHTML text +# end +# +# An empty category will place the item in the default category: +# +# # :category: +# # +# # This method is in the default category +# +# def some_method +# # ... +# end +# +# Unlike the :section: directive, :category: is not sticky. The category +# only applies to the item immediately following the comment. +# +# Use the :section: directive to provide introductory text for a section of +# documentation. +# # [:section: title] -# 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. +# Provides section introductory text in RDoc output. The title following +# +:section:+ is used as the section name and the remainder of the comment +# containing the section is used as introductory text. A section's comment +# block must be separated from following comment blocks. Use an empty title +# to switch to the default section. +# +# The :section: directive is sticky, so subsequent methods, aliases, +# attributes, and classes will be contained in this section until the +# section is changed. The :category: directive will override the :section: +# directive. # # 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: +# the block are also removed. This allows you to add visual cues to the +# section. +# +# Example: # # # ---------------------------------------- # # :section: My Section @@ -514,10 +552,12 @@ require 'rdoc' # # See it glisten in the noon-day sun. # # ---------------------------------------- # -# Sections may be referenced multiple times in a class or module allowing -# methods, attributes and constants to be ordered one way for implementation -# ordering but still grouped together in documentation. If a section has -# multiple comments they will be concatenated with a dividing rule. +# ## +# # Comment for some_method +# +# def some_method +# # ... +# end # # [+:call-seq:+] # Lines up to the next blank line in the comment are treated as the method's diff --git a/lib/rdoc/markup/document.rb b/lib/rdoc/markup/document.rb index 688e8e822e..b4e070285e 100644 --- a/lib/rdoc/markup/document.rb +++ b/lib/rdoc/markup/document.rb @@ -3,6 +3,12 @@ class RDoc::Markup::Document + ## + # The file this document was created from. See also + # RDoc::ClassModule#add_comment + + attr_accessor :file + ## # The parts of the Document @@ -14,6 +20,8 @@ class RDoc::Markup::Document def initialize *parts @parts = [] @parts.push(*parts) + + @file = nil end ## @@ -36,7 +44,9 @@ class RDoc::Markup::Document end def == other # :nodoc: - self.class == other.class and @parts == other.parts + self.class == other.class and + @file == other.file and + @parts == other.parts end ## @@ -59,8 +69,30 @@ class RDoc::Markup::Document @parts.empty? end + ## + # When this is a collection of documents (#file is not set and this document + # contains only other documents as its direct children) #merge replaces + # documents in this class with documents from +other+ when the file matches + # and adds documents from +other+ when the files do not. + # + # The information in +other+ is preferred over the receiver + + def merge other + other.parts.each do |other_part| + self.parts.delete_if do |self_part| + self_part.file and self_part.file == other_part.file + end + + self.parts << other_part + end + + self + end + def pretty_print q # :nodoc: - q.group 2, '[doc: ', ']' do + start = @file ? "[doc (#{@file}): " : '[doc: ' + + q.group 2, start, ']' do q.seplist @parts do |part| q.pp part end diff --git a/lib/rdoc/markup/formatter.rb b/lib/rdoc/markup/formatter.rb index 9308954de1..b6e12f82e7 100644 --- a/lib/rdoc/markup/formatter.rb +++ b/lib/rdoc/markup/formatter.rb @@ -16,15 +16,25 @@ class RDoc::Markup::Formatter ## # Creates a new Formatter - def initialize - @markup = RDoc::Markup.new - @am = @markup.attribute_manager + def initialize markup = nil + @markup = markup || RDoc::Markup.new + @am = @markup.attribute_manager + @attr_tags = [] @in_tt = 0 @tt_bit = RDoc::Markup::Attribute.bitmap_for :TT end + ## + # Adds +document+ to the output + + def accept_document document + document.parts.each do |item| + item.accept self + end + end + ## # Add a new set of tags for an attribute. We allow separate start and end # tags for flexibility diff --git a/lib/rdoc/markup/formatter_test_case.rb b/lib/rdoc/markup/formatter_test_case.rb index 26dc4b25e9..c739f990b3 100644 --- a/lib/rdoc/markup/formatter_test_case.rb +++ b/lib/rdoc/markup/formatter_test_case.rb @@ -119,6 +119,16 @@ class RDoc::Markup::FormatterTestCase < MiniTest::Unit::TestCase accept_blank_line end + ## + # Test case that calls @to.accept_document + + def test_accept_document + @to.start_accepting + @to.accept_document @RM::Document.new @RM::Paragraph.new 'hello' + + accept_document + end + ## # Calls accept_heading with a level 5 RDoc::Markup::Heading diff --git a/lib/rdoc/markup/indented_paragraph.rb b/lib/rdoc/markup/indented_paragraph.rb new file mode 100644 index 0000000000..d995c7d8ed --- /dev/null +++ b/lib/rdoc/markup/indented_paragraph.rb @@ -0,0 +1,33 @@ +## +# An Indented Paragraph of text + +class RDoc::Markup::IndentedParagraph < RDoc::Markup::Raw + + ## + # The indent in number of spaces + + attr_reader :indent + + ## + # Creates a new IndentedParagraph containing +parts+ indented with +indent+ + # spaces + + def initialize indent, *parts + @indent = indent + + super(*parts) + end + + def == other # :nodoc: + super and indent == other.indent + end + + ## + # Calls #accept_indented_paragraph on +visitor+ + + def accept visitor + visitor.accept_indented_paragraph self + end + +end + diff --git a/lib/rdoc/markup/parser.rb b/lib/rdoc/markup/parser.rb index c94b900154..68616d7787 100644 --- a/lib/rdoc/markup/parser.rb +++ b/lib/rdoc/markup/parser.rb @@ -478,6 +478,7 @@ require 'rdoc/markup/list' require 'rdoc/markup/list_item' require 'rdoc/markup/raw' require 'rdoc/markup/paragraph' +require 'rdoc/markup/indented_paragraph' require 'rdoc/markup/rule' require 'rdoc/markup/verbatim' diff --git a/lib/rdoc/markup/pre_process.rb b/lib/rdoc/markup/pre_process.rb index ccc2688050..03f919aa0e 100644 --- a/lib/rdoc/markup/pre_process.rb +++ b/lib/rdoc/markup/pre_process.rb @@ -74,6 +74,13 @@ class RDoc::Markup::PreProcess filename = param.split[0] encoding = if defined?(Encoding) then text.encoding else nil end include_file filename, prefix, encoding + when 'category' then + if RDoc::Context === code_object then + section = code_object.add_section param, '' + code_object.temporary_section = section + end + + '' # ignore category if we're not on an RDoc::Context else result = yield directive, param if block_given? diff --git a/lib/rdoc/markup/to_ansi.rb b/lib/rdoc/markup/to_ansi.rb index c9f874ea3c..108a038075 100644 --- a/lib/rdoc/markup/to_ansi.rb +++ b/lib/rdoc/markup/to_ansi.rb @@ -8,7 +8,7 @@ class RDoc::Markup::ToAnsi < RDoc::Markup::ToRdoc ## # Creates a new ToAnsi visitor that is ready to output vibrant ANSI color! - def initialize + def initialize markup = nil super @headings.clear diff --git a/lib/rdoc/markup/to_bs.rb b/lib/rdoc/markup/to_bs.rb index 931edd81ea..32b1bbb9eb 100644 --- a/lib/rdoc/markup/to_bs.rb +++ b/lib/rdoc/markup/to_bs.rb @@ -11,7 +11,7 @@ class RDoc::Markup::ToBs < RDoc::Markup::ToRdoc ## # Returns a new ToBs that is ready for hot backspace action! - def initialize + def initialize markup = nil super @in_b = false diff --git a/lib/rdoc/markup/to_html.rb b/lib/rdoc/markup/to_html.rb index 599f3713f1..b518fbf265 100644 --- a/lib/rdoc/markup/to_html.rb +++ b/lib/rdoc/markup/to_html.rb @@ -17,7 +17,7 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter LIST_TYPE_TO_HTML = { :BULLET => [''], - :LABEL => ['
', '
'], + :LABEL => ['
', '
'], :LALPHA => ['
    ', '
'], :NOTE => ['', '
'], :NUMBER => ['
    ', '
'], @@ -62,7 +62,7 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter ## # Creates a new formatter that will output HTML - def initialize + def initialize markup = nil super @th = nil diff --git a/lib/rdoc/markup/to_html_crossref.rb b/lib/rdoc/markup/to_html_crossref.rb index 026defb862..aeecc3f4d0 100644 --- a/lib/rdoc/markup/to_html_crossref.rb +++ b/lib/rdoc/markup/to_html_crossref.rb @@ -104,9 +104,10 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml # 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, hyperlink_all = false) + def initialize(from_path, context, show_hash, hyperlink_all = false, + markup = nil) raise ArgumentError, 'from_path cannot be nil' if from_path.nil? - super() + super markup crossref_re = hyperlink_all ? ALL_CROSSREF_REGEXP : CROSSREF_REGEXP diff --git a/lib/rdoc/markup/to_rdoc.rb b/lib/rdoc/markup/to_rdoc.rb index b10af036d9..6f2faac2f6 100644 --- a/lib/rdoc/markup/to_rdoc.rb +++ b/lib/rdoc/markup/to_rdoc.rb @@ -44,7 +44,7 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter ## # Creates a new formatter that will output (mostly) \RDoc markup - def initialize + def initialize markup = nil super @markup.add_special(/\\\S/, :SUPPRESSED_CROSSREF) @@ -171,6 +171,15 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter wrap attributes(paragraph.text) end + ## + # Adds +paragraph+ to the output + + def accept_indented_paragraph paragraph + @indent += paragraph.indent + wrap attributes(paragraph.text) + @indent -= paragraph.indent + end + ## # Adds +raw+ to the output diff --git a/lib/rdoc/markup/to_test.rb b/lib/rdoc/markup/to_test.rb index f79f9475f1..4847fd29f7 100644 --- a/lib/rdoc/markup/to_test.rb +++ b/lib/rdoc/markup/to_test.rb @@ -21,7 +21,7 @@ class RDoc::Markup::ToTest < RDoc::Markup::Formatter end def accept_paragraph(paragraph) - @res << paragraph.text + @res << convert_flow(@am.flow(paragraph.text)) end def accept_raw raw diff --git a/lib/rdoc/markup/to_tt_only.rb b/lib/rdoc/markup/to_tt_only.rb index 98ad2f6936..078e87db98 100644 --- a/lib/rdoc/markup/to_tt_only.rb +++ b/lib/rdoc/markup/to_tt_only.rb @@ -20,7 +20,7 @@ class RDoc::Markup::ToTtOnly < RDoc::Markup::Formatter ## # Creates a new tt-only formatter. - def initialize + def initialize markup = nil super add_tag :TT, nil, nil diff --git a/lib/rdoc/parser/c.rb b/lib/rdoc/parser/c.rb index 12efb407a2..7d05d12fb4 100644 --- a/lib/rdoc/parser/c.rb +++ b/lib/rdoc/parser/c.rb @@ -1,4 +1,4 @@ -require 'rdoc/parser' + require 'rdoc/parser/ruby' require 'rdoc/known_classes' @@ -64,8 +64,17 @@ require 'rdoc/known_classes' # [Document-variable: +name+] # Documentation for the named +rb_define_variable+ # -# [Document-method: +name+] -# Documentation for the named method. +# [Document-method: +method_name+] +# Documentation for the named method. Use this when the method name is +# unambiguous. +# +# [Document-method: ClassName::method_name] +# Documentation for a singleton method in the given class. Use this when +# the method name alone is ambiguous. +# +# [Document-method: ClassName#method_name] +# Documentation for a instance method in the given class. Use this when the +# method name alone is ambiguous. # # [Document-attr: +name+] # Documentation for the named attribute. @@ -113,6 +122,17 @@ class RDoc::Parser::C < RDoc::Parser attr_accessor :content + + ## + # Maps C variable names to names of ruby classes (andsingleton classes) + + attr_reader :known_classes + + ## + # Maps C variable names to names of ruby singleton classes + + attr_reader :singleton_classes + ## # Resets cross-file state. Call when parsing different projects that need # separate documentation. @@ -132,8 +152,8 @@ 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 + @classes = {} + @singleton_classes = {} @file_dir = File.dirname(@file_name) end @@ -146,17 +166,25 @@ class RDoc::Parser::C < RDoc::Parser \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_name = @known_classes[var_name] + + unless class_name then + warn "Enclosing class/module %p for alias %s %s not known" % [ + var_name, new_name, old_name] + next + end + + class_obj = find_class var_name, class_name al = RDoc::Alias.new '', old_name, new_name, '' - al.singleton = @singleton_classes.key?(var_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 al.record_location @top_level + class_obj.add_alias al @stats.add_alias al end @@ -262,6 +290,18 @@ class RDoc::Parser::C < RDoc::Parser var_name = "rb_cObject" if !var_name or var_name == "rb_mKernel" handle_constants type, var_name, const_name, definition end + + @content.scan(%r% + \Wrb_curses_define_const + \s*\( + \s* + (\w+) + \s* + \) + \s*;%xm) do |consts| + const = consts.first + handle_constants 'const', 'mCurses', const, "UINT2NUM(#{const})" + end end ## @@ -271,7 +311,8 @@ class RDoc::Parser::C < RDoc::Parser @content.scan(/rb_include_module\s*\(\s*(\w+?),\s*(\w+?)\s*\)/) do |c,m| if cls = @classes[c] m = @known_classes[m] || m - cls.add_include RDoc::Include.new(m, "") + incl = cls.add_include RDoc::Include.new(m, "") + incl.record_location @top_level end end end @@ -293,7 +334,7 @@ class RDoc::Parser::C < RDoc::Parser \s*"([^"]+)", \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?, \s*(-?\w+)\s*\) - (?:;\s*/[*/]\s+in\s+(\w+?\.[cy]))? + (?:;\s*/[*/]\s+in\s+(\w+?\.(?:cpp|c|y)))? %xm) do |type, var_name, meth_name, function, param_count, source_file| # Ignore top-object and weird struct.c dynamic stuff @@ -401,7 +442,7 @@ class RDoc::Parser::C < RDoc::Parser # distinct (for example Kernel.hash and Kernel.object_id share the same # implementation - override_comment = find_override_comment class_name, meth_obj.name + override_comment = find_override_comment class_name, meth_obj comment = override_comment if override_comment find_modifiers comment, meth_obj if comment @@ -444,7 +485,7 @@ class RDoc::Parser::C < RDoc::Parser 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 + comment = find_override_comment class_name, meth_obj if comment then find_modifiers comment, meth_obj @@ -524,21 +565,26 @@ class RDoc::Parser::C < RDoc::Parser comment = look_for_directives_in class_mod, comment - class_mod.comment = comment + class_mod.add_comment comment, @top_level end ## # Finds a comment matching +type+ and +const_name+ either above the # comment or in the matching Document- section. - def find_const_comment(type, const_name) + def find_const_comment(type, const_name, class_name = nil) if @content =~ %r%((?>^\s*/\*.*?\*/\s+)) rb_define_#{type}\((?:\s*(\w+),)?\s* "#{const_name}"\s*, .*?\)\s*;%xmi then $1 + elsif class_name and + @content =~ %r%Document-(?:const|global|variable):\s + #{class_name}::#{const_name} + \s*?\n((?>.*?\*/))%xm then + $1 elsif @content =~ %r%Document-(?:const|global|variable):\s#{const_name} - \s*?\n((?>.*?\*/))%xm + \s*?\n((?>.*?\*/))%xm then $1 else '' @@ -607,12 +653,13 @@ class RDoc::Parser::C < RDoc::Parser end ## - # Finds a Document-method override for +meth_name+ in +class_name+ + # Finds a Document-method override for +meth_obj+ on +class_name+ - def find_override_comment(class_name, meth_name) - name = Regexp.escape(meth_name) + def find_override_comment class_name, meth_obj + name = Regexp.escape meth_obj.name + prefix = Regexp.escape meth_obj.name_prefix - if @content =~ %r%Document-method:\s+#{class_name}(?:\.|::|#)#{name}\s*?\n((?>.*?\*/))%m then + if @content =~ %r%Document-method:\s+#{class_name}#{prefix}#{name}\s*?\n((?>.*?\*/))%m then $1 elsif @content =~ %r%Document-method:\s#{name}\s*?\n((?>.*?\*/))%m then $1 @@ -726,7 +773,7 @@ class RDoc::Parser::C < RDoc::Parser return end - comment = find_const_comment type, const_name + comment = find_const_comment type, const_name, class_name comment = strip_stars comment comment = normalize_comment comment @@ -781,13 +828,8 @@ class RDoc::Parser::C < RDoc::Parser def handle_method(type, var_name, meth_name, function, 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 + singleton = @singleton_classes.key? var_name return unless class_name @@ -845,6 +887,7 @@ class RDoc::Parser::C < RDoc::Parser def handle_singleton sclass_var, class_var class_name = @known_classes[class_var] + @known_classes[sclass_var] = class_name @singleton_classes[sclass_var] = class_name end diff --git a/lib/rdoc/parser/ruby.rb b/lib/rdoc/parser/ruby.rb index 4d273598be..50071ab736 100644 --- a/lib/rdoc/parser/ruby.rb +++ b/lib/rdoc/parser/ruby.rb @@ -350,7 +350,9 @@ class RDoc::Parser::Ruby < RDoc::Parser def get_constant_with_optional_parens skip_tkspace false + nest = 0 + while TkLPAREN === (tk = peek_tk) or TkfLPAREN === tk do get_tk skip_tkspace @@ -631,7 +633,7 @@ class RDoc::Parser::Ruby < RDoc::Parser cls.offset = offset cls.line = line_no - cls.comment = comment if cls.document_self + cls.add_comment comment, @top_level if cls.document_self @top_level.add_to_classes_or_modules cls @stats.add_class cls @@ -650,7 +652,7 @@ class RDoc::Parser::Ruby < RDoc::Parser other.offset = offset other.line = line_no - other.comment = comment + other.add_comment comment, @top_level end # notify :nodoc: all if not a constant-named class/module @@ -826,14 +828,19 @@ class RDoc::Parser::Ruby < RDoc::Parser ## # Parses an +include+ in +context+ with +comment+ - def parse_include(context, comment) + def parse_include context, comment loop do skip_tkspace_comment name = get_constant_with_optional_parens - context.add_include RDoc::Include.new(name, comment) unless name.empty? + + unless name.empty? then + incl = context.add_include RDoc::Include.new(name, comment) + incl.record_location @top_level + end return unless TkCOMMA === peek_tk + get_tk end end @@ -1231,7 +1238,7 @@ 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 + mod.add_comment comment, @top_level if mod.document_self parse_statements(mod) @top_level.add_to_classes_or_modules mod @@ -1295,9 +1302,12 @@ class RDoc::Parser::Ruby < RDoc::Parser while TkCOMMENT === tk do comment << tk.text << "\n" - tk = get_tk # this is the newline - skip_tkspace false # leading spaces tk = get_tk + + if TkNL === tk then + skip_tkspace false # leading spaces + tk = get_tk + end end unless comment.empty? then @@ -1313,7 +1323,7 @@ class RDoc::Parser::Ruby < RDoc::Parser non_comment_seen = true end - unget_tk tk + unget_tk tk # TODO peek instead of get then unget keep_comment = true when TkCLASS then diff --git a/lib/rdoc/rdoc.rb b/lib/rdoc/rdoc.rb index 6d7c4eace6..95ba9ae8ab 100644 --- a/lib/rdoc/rdoc.rb +++ b/lib/rdoc/rdoc.rb @@ -100,6 +100,14 @@ class RDoc::RDoc @current = rdoc end + ## + # Resets all internal state + + def self.reset + RDoc::TopLevel.reset + RDoc::Parser::C.reset + end + ## # Creates a new RDoc::RDoc instance. Call #document to parse files and # generate documentation. @@ -306,8 +314,12 @@ option) # Parses +filename+ and returns an RDoc::TopLevel def parse_file filename + if defined?(Encoding) then + encoding = @options.encoding + filename = filename.encode encoding + end + @stats.add_file filename - encoding = @options.encoding if defined?(Encoding) content = RDoc::Encoding.read_file filename, encoding @@ -396,8 +408,7 @@ The internal error was: # current directory, so make sure you're somewhere writable before invoking. def document options - RDoc::TopLevel.reset - RDoc::Parser::C.reset + RDoc::RDoc.reset if RDoc::Options === options then @options = options diff --git a/lib/rdoc/ri/driver.rb b/lib/rdoc/ri/driver.rb index 46aeaeebc2..2c6f2f48e2 100644 --- a/lib/rdoc/ri/driver.rb +++ b/lib/rdoc/ri/driver.rb @@ -456,9 +456,13 @@ Options may also be set in the 'RI' environment variable. out << RDoc::Markup::Heading.new(1, "#{name}:") out << RDoc::Markup::BlankLine.new - out.push(*methods.map do |method| - RDoc::Markup::Verbatim.new method - end) + if @use_stdout and !@interactive + out.push(*methods.map do |method| + RDoc::Markup::Verbatim.new method + end) + else + out << RDoc::Markup::IndentedParagraph.new(2, methods.join(', ')) + end out << RDoc::Markup::BlankLine.new end @@ -532,8 +536,9 @@ Options may also be set in the 'RI' environment variable. klass_name = method ? name : klass if name !~ /#|\./ then - completions = klasses.grep(/^#{klass_name}[^:]*$/) - completions.concat klasses.grep(/^#{name}[^:]*$/) if name =~ /::$/ + completions = klasses.grep(/^#{Regexp.escape klass_name}[^:]*$/) + completions.concat klasses.grep(/^#{Regexp.escape name}[^:]*$/) if + name =~ /::$/ completions << klass if classes.key? klass # to complete a method name elsif selector then diff --git a/lib/rdoc/ri/store.rb b/lib/rdoc/ri/store.rb index c3939c493a..e48386adcb 100644 --- a/lib/rdoc/ri/store.rb +++ b/lib/rdoc/ri/store.rb @@ -19,6 +19,8 @@ require 'fileutils' # :modules => [], # classes and modules in this store # :ancestors => {}, # class name => ancestor names # } +#-- +# TODO need to store the list of files and prune classes class RDoc::RI::Store @@ -43,20 +45,27 @@ class RDoc::RI::Store attr_reader :cache + ## + # The encoding of the contents in the Store + + attr_accessor :encoding + ## # Creates a new Store of +type+ that will load or save to +path+ def initialize path, type = nil - @dry_run = false - @type = type - @path = path + @dry_run = false + @type = type + @path = path + @encoding = nil @cache = { - :class_methods => {}, - :instance_methods => {}, - :attributes => {}, - :modules => [], :ancestors => {}, + :attributes => {}, + :class_methods => {}, + :encoding => @encoding, + :instance_methods => {}, + :modules => [], } end @@ -106,6 +115,22 @@ class RDoc::RI::Store File.join @path, *klass_name.split('::') end + ## + # Removes empty items and ensures item in each collection are unique and + # sorted + + def clean_cache_collection collection # :nodoc: + collection.each do |name, item| + if item.empty? then + collection.delete name + else + # HACK mongrel-1.1.5 documents its files twice + item.uniq! + item.sort! + end + end + end + ## # Friendly rendition of #path @@ -138,9 +163,29 @@ class RDoc::RI::Store # Loads cache file for this store def load_cache + #orig_enc = @encoding + open cache_path, 'rb' do |io| @cache = Marshal.load io.read end + + load_enc = @cache[:encoding] + + # TODO this feature will be time-consuming to add: + # a) Encodings may be incompatible but transcodeable + # b) Need to warn in the appropriate spots, wherever they may be + # c) Need to handle cross-cache differences in encodings + # d) Need to warn when generating into a cache with diffent encodings + # + #if orig_enc and load_enc != orig_enc then + # warn "Cached encoding #{load_enc} is incompatible with #{orig_enc}\n" \ + # "from #{path}/cache.ri" unless + # Encoding.compatible? orig_enc, load_enc + #end + + @encoding = load_enc unless @encoding + + @cache rescue Errno::ENOENT end @@ -192,17 +237,21 @@ class RDoc::RI::Store # Writes the cache file for this store def save_cache - # HACK mongrel-1.1.5 documents its files twice - @cache[:ancestors]. each do |_, m| m.uniq!; m.sort! end - @cache[:attributes]. each do |_, m| m.uniq!; m.sort! end - @cache[:class_methods]. each do |_, m| m.uniq!; m.sort! end - @cache[:instance_methods].each do |_, m| m.uniq!; m.sort! end - @cache[:modules].uniq!; @cache[:modules].sort! + clean_cache_collection @cache[:ancestors] + clean_cache_collection @cache[:attributes] + clean_cache_collection @cache[:class_methods] + clean_cache_collection @cache[:instance_methods] + + @cache[:modules].uniq! + @cache[:modules].sort! + @cache[:encoding] = @encoding # this gets set twice due to assert_cache return if @dry_run + marshal = Marshal.dump @cache + open cache_path, 'wb' do |io| - Marshal.dump @cache, io + io.write marshal end end @@ -210,11 +259,13 @@ class RDoc::RI::Store # Writes the ri data for +klass+ def save_class klass - FileUtils.mkdir_p class_path(klass.full_name) unless @dry_run + full_name = klass.full_name - @cache[:modules] << klass.full_name + FileUtils.mkdir_p class_path(full_name) unless @dry_run - path = class_file klass.full_name + @cache[:modules] << full_name + + path = class_file full_name begin disk_klass = nil @@ -223,7 +274,7 @@ class RDoc::RI::Store disk_klass = Marshal.load io.read end - klass.merge disk_klass + klass = disk_klass.merge klass rescue Errno::ENOENT end @@ -233,22 +284,52 @@ class RDoc::RI::Store String === ancestor ? ancestor : ancestor.full_name end - @cache[:ancestors][klass.full_name] ||= [] - @cache[:ancestors][klass.full_name].push(*ancestors) + @cache[:ancestors][full_name] ||= [] + @cache[:ancestors][full_name].push(*ancestors) attributes = klass.attributes.map do |attribute| "#{attribute.definition} #{attribute.name}" end unless attributes.empty? then - @cache[:attributes][klass.full_name] ||= [] - @cache[:attributes][klass.full_name].push(*attributes) + @cache[:attributes][full_name] ||= [] + @cache[:attributes][full_name].push(*attributes) + end + + to_delete = [] + + unless klass.method_list.empty? then + @cache[:class_methods][full_name] ||= [] + @cache[:instance_methods][full_name] ||= [] + + class_methods, instance_methods = + klass.method_list.partition { |meth| meth.singleton } + + class_methods = class_methods. map { |method| method.name } + instance_methods = instance_methods.map { |method| method.name } + + old = @cache[:class_methods][full_name] - class_methods + to_delete.concat old.map { |method| + method_file full_name, "#{full_name}::#{method}" + } + + old = @cache[:instance_methods][full_name] - instance_methods + to_delete.concat old.map { |method| + method_file full_name, "#{full_name}##{method}" + } + + @cache[:class_methods][full_name] = class_methods + @cache[:instance_methods][full_name] = instance_methods end return if @dry_run + FileUtils.rm_f to_delete + + marshal = Marshal.dump klass + open path, 'wb' do |io| - Marshal.dump klass, io + io.write marshal end end @@ -256,20 +337,24 @@ 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) unless @dry_run + full_name = klass.full_name + + FileUtils.mkdir_p class_path(full_name) unless @dry_run cache = if method.singleton then @cache[:class_methods] else @cache[:instance_methods] end - cache[klass.full_name] ||= [] - cache[klass.full_name] << method.name + cache[full_name] ||= [] + cache[full_name] << method.name return if @dry_run - open method_file(klass.full_name, method.full_name), 'wb' do |io| - Marshal.dump method, io + marshal = Marshal.dump method + + open method_file(full_name, method.full_name), 'wb' do |io| + io.write marshal end end diff --git a/lib/rdoc/rubygems_hook.rb b/lib/rdoc/rubygems_hook.rb new file mode 100644 index 0000000000..178ca1d2ae --- /dev/null +++ b/lib/rdoc/rubygems_hook.rb @@ -0,0 +1,220 @@ +require 'rubygems' +require 'rubygems/user_interaction' +require 'fileutils' +require 'rdoc' + +## +# Gem::RDoc provides methods to generate RDoc and ri data for installed gems +# upon gem installation. +# +# This file is automatically required by RubyGems 1.9 and newer. + +class RDoc::RubygemsHook + + include Gem::UserInteraction + + @rdoc_version = nil + @specs = [] + + ## + # Force installation of documentation? + + attr_accessor :force + + ## + # Generate rdoc? + + attr_accessor :generate_rdoc + + ## + # Generate ri data? + + attr_accessor :generate_ri + + class << self + + ## + # Loaded version of RDoc. Set by ::load_rdoc + + attr_reader :rdoc_version + + end + + ## + # Post installs hook that generates documentation for each specification in + # +specs+ + + def self.generation_hook installer, specs + types = installer.document + + generate_rdoc = types.include? 'rdoc' + generate_ri = types.include? 'ri' + + specs.each do |spec| + new(spec, generate_rdoc, generate_ri).generate + end + end + + ## + # Loads the RDoc generator + + def self.load_rdoc + return if @rdoc_version + + require 'rdoc/rdoc' + + @rdoc_version = Gem::Version.new ::RDoc::VERSION + end + + ## + # Creates a new documentation generator for +spec+. RDoc and ri data + # generation can be disabled through +generate_rdoc+ and +generate_ri+ + # respectively. + + def initialize spec, generate_rdoc = true, generate_ri = true + @doc_dir = spec.doc_dir + @file_info = nil + @force = false + @rdoc = nil + @spec = spec + + @generate_rdoc = generate_rdoc + @generate_ri = generate_ri + + @rdoc_dir = spec.doc_dir 'rdoc' + @ri_dir = spec.doc_dir 'ri' + end + + ## + # Removes legacy rdoc arguments from +args+ + #-- + # TODO move to RDoc::Options + + def delete_legacy_args args + args.delete '--inline-source' + args.delete '--promiscuous' + args.delete '-p' + args.delete '--one-file' + end + + ## + # Generates documentation using the named +generator+ ("darkfish" or "ri") + # and following the given +options+. + # + # Documentation will be generated into +destination+ + + def document generator, options, destination + options = options.dup + options.exclude ||= [] # TODO maybe move to RDoc::Options#finish + options.setup_generator generator + options.op_dir = destination + options.finish + + @rdoc.options = options + @rdoc.generator = options.generator.new options + + say "Installing #{generator} documentation for #{@spec.full_name}" + + FileUtils.mkdir_p options.op_dir + + Dir.chdir options.op_dir do + begin + @rdoc.class.current = @rdoc + @rdoc.generator.generate @file_info + ensure + @rdoc.class.current = nil + end + end + end + + ## + # Generates RDoc and ri data + + def generate + return unless @generate_ri or @generate_rdoc + + setup + + ::RDoc::RDoc.reset + + options = ::RDoc::Options.new + options.default_title = "#{@spec.full_name} Documentation" + options.files = [] + options.files.push(*@spec.require_paths) + options.files.push(*@spec.extra_rdoc_files) + + args = @spec.rdoc_options + + case config_args = Gem.configuration[:rdoc] + when String then + args = args.concat config_args.split + when Array then + args = args.concat config_args + end + + delete_legacy_args args + options.parse args + options.quiet = !Gem.configuration.really_verbose + + @rdoc = new_rdoc + @rdoc.options = options + + Dir.chdir @spec.full_gem_path do + @file_info = @rdoc.parse_files options.files + end + + document 'ri', options, @ri_dir if + @generate_ri and (@force or not File.exist? @ri_dir) + + document 'darkfish', options, @rdoc_dir if + @generate_rdoc and (@force or not File.exist? @rdoc_dir) + end + + ## + # #new_rdoc creates a new RDoc instance. This method is provided only to + # make testing easier. + + def new_rdoc # :nodoc: + ::RDoc::RDoc.new + end + + ## + # Is rdoc documentation installed? + + def rdoc_installed? + File.exist? @rdoc_dir + end + + ## + # Removes generated RDoc and ri data + + def remove + base_dir = @spec.base_dir + + raise Gem::FilePermissionError, base_dir unless File.writable? base_dir + + FileUtils.rm_rf @rdoc_dir + FileUtils.rm_rf @ri_dir + end + + ## + # Is ri data installed? + + def ri_installed? + File.exist? @ri_dir + end + + ## + # Prepares the spec for documentation generation + + def setup + self.class.load_rdoc + + raise Gem::FilePermissionError, @doc_dir if + File.exist?(@doc_dir) and not File.writable?(@doc_dir) + + FileUtils.mkdir_p @doc_dir unless File.exist? @doc_dir + end + +end + diff --git a/lib/rdoc/text.rb b/lib/rdoc/text.rb index ace44bf56f..aec334b545 100644 --- a/lib/rdoc/text.rb +++ b/lib/rdoc/text.rb @@ -157,7 +157,7 @@ http://rubyforge.org/tracker/?atid=2472&group_id=627&func=browse def strip_stars text encoding = text.encoding if Object.const_defined? :Encoding - text = text.gsub %r%Document-method:\s+[\w:.#]+%, '' + text = text.gsub %r%Document-method:\s+[\w:.#=!?]+%, '' space = ' ' space.force_encoding encoding if encoding diff --git a/lib/rdoc/top_level.rb b/lib/rdoc/top_level.rb index d0ea621f8c..b9fd5c9f6f 100644 --- a/lib/rdoc/top_level.rb +++ b/lib/rdoc/top_level.rb @@ -219,6 +219,20 @@ class RDoc::TopLevel < RDoc::Context end end + ## + # Creates a new RDoc::TopLevel with +file_name+ only if one with the same + # name does not exist in all_files. + + def self.new file_name + if top_level = @all_files_hash[file_name] then + top_level + else + top_level = super + @all_files_hash[file_name] = top_level + top_level + end + end + ## # Removes from +all_hash+ the contexts that are nodoc or have no content. # @@ -295,6 +309,15 @@ class RDoc::TopLevel < RDoc::Context RDoc::TopLevel.files_hash[file_name] = self end + ## + # An RDoc::TopLevel is equal to another with the same absolute_name + + def == other + other.class === self and @absolute_name == other.absolute_name + end + + alias eql? == + ## # Adds +an_alias+ to +Object+ instead of +self+. @@ -375,6 +398,14 @@ class RDoc::TopLevel < RDoc::Context @relative_name end + ## + # An RDoc::TopLevel has the same hash as another with the same + # absolute_name + + def hash + @absolute_name.hash + end + ## # URL for this with a +prefix+ diff --git a/test/rdoc/test_rdoc_any_method.rb b/test/rdoc/test_rdoc_any_method.rb index 930d9773bd..c0feccef95 100644 --- a/test/rdoc/test_rdoc_any_method.rb +++ b/test/rdoc/test_rdoc_any_method.rb @@ -83,6 +83,40 @@ method(a, b) { |c, d| ... } assert_equal '', @c2_a.markup_code end + def test_marshal_dump + top_level = RDoc::TopLevel.new 'file.rb' + m = RDoc::AnyMethod.new nil, 'method' + m.block_params = 'some_block' + m.call_seq = 'call_seq' + m.comment = 'this is a comment' + m.params = 'param' + m.record_location top_level + + cm = RDoc::ClassModule.new 'Klass' + cm.add_method m + + al = RDoc::Alias.new nil, 'method', 'aliased', 'alias comment' + al_m = m.add_alias al, cm + + loaded = Marshal.load Marshal.dump m + + comment = RDoc::Markup::Document.new( + RDoc::Markup::Paragraph.new('this is a comment')) + + assert_equal m, loaded + + assert_equal [al_m], loaded.aliases + assert_equal 'some_block', loaded.block_params + assert_equal 'call_seq', loaded.call_seq + assert_equal comment, loaded.comment + assert_equal top_level, loaded.file + assert_equal 'Klass#method', loaded.full_name + assert_equal 'method', loaded.name + assert_equal 'param', loaded.params + assert_equal nil, loaded.singleton # defaults to nil + assert_equal :public, loaded.visibility + end + def test_marshal_load instance_method = Marshal.load Marshal.dump(@c1.method_list.last) @@ -103,6 +137,40 @@ method(a, b) { |c, d| ... } assert_equal '()', class_method.params end + def test_marshal_load_version_0 + m = RDoc::AnyMethod.new nil, 'method' + cm = RDoc::ClassModule.new 'Klass' + cm.add_method m + al = RDoc::Alias.new nil, 'method', 'aliased', 'alias comment' + al_m = m.add_alias al, cm + + loaded = Marshal.load "\x04\bU:\x14RDoc::AnyMethod[\x0Fi\x00I" \ + "\"\vmethod\x06:\x06EF\"\x11Klass#method0:\vpublic" \ + "o:\eRDoc::Markup::Document\x06:\v@parts[\x06" \ + "o:\x1CRDoc::Markup::Paragraph\x06;\t[\x06I" \ + "\"\x16this is a comment\x06;\x06FI" \ + "\"\rcall_seq\x06;\x06FI\"\x0Fsome_block\x06;\x06F" \ + "[\x06[\aI\"\faliased\x06;\x06Fo;\b\x06;\t[\x06" \ + "o;\n\x06;\t[\x06I\"\x12alias comment\x06;\x06FI" \ + "\"\nparam\x06;\x06F" + + comment = RDoc::Markup::Document.new( + RDoc::Markup::Paragraph.new('this is a comment')) + + assert_equal m, loaded + + assert_equal [al_m], loaded.aliases + assert_equal 'some_block', loaded.block_params + assert_equal 'call_seq', loaded.call_seq + assert_equal comment, loaded.comment + assert_equal 'Klass#method', loaded.full_name + assert_equal 'method', loaded.name + assert_equal 'param', loaded.params + assert_equal nil, loaded.singleton # defaults to nil + assert_equal :public, loaded.visibility + assert_equal nil, loaded.file + end + def test_name m = RDoc::AnyMethod.new nil, nil diff --git a/test/rdoc/test_rdoc_attr.rb b/test/rdoc/test_rdoc_attr.rb index 9751cc175d..b69d8c6499 100644 --- a/test/rdoc/test_rdoc_attr.rb +++ b/test/rdoc/test_rdoc_attr.rb @@ -42,6 +42,68 @@ class TestRDocAttr < MiniTest::Unit::TestCase assert_equal '(unknown)#attr', @a.full_name end + def test_marshal_dump + tl = RDoc::TopLevel.new 'file.rb' + + @a.comment = 'this is a comment' + @a.record_location tl + + cm = RDoc::ClassModule.new 'Klass' + cm.add_attribute @a + + loaded = Marshal.load Marshal.dump @a + + assert_equal @a, loaded + + comment = RDoc::Markup::Document.new( + RDoc::Markup::Paragraph.new('this is a comment')) + + assert_equal comment, loaded.comment + assert_equal 'file.rb', loaded.file.relative_name + assert_equal 'Klass#attr', loaded.full_name + assert_equal 'attr', loaded.name + assert_equal 'RW', loaded.rw + assert_equal false, loaded.singleton + assert_equal :public, loaded.visibility + + @a.rw = 'R' + @a.singleton = true + @a.visibility = :protected + + loaded = Marshal.load Marshal.dump @a + + assert_equal @a, loaded + + assert_equal comment, loaded.comment + assert_equal 'Klass::attr', loaded.full_name + assert_equal 'attr', loaded.name + assert_equal 'R', loaded.rw + assert_equal true, loaded.singleton + assert_equal :protected, loaded.visibility + end + + def test_marshal_load_version_1 + data = "\x04\bU:\x0FRDoc::Attr[\fi\x06I\"\tattr\x06:\x06EF" \ + "\"\x0FKlass#attrI\"\aRW\x06;\x06F:\vpublic" \ + "o:\eRDoc::Markup::Document\x06:\v@parts[\x06" \ + "o:\x1CRDoc::Markup::Paragraph\x06;\t[\x06I" \ + "\"\x16this is a comment\x06;\x06FF" + + loaded = Marshal.load data + + comment = RDoc::Markup::Document.new( + RDoc::Markup::Paragraph.new('this is a comment')) + + assert_equal comment, loaded.comment + assert_equal 'Klass#attr', loaded.full_name + assert_equal 'attr', loaded.name + assert_equal 'RW', loaded.rw + assert_equal false, loaded.singleton + assert_equal :public, loaded.visibility + + assert_equal nil, loaded.file # version 2 + end + def test_params assert_nil @a.params end diff --git a/test/rdoc/test_rdoc_class_module.rb b/test/rdoc/test_rdoc_class_module.rb index 59190a6e3b..1ac0eda298 100644 --- a/test/rdoc/test_rdoc_class_module.rb +++ b/test/rdoc/test_rdoc_class_module.rb @@ -1,3 +1,4 @@ +require 'pp' require File.expand_path '../xref_test_case', __FILE__ class TestRDocClassModule < XrefTestCase @@ -8,6 +9,37 @@ class TestRDocClassModule < XrefTestCase @RM = RDoc::Markup end + def mu_pp obj + s = '' + s = PP.pp obj, s + s.force_encoding Encoding.default_external if defined? Encoding + s.chomp + end + + def test_add_comment + tl1 = RDoc::TopLevel.new 'one.rb' + tl2 = RDoc::TopLevel.new 'two.rb' + tl3 = RDoc::TopLevel.new 'three.rb' + + cm = RDoc::ClassModule.new 'Klass' + cm.add_comment '# comment 1', tl1 + + assert_equal [['comment 1', tl1]], cm.comment_location + assert_equal 'comment 1', cm.comment + + cm.add_comment '# comment 2', tl2 + + assert_equal [['comment 1', tl1], ['comment 2', tl2]], cm.comment_location + assert_equal "comment 1\n---\ncomment 2", cm.comment + + cm.add_comment "# * comment 3", tl3 + + assert_equal [['comment 1', tl1], + ['comment 2', tl2], + ['* comment 3', tl3]], cm.comment_location + assert_equal "comment 1\n---\ncomment 2\n---\n* comment 3", cm.comment + end + def test_ancestors assert_equal [@parent], @child.ancestors end @@ -45,70 +77,468 @@ class TestRDocClassModule < XrefTestCase assert_nil @c2.find_class_named('C1') end - def test_merge + def test_from_module_comment + tl = RDoc::TopLevel.new 'file.rb' + klass = tl.add_class RDoc::NormalModule, 'Klass' + klass.add_comment 'really a class', tl + + klass = RDoc::ClassModule.from_module RDoc::NormalClass, klass + + assert_equal [['really a class', tl]], klass.comment_location + end + + def test_marshal_dump + tl = RDoc::TopLevel.new 'file.rb' + + ns = tl.add_module RDoc::NormalModule, 'Namespace' + + cm = ns.add_class RDoc::NormalClass, 'Klass', 'Super' + cm.record_location tl + + a1 = RDoc::Attr.new nil, 'a1', 'RW', '' + a1.record_location tl + a2 = RDoc::Attr.new nil, 'a2', 'RW', '', true + a2.record_location tl + + m1 = RDoc::AnyMethod.new nil, 'm1' + m1.record_location tl + + c1 = RDoc::Constant.new 'C1', nil, '' + c1.record_location tl + + i1 = RDoc::Include.new 'I1', '' + i1.record_location tl + + cm.add_attribute a1 + cm.add_attribute a2 + cm.add_method m1 + cm.add_constant c1 + cm.add_include i1 + cm.add_comment 'this is a comment', tl + + loaded = Marshal.load Marshal.dump cm + + assert_equal cm, loaded + + inner = RDoc::Markup::Document.new( + RDoc::Markup::Paragraph.new('this is a comment')) + inner.file = tl.absolute_name + + comment = RDoc::Markup::Document.new inner + + assert_equal [a2, a1], loaded.attributes.sort + assert_equal comment, loaded.comment + assert_equal [c1], loaded.constants + assert_equal 'Namespace::Klass', loaded.full_name + assert_equal [i1], loaded.includes + assert_equal [m1], loaded.method_list + assert_equal 'Klass', loaded.name + assert_equal 'Super', loaded.superclass + + assert_equal tl, loaded.attributes.first.file + + assert_equal tl, loaded.constants.first.file + + assert_equal tl, loaded.includes.first.file + + assert_equal tl, loaded.method_list.first.file + end + + def test_marshal_load_version_0 + tl = RDoc::TopLevel.new 'file.rb' + ns = tl.add_module RDoc::NormalModule, 'Namespace' + cm = ns.add_class RDoc::NormalClass, 'Klass', 'Super' + + a = RDoc::Attr.new(nil, 'a1', 'RW', '') + m = RDoc::AnyMethod.new(nil, 'm1') + c = RDoc::Constant.new('C1', nil, '') + i = RDoc::Include.new('I1', '') + + cm.add_attribute a + cm.add_method m + cm.add_constant c + cm.add_include i + cm.add_comment 'this is a comment', tl + + loaded = Marshal.load "\x04\bU:\x16RDoc::NormalClass[\x0Ei\x00\"\nKlass" \ + "\"\x15Namespace::KlassI\"\nSuper\x06:\x06EF" \ + "o:\eRDoc::Markup::Document\x06:\v@parts[\x06" \ + "o:\x1CRDoc::Markup::Paragraph\x06;\b[\x06I" \ + "\"\x16this is a comment\x06;\x06F[\x06[\aI" \ + "\"\aa1\x06;\x06FI\"\aRW\x06;\x06F[\x06[\aI" \ + "\"\aC1\x06;\x06Fo;\a\x06;\b[\x00[\x06[\aI" \ + "\"\aI1\x06;\x06Fo;\a\x06;\b[\x00[\a[\aI" \ + "\"\nclass\x06;\x06F[\b[\a:\vpublic[\x00[\a" \ + ":\x0Eprotected[\x00[\a:\fprivate[\x00[\aI" \ + "\"\rinstance\x06;\x06F[\b[\a;\n[\x06I" \ + "\"\am1\x06;\x06F[\a;\v[\x00[\a;\f[\x00" + + assert_equal cm, loaded + + comment = RDoc::Markup::Document.new( + RDoc::Markup::Paragraph.new('this is a comment')) + + assert_equal [a], loaded.attributes + assert_equal comment, loaded.comment + assert_equal [c], loaded.constants + assert_equal 'Namespace::Klass', loaded.full_name + assert_equal [i], loaded.includes + assert_equal [m], loaded.method_list + assert_equal 'Klass', loaded.name + assert_equal 'Super', loaded.superclass + assert_equal nil, loaded.file + end + + def test_merge_attributes + tl1 = RDoc::TopLevel.new 'one.rb' + tl2 = RDoc::TopLevel.new 'two.rb' + cm1 = RDoc::ClassModule.new 'Klass' - cm1.comment = 'klass 1' - cm1.add_attribute RDoc::Attr.new(nil, 'a1', 'RW', '') - cm1.add_attribute RDoc::Attr.new(nil, 'a3', 'R', '') - cm1.add_attribute RDoc::Attr.new(nil, 'a4', 'R', '') - cm1.add_constant RDoc::Constant.new('C1', nil, '') - cm1.add_include RDoc::Include.new('I1', '') - cm1.add_method RDoc::AnyMethod.new(nil, 'm1') + + attr = cm1.add_attribute RDoc::Attr.new(nil, 'a1', 'RW', '') + attr.record_location tl1 + attr = cm1.add_attribute RDoc::Attr.new(nil, 'a3', 'R', '') + attr.record_location tl1 + attr = cm1.add_attribute RDoc::Attr.new(nil, 'a4', 'R', '') + attr.record_location tl1 cm2 = RDoc::ClassModule.new 'Klass' - cm2.instance_variable_set(:@comment, - @RM::Document.new( - @RM::Paragraph.new('klass 2'))) - cm2.add_attribute RDoc::Attr.new(nil, 'a2', 'RW', '') - cm2.add_attribute RDoc::Attr.new(nil, 'a3', 'W', '') - cm2.add_attribute RDoc::Attr.new(nil, 'a4', 'R', '') - cm2.add_constant RDoc::Constant.new('C2', nil, '') - cm2.add_include RDoc::Include.new('I2', '') - cm2.add_method RDoc::AnyMethod.new(nil, 'm2') + # TODO allow merging when comment == '' + cm2.instance_variable_set :@comment, @RM::Document.new + + attr = cm2.add_attribute RDoc::Attr.new(nil, 'a2', 'RW', '') + attr.record_location tl2 + attr = cm2.add_attribute RDoc::Attr.new(nil, 'a3', 'W', '') + attr.record_location tl1 + attr = cm2.add_attribute RDoc::Attr.new(nil, 'a4', 'W', '') + attr.record_location tl1 cm1.merge cm2 - document = @RM::Document.new( - @RM::Paragraph.new('klass 2'), - @RM::Paragraph.new('klass 1')) + expected = [ + RDoc::Attr.new(nil, 'a2', 'RW', ''), + RDoc::Attr.new(nil, 'a3', 'W', ''), + RDoc::Attr.new(nil, 'a4', 'W', ''), + ] - assert_equal document, cm1.comment + expected.each do |a| a.parent = cm1 end + assert_equal expected, cm1.attributes.sort + end + + def test_merge_attributes_version_0 + tl1 = RDoc::TopLevel.new 'one.rb' + + cm1 = RDoc::ClassModule.new 'Klass' + + attr = cm1.add_attribute RDoc::Attr.new(nil, 'a1', 'RW', '') + attr.record_location tl1 + attr = cm1.add_attribute RDoc::Attr.new(nil, 'a3', 'R', '') + attr.record_location tl1 + attr = cm1.add_attribute RDoc::Attr.new(nil, 'a4', 'R', '') + attr.record_location tl1 + + cm2 = RDoc::ClassModule.new 'Klass' + # TODO allow merging when comment == '' + cm2.instance_variable_set :@comment, @RM::Document.new + + attr = cm2.add_attribute RDoc::Attr.new(nil, 'a2', 'RW', '') + attr = cm2.add_attribute RDoc::Attr.new(nil, 'a3', 'W', '') + attr = cm2.add_attribute RDoc::Attr.new(nil, 'a4', 'W', '') + + cm1.merge cm2 expected = [ RDoc::Attr.new(nil, 'a1', 'RW', ''), RDoc::Attr.new(nil, 'a2', 'RW', ''), RDoc::Attr.new(nil, 'a3', 'RW', ''), - RDoc::Attr.new(nil, 'a4', 'R', ''), + RDoc::Attr.new(nil, 'a4', 'RW', ''), ] expected.each do |a| a.parent = cm1 end assert_equal expected, cm1.attributes.sort + end + + def test_merge_comment + tl1 = RDoc::TopLevel.new 'one.rb' + tl2 = RDoc::TopLevel.new 'two.rb' + + cm1 = RDoc::ClassModule.new 'Klass' + cm1.add_comment 'klass 1', tl1 + + cm2 = RDoc::ClassModule.new 'Klass' + cm2.add_comment 'klass 2', tl2 + cm2.add_comment 'klass 3', tl1 + + cm2 = Marshal.load Marshal.dump cm2 + + cm1.merge cm2 + + inner1 = @RM::Document.new @RM::Paragraph.new 'klass 3' + inner1.file = 'one.rb' + inner2 = @RM::Document.new @RM::Paragraph.new 'klass 2' + inner2.file = 'two.rb' + + expected = @RM::Document.new inner2, inner1 + + assert_equal expected, cm1.comment + end + + def test_merge_comment_version_0 + tl = RDoc::TopLevel.new 'file.rb' + + cm1 = RDoc::ClassModule.new 'Klass' + cm1.add_comment 'klass 1', tl + + cm2 = RDoc::ClassModule.new 'Klass' + + cm2.instance_variable_set(:@comment, + @RM::Document.new( + @RM::Paragraph.new('klass 2'))) + cm2.instance_variable_set :@comment_location, @RM::Document.new(cm2.comment) + + cm1.merge cm2 + + inner = @RM::Document.new @RM::Paragraph.new 'klass 1' + inner.file = 'file.rb' + + expected = @RM::Document.new \ + inner, + @RM::Document.new(@RM::Paragraph.new('klass 2')) + + assert_equal expected, cm1.comment + end + + def test_merge_constants + tl1 = RDoc::TopLevel.new 'one.rb' + tl2 = RDoc::TopLevel.new 'two.rb' + + cm1 = RDoc::ClassModule.new 'Klass' + + const = cm1.add_constant RDoc::Constant.new('C1', nil, 'one') + const.record_location tl1 + const = cm1.add_constant RDoc::Constant.new('C3', nil, 'one') + const.record_location tl1 + + cm2 = RDoc::ClassModule.new 'Klass' + cm2.instance_variable_set :@comment, @RM::Document.new + + const = cm2.add_constant RDoc::Constant.new('C2', nil, 'two') + const.record_location tl2 + const = cm2.add_constant RDoc::Constant.new('C3', nil, 'one') + const.record_location tl1 + const = cm2.add_constant RDoc::Constant.new('C4', nil, 'one') + const.record_location tl1 + + cm1.merge cm2 expected = [ - RDoc::Constant.new('C1', nil, ''), - RDoc::Constant.new('C2', nil, ''), + RDoc::Constant.new('C2', nil, 'two'), + RDoc::Constant.new('C3', nil, 'one'), + RDoc::Constant.new('C4', nil, 'one'), ] - expected.each do |c| c.parent = cm1 end + expected.each do |a| a.parent = cm1 end + assert_equal expected, cm1.constants.sort + end + + def test_merge_constants_version_0 + tl1 = RDoc::TopLevel.new 'one.rb' + + cm1 = RDoc::ClassModule.new 'Klass' + + const = cm1.add_constant RDoc::Constant.new('C1', nil, 'one') + const.record_location tl1 + const = cm1.add_constant RDoc::Constant.new('C3', nil, 'one') + const.record_location tl1 + + cm2 = RDoc::ClassModule.new 'Klass' + cm2.instance_variable_set :@comment, @RM::Document.new + + const = cm2.add_constant RDoc::Constant.new('C2', nil, 'two') + const = cm2.add_constant RDoc::Constant.new('C3', nil, 'two') + const = cm2.add_constant RDoc::Constant.new('C4', nil, 'two') + + cm1.merge cm2 expected = [ - RDoc::Include.new('I1', ''), - RDoc::Include.new('I2', ''), + RDoc::Constant.new('C1', nil, 'one'), + RDoc::Constant.new('C2', nil, 'two'), + RDoc::Constant.new('C3', nil, 'one'), + RDoc::Constant.new('C4', nil, 'two'), ] - expected.each do |i| i.parent = cm1 end + expected.each do |a| a.parent = cm1 end + + assert_equal expected, cm1.constants.sort + end + + def test_merge_includes + tl1 = RDoc::TopLevel.new 'one.rb' + tl2 = RDoc::TopLevel.new 'two.rb' + + cm1 = RDoc::ClassModule.new 'Klass' + + incl = cm1.add_include RDoc::Include.new('I1', 'one') + incl.record_location tl1 + incl = cm1.add_include RDoc::Include.new('I3', 'one') + incl.record_location tl1 + + cm2 = RDoc::ClassModule.new 'Klass' + cm2.instance_variable_set :@comment, @RM::Document.new + + incl = cm2.add_include RDoc::Include.new('I2', 'two') + incl.record_location tl2 + incl = cm2.add_include RDoc::Include.new('I3', 'one') + incl.record_location tl1 + incl = cm2.add_include RDoc::Include.new('I4', 'one') + incl.record_location tl1 + + cm1.merge cm2 + + expected = [ + RDoc::Include.new('I2', 'two'), + RDoc::Include.new('I3', 'one'), + RDoc::Include.new('I4', 'one'), + ] + + expected.each do |a| a.parent = cm1 end + assert_equal expected, cm1.includes.sort + end + + def test_merge_includes_version_0 + tl1 = RDoc::TopLevel.new 'one.rb' + + cm1 = RDoc::ClassModule.new 'Klass' + + incl = cm1.add_include RDoc::Include.new('I1', 'one') + incl.record_location tl1 + incl = cm1.add_include RDoc::Include.new('I3', 'one') + incl.record_location tl1 + + cm2 = RDoc::ClassModule.new 'Klass' + cm2.instance_variable_set :@comment, @RM::Document.new + + incl = cm2.add_include RDoc::Include.new('I2', 'two') + incl = cm2.add_include RDoc::Include.new('I3', 'two') + incl = cm2.add_include RDoc::Include.new('I4', 'two') + + cm1.merge cm2 + + expected = [ + RDoc::Include.new('I1', 'one'), + RDoc::Include.new('I2', 'two'), + RDoc::Include.new('I3', 'one'), + RDoc::Include.new('I4', 'two'), + ] + + expected.each do |a| a.parent = cm1 end + + assert_equal expected, cm1.includes.sort + end + + def test_merge_methods + tl1 = RDoc::TopLevel.new 'one.rb' + tl2 = RDoc::TopLevel.new 'two.rb' + + cm1 = RDoc::ClassModule.new 'Klass' + + meth = cm1.add_method RDoc::AnyMethod.new(nil, 'm1') + meth.record_location tl1 + meth = cm1.add_method RDoc::AnyMethod.new(nil, 'm3') + meth.record_location tl1 + + cm2 = RDoc::ClassModule.new 'Klass' + cm2.instance_variable_set :@comment, @RM::Document.new + + meth = cm2.add_method RDoc::AnyMethod.new(nil, 'm2') + meth.record_location tl2 + meth = cm2.add_method RDoc::AnyMethod.new(nil, 'm3') + meth.record_location tl1 + meth = cm2.add_method RDoc::AnyMethod.new(nil, 'm4') + meth.record_location tl1 + + cm1.merge cm2 + + expected = [ + RDoc::AnyMethod.new(nil, 'm2'), + RDoc::AnyMethod.new(nil, 'm3'), + RDoc::AnyMethod.new(nil, 'm4'), + ] + + expected.each do |a| a.parent = cm1 end + + assert_equal expected, cm1.method_list.sort + end + + def test_merge_methods_version_0 + tl1 = RDoc::TopLevel.new 'one.rb' + + cm1 = RDoc::ClassModule.new 'Klass' + + meth = cm1.add_method RDoc::AnyMethod.new(nil, 'm1') + meth.record_location tl1 + meth = cm1.add_method RDoc::AnyMethod.new(nil, 'm3') + meth.record_location tl1 + + cm2 = RDoc::ClassModule.new 'Klass' + cm2.instance_variable_set :@comment, @RM::Document.new + + meth = cm2.add_method RDoc::AnyMethod.new(nil, 'm2') + meth = cm2.add_method RDoc::AnyMethod.new(nil, 'm3') + meth = cm2.add_method RDoc::AnyMethod.new(nil, 'm4') + + cm1.merge cm2 expected = [ RDoc::AnyMethod.new(nil, 'm1'), RDoc::AnyMethod.new(nil, 'm2'), + RDoc::AnyMethod.new(nil, 'm3'), + RDoc::AnyMethod.new(nil, 'm4'), ] - expected.each do |m| m.parent = cm1 end + expected.each do |a| a.parent = cm1 end + assert_equal expected, cm1.method_list.sort end + def test_parse + tl1 = RDoc::TopLevel.new 'one.rb' + tl2 = RDoc::TopLevel.new 'two.rb' + + cm = RDoc::ClassModule.new 'Klass' + cm.add_comment 'comment 1', tl1 + cm.add_comment 'comment 2', tl2 + + doc1 = @RM::Document.new @RM::Paragraph.new 'comment 1' + doc1.file = tl1.absolute_name + doc2 = @RM::Document.new @RM::Paragraph.new 'comment 2' + doc2.file = tl2.absolute_name + + expected = @RM::Document.new doc1, doc2 + + assert_equal expected, cm.parse(cm.comment_location) + end + + def test_parse_comment_location + tl1 = RDoc::TopLevel.new 'one.rb' + tl2 = RDoc::TopLevel.new 'two.rb' + + cm = RDoc::ClassModule.new 'Klass' + cm.add_comment 'comment 1', tl1 + cm.add_comment 'comment 2', tl2 + + cm = Marshal.load Marshal.dump cm + + doc1 = @RM::Document.new @RM::Paragraph.new 'comment 1' + doc1.file = tl1.absolute_name + doc2 = @RM::Document.new @RM::Paragraph.new 'comment 2' + doc2.file = tl2.absolute_name + + assert_same cm.comment_location, cm.parse(cm.comment_location) + end + def test_remove_nodoc_children parent = RDoc::ClassModule.new 'A' parent.modules_hash.replace 'B' => true, 'C' => true diff --git a/test/rdoc/test_rdoc_code_object.rb b/test/rdoc/test_rdoc_code_object.rb index b4ebfc4b3e..cdac7eeed6 100644 --- a/test/rdoc/test_rdoc_code_object.rb +++ b/test/rdoc/test_rdoc_code_object.rb @@ -30,6 +30,15 @@ class TestRDocCodeObject < XrefTestCase assert_equal 'I am a comment', @co.comment end + def test_comment_equals_document + doc = RDoc::Markup::Document.new + @co.comment = doc + + @co.comment = '' + + assert_equal doc, @co.comment + end + def test_comment_equals_encoding skip "Encoding not implemented" unless Object.const_defined? :Encoding @@ -129,6 +138,14 @@ class TestRDocCodeObject < XrefTestCase assert_equal [@parent, @xref_data], parents end + def test_file_name + assert_equal nil, @co.file_name + + @co.record_location RDoc::TopLevel.new 'lib/file.rb' + + assert_equal 'lib/file.rb', @co.file_name + end + def test_full_name_equals @co.full_name = 'hi' diff --git a/test/rdoc/test_rdoc_context.rb b/test/rdoc/test_rdoc_context.rb index 55c6c6e6e3..2fb8ef7bb7 100644 --- a/test/rdoc/test_rdoc_context.rb +++ b/test/rdoc/test_rdoc_context.rb @@ -17,6 +17,7 @@ class TestRDocContext < XrefTestCase assert_equal nil, @context.parent assert_equal :public, @context.visibility assert_equal 1, @context.sections.length + assert_equal nil, @context.temporary_section assert_empty @context.classes_hash assert_empty @context.modules_hash @@ -137,6 +138,13 @@ class TestRDocContext < XrefTestCase assert_equal 'Object', object.superclass.full_name end + def test_add_class_singleton + @c1.add_class RDoc::NormalClass, 'Klass', 'Object' + + assert_includes @c1.classes.map { |k| k.full_name }, 'C1::Klass' + assert_includes RDoc::TopLevel.classes.map { |k| k.full_name }, 'C1::Klass' + end + def test_add_class_superclass @c1.add_class RDoc::NormalClass, 'Klass', 'Object' @c1.add_class RDoc::NormalClass, 'Klass', 'Other' @@ -244,6 +252,29 @@ class TestRDocContext < XrefTestCase assert_includes @c1.top_level.requires, req end + def test_add_section + default_section = @context.sections.first + + @context.add_section nil, '# comment' + + assert_equal 1, @context.sections.length + assert_equal '# comment', @context.sections.first.comment + + @context.add_section nil, '# new comment' + + assert_equal 1, @context.sections.length + assert_equal "# comment\n# ---\n# new comment", + @context.sections.first.comment + + @context.add_section 'other', '' + + assert_equal 2, @context.sections.length + + new_section = @context.sections.find { |section| section.title == 'other' } + assert new_section + assert_equal default_section, @context.current_section + end + def test_add_to incl = RDoc::Include.new 'Name', 'comment' arr = [] @@ -254,6 +285,19 @@ class TestRDocContext < XrefTestCase assert_equal @context.current_section, incl.section end + def test_add_to_temporary_section + incl = RDoc::Include.new 'Name', 'comment' + arr = [] + section = @context.add_section 'temporary', '' + @context.temporary_section = section + + @context.add_to arr, incl + + assert_includes arr, incl + assert_equal @context, incl.parent + assert_equal section, incl.section + end + def test_add_to_no_document_self incl = RDoc::Include.new 'Name', 'comment' arr = [] @@ -281,6 +325,16 @@ class TestRDocContext < XrefTestCase assert_equal %w[C3::H1 C3::H2], @c3.classes.map { |k| k.full_name } end + def test_current_section + default_section = @context.current_section + + new_section = @context.add_section 'other', '' + @context.temporary_section = new_section + + assert_equal new_section, @context.current_section + assert_equal default_section, @context.current_section + end + def test_defined_in_eh assert @c1.defined_in?(@c1.top_level) @@ -596,6 +650,22 @@ class TestRDocContext < XrefTestCase assert_equal [@pub, @prot, @priv], methods end + def test_set_current_section + default_section = @context.sections.first + + @context.set_current_section nil, '' + + assert_equal default_section, @context.current_section + + @context.set_current_section 'other', '' + + new_section = @context.sections.find { |section| + section != default_section + } + + assert_equal new_section, @context.current_section + end + def util_visibilities @pub = RDoc::AnyMethod.new nil, 'pub' @prot = RDoc::AnyMethod.new nil, 'prot' diff --git a/test/rdoc/test_rdoc_generator_ri.rb b/test/rdoc/test_rdoc_generator_ri.rb index 780d9cc570..2be006843c 100644 --- a/test/rdoc/test_rdoc_generator_ri.rb +++ b/test/rdoc/test_rdoc_generator_ri.rb @@ -9,6 +9,7 @@ class TestRDocGeneratorRI < MiniTest::Unit::TestCase def setup @options = RDoc::Options.new + @options.encoding = Encoding::UTF_8 if Object.const_defined? :Encoding @pwd = Dir.pwd RDoc::TopLevel.reset @@ -21,9 +22,15 @@ class TestRDocGeneratorRI < MiniTest::Unit::TestCase @top_level = RDoc::TopLevel.new 'file.rb' @klass = @top_level.add_class RDoc::NormalClass, 'Object' + @meth = RDoc::AnyMethod.new nil, 'method' + @meth.record_location @top_level + @meth_bang = RDoc::AnyMethod.new nil, 'method!' + @meth_bang.record_location @top_level + @attr = RDoc::Attr.new nil, 'attr', 'RW', '' + @attr.record_location @top_level @klass.add_method @meth @klass.add_method @meth_bang @@ -44,9 +51,6 @@ class TestRDocGeneratorRI < MiniTest::Unit::TestCase end def test_generate - top_level = RDoc::TopLevel.new 'file.rb' - top_level.add_class @klass.class, @klass.name - @g.generate nil assert_file File.join(@tmpdir, 'cache.ri') @@ -56,6 +60,13 @@ class TestRDocGeneratorRI < MiniTest::Unit::TestCase assert_file File.join(@tmpdir, 'Object', 'attr-i.ri') assert_file File.join(@tmpdir, 'Object', 'method-i.ri') assert_file File.join(@tmpdir, 'Object', 'method%21-i.ri') + + store = RDoc::RI::Store.new @tmpdir + store.load_cache + + encoding = Object.const_defined?(:Encoding) ? Encoding::UTF_8 : nil + + assert_equal encoding, store.encoding end def test_generate_dry_run diff --git a/test/rdoc/test_rdoc_markup.rb b/test/rdoc/test_rdoc_markup.rb index 9f7866fb5a..48683dbcda 100644 --- a/test/rdoc/test_rdoc_markup.rb +++ b/test/rdoc/test_rdoc_markup.rb @@ -18,7 +18,10 @@ the time STR m = RDoc::Markup.new - out = m.convert str, RDoc::Markup::ToTest.new + + tt = RDoc::Markup::ToTest.new m + + out = m.convert str, tt expected = [ "now is the time", @@ -33,5 +36,25 @@ the time assert_equal expected, out end + def test_convert_custom_markup + str = <<-STR +{stricken} + STR + + m = RDoc::Markup.new + m.add_word_pair '{', '}', :STRIKE + + tt = RDoc::Markup::ToTest.new m + tt.add_tag :STRIKE, 'STRIKE ', ' STRIKE' + + out = m.convert str, tt + + expected = [ + "STRIKE stricken STRIKE", + ] + + assert_equal expected, out + end + end diff --git a/test/rdoc/test_rdoc_markup_document.rb b/test/rdoc/test_rdoc_markup_document.rb index ab2f1c2362..70fb3efb16 100644 --- a/test/rdoc/test_rdoc_markup_document.rb +++ b/test/rdoc/test_rdoc_markup_document.rb @@ -47,5 +47,82 @@ class TestRDocMarkupDocument < MiniTest::Unit::TestCase end end + def test_empty_eh + assert_empty @d + + @d << @RM::BlankLine.new + + refute_empty @d + end + + def test_equals2 + d2 = @RM::Document.new + + assert_equal @d, d2 + + d2 << @RM::BlankLine.new + + refute_equal @d, d2 + end + + def test_equals2_file + d2 = @RM::Document.new + d2.file = 'file.rb' + + refute_equal @d, d2 + + @d.file = 'file.rb' + + assert_equal @d, d2 + end + + def test_lt2 + @d << @RM::BlankLine.new + + refute_empty @d + end + + def test_merge + original = @RM::Document.new @RM::Paragraph.new 'original' + original.file = 'file.rb' + root = @RM::Document.new original + + replace = @RM::Document.new @RM::Paragraph.new 'replace' + replace.file = 'file.rb' + + other = @RM::Document.new replace + + result = root.merge other + + inner = @RM::Document.new @RM::Paragraph.new 'replace' + inner.file = 'file.rb' + expected = @RM::Document.new inner + + assert_equal expected, result + end + + def test_merge_add + original = @RM::Document.new @RM::Paragraph.new 'original' + original.file = 'file.rb' + root = @RM::Document.new original + + addition = @RM::Document.new @RM::Paragraph.new 'addition' + addition.file = 'other.rb' + + other = @RM::Document.new addition + + result = root.merge other + + expected = @RM::Document.new original, addition + + assert_equal expected, result + end + + def test_push + @d.push @RM::BlankLine.new, @RM::BlankLine.new + + refute_empty @d + end + end diff --git a/test/rdoc/test_rdoc_markup_indented_paragraph.rb b/test/rdoc/test_rdoc_markup_indented_paragraph.rb new file mode 100644 index 0000000000..e3e2b630e1 --- /dev/null +++ b/test/rdoc/test_rdoc_markup_indented_paragraph.rb @@ -0,0 +1,40 @@ +require 'pp' +require 'rubygems' +require 'minitest/autorun' +require 'rdoc/markup' + +class TestRDocMarkupIndentedParagraph < MiniTest::Unit::TestCase + + def setup + @IP = RDoc::Markup::IndentedParagraph + end + + def test_initialize + ip = @IP.new 2, 'a', 'b' + + assert_equal 2, ip.indent + assert_equal %w[a b], ip.parts + end + + def test_accept + visitor = Object.new + def visitor.accept_indented_paragraph(obj) @obj = obj end + def visitor.obj() @obj end + + paragraph = @IP.new 0 + + paragraph.accept visitor + + assert_equal paragraph, visitor.obj + end + + def test_equals2 + one = @IP.new 1 + two = @IP.new 2 + + assert_equal one, one + refute_equal one, two + end + +end + diff --git a/test/rdoc/test_rdoc_markup_paragraph.rb b/test/rdoc/test_rdoc_markup_paragraph.rb index a1eba7038d..6ae1ad9a84 100644 --- a/test/rdoc/test_rdoc_markup_paragraph.rb +++ b/test/rdoc/test_rdoc_markup_paragraph.rb @@ -5,5 +5,17 @@ require 'rdoc/markup' class TestRDocMarkupParagraph < MiniTest::Unit::TestCase + def test_accept + visitor = Object.new + def visitor.accept_paragraph(obj) @obj = obj end + def visitor.obj() @obj end + + paragraph = RDoc::Markup::Paragraph.new + + paragraph.accept visitor + + assert_equal paragraph, visitor.obj + end + end diff --git a/test/rdoc/test_rdoc_markup_pre_process.rb b/test/rdoc/test_rdoc_markup_pre_process.rb index 9fff53df77..db5fca5ac1 100644 --- a/test/rdoc/test_rdoc_markup_pre_process.rb +++ b/test/rdoc/test_rdoc_markup_pre_process.rb @@ -40,6 +40,11 @@ Regular expressions (regexps) 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 @@ -59,6 +64,11 @@ contents of a string. expected = "?\n" + # 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 @@ -86,6 +96,17 @@ contents of a string. assert_equal "", text end + def test_handle_category + context = RDoc::Context.new + original_section = context.current_section + + text = "# :category: other\n" + + @pp.handle text, context + + refute_equal original_section, context.current_section + end + def test_handle_code_object cd = RDoc::CodeObject.new text = "# :x: y\n" diff --git a/test/rdoc/test_rdoc_markup_to_ansi.rb b/test/rdoc/test_rdoc_markup_to_ansi.rb index 1334ac71c4..f60d1999c2 100644 --- a/test/rdoc/test_rdoc_markup_to_ansi.rb +++ b/test/rdoc/test_rdoc_markup_to_ansi.rb @@ -18,6 +18,10 @@ class TestRDocMarkupToAnsi < RDoc::Markup::TextFormatterTestCase assert_equal "\e[0m\n", @to.res.join end + def accept_document + assert_equal "\e[0mhello\n", @to.res.join + end + def accept_heading assert_equal "\e[0mHello\n", @to.res.join end diff --git a/test/rdoc/test_rdoc_markup_to_bs.rb b/test/rdoc/test_rdoc_markup_to_bs.rb index 3d2e4da8de..35a9266b5d 100644 --- a/test/rdoc/test_rdoc_markup_to_bs.rb +++ b/test/rdoc/test_rdoc_markup_to_bs.rb @@ -18,6 +18,10 @@ class TestRDocMarkupToBs < RDoc::Markup::TextFormatterTestCase assert_equal "\n", @to.res.join end + def accept_document + assert_equal "hello\n", @to.res.join + end + def accept_heading skip "No String#chars, upgrade your ruby" unless ''.respond_to? :chars assert_equal "===== H\bHe\bel\bll\blo\bo\n", @to.res.join diff --git a/test/rdoc/test_rdoc_markup_to_html.rb b/test/rdoc/test_rdoc_markup_to_html.rb index 8a2971155b..b43adf1560 100644 --- a/test/rdoc/test_rdoc_markup_to_html.rb +++ b/test/rdoc/test_rdoc_markup_to_html.rb @@ -30,6 +30,10 @@ class TestRDocMarkupToHtml < RDoc::Markup::FormatterTestCase assert_empty @to.res.join end + def accept_document + assert_equal "\n

hello

\n", @to.res.join + end + def accept_heading assert_equal "\n
Hello
\n", @to.res.join end @@ -69,7 +73,7 @@ class TestRDocMarkupToHtml < RDoc::Markup::FormatterTestCase assert_equal [], @to.list assert_equal [], @to.in_list_entry - assert_equal "
\n", @to.res.join + assert_equal "
\n", @to.res.join end def accept_list_end_lalpha @@ -129,7 +133,7 @@ class TestRDocMarkupToHtml < RDoc::Markup::FormatterTestCase end def accept_list_item_start_label - assert_equal "
cat
\n
", @to.res.join + assert_equal "
cat
\n
", @to.res.join end def accept_list_item_start_lalpha @@ -171,7 +175,7 @@ class TestRDocMarkupToHtml < RDoc::Markup::FormatterTestCase assert_equal [:LABEL], @to.list assert_equal [false], @to.in_list_entry - assert_equal "
", @to.res.join + assert_equal '
', @to.res.join end def accept_list_start_lalpha diff --git a/test/rdoc/test_rdoc_markup_to_rdoc.rb b/test/rdoc/test_rdoc_markup_to_rdoc.rb index 20ff937c5a..06cae078c6 100644 --- a/test/rdoc/test_rdoc_markup_to_rdoc.rb +++ b/test/rdoc/test_rdoc_markup_to_rdoc.rb @@ -18,6 +18,10 @@ class TestRDocMarkupToRDoc < RDoc::Markup::TextFormatterTestCase assert_equal "\n", @to.res.join end + def accept_document + assert_equal "hello\n", @to.res.join + end + def accept_heading assert_equal "===== Hello\n", @to.res.join end @@ -323,5 +327,15 @@ words words words words assert_equal expected, @to.end_accepting end + def test_accept_indented_paragraph + ip = RDoc::Markup::IndentedParagraph.new 2, 'cats are cool' + + @to.start_accepting + + @to.accept_indented_paragraph ip + + assert_equal " cats are cool\n", @to.end_accepting + end + end diff --git a/test/rdoc/test_rdoc_markup_to_tt_only.rb b/test/rdoc/test_rdoc_markup_to_tt_only.rb index dd50ba8fde..f5bb662897 100644 --- a/test/rdoc/test_rdoc_markup_to_tt_only.rb +++ b/test/rdoc/test_rdoc_markup_to_tt_only.rb @@ -17,6 +17,10 @@ class TestRDocMarkupToTtOnly < RDoc::Markup::FormatterTestCase assert_empty @to.end_accepting end + def accept_document + assert_equal [], @to.res + end + def accept_heading assert_empty @to.end_accepting end diff --git a/test/rdoc/test_rdoc_parser_c.rb b/test/rdoc/test_rdoc_parser_c.rb index 45d0b1e341..438eeee2ab 100644 --- a/test/rdoc/test_rdoc_parser_c.rb +++ b/test/rdoc/test_rdoc_parser_c.rb @@ -46,7 +46,7 @@ assert call-seq correct =end class RDoc::Parser::C - attr_accessor :classes, :singleton_classes + attr_accessor :classes public :do_classes, :do_constants end @@ -70,6 +70,22 @@ class TestRDocParserC < MiniTest::Unit::TestCase @tempfile.close end + def test_class_can_parse + c_parser = RDoc::Parser::C + + assert_equal c_parser, c_parser.can_parse('file.C') + assert_equal c_parser, c_parser.can_parse('file.CC') + assert_equal c_parser, c_parser.can_parse('file.H') + assert_equal c_parser, c_parser.can_parse('file.HH') + assert_equal c_parser, c_parser.can_parse('file.c') + assert_equal c_parser, c_parser.can_parse('file.cc') + assert_equal c_parser, c_parser.can_parse('file.cpp') + assert_equal c_parser, c_parser.can_parse('file.cxx') + assert_equal c_parser, c_parser.can_parse('file.h') + assert_equal c_parser, c_parser.can_parse('file.hh') + assert_equal c_parser, c_parser.can_parse('file.y') + end + def test_do_attr_rb_attr content = <<-EOF void Init_Blah(void) { @@ -224,6 +240,21 @@ VALUE cFoo = boot_defclass("Foo", 0); assert_equal nil, klass.superclass end + def test_do_aliases_missing_class + content = <<-EOF +void Init_Blah(void) { + rb_define_alias(cDate, "b", "a"); +} + EOF + + _, err = capture_io do + refute util_get_class(content, 'cDate') + end + + assert_equal "Enclosing class/module \"cDate\" for alias b a not known\n", + err + end + def test_do_classes_class content = <<-EOF /* Document-class: Foo @@ -398,6 +429,140 @@ Multiline comment goes here because this comment spans multiple lines. assert constants.empty?, constants.inspect end + def test_do_constants_curses + content = <<-EOF +void Init_curses(){ + mCurses = rb_define_module("Curses"); + + /* + * Document-const: Curses::COLOR_BLACK + * + * Value of the color black + */ + rb_curses_define_const(COLOR_BLACK); +} + EOF + + @parser = util_parser content + + @parser.do_classes + @parser.do_constants + + klass = @parser.classes['mCurses'] + + constants = klass.constants + refute_empty klass.constants + + assert_equal 'COLOR_BLACK', constants.first.name + assert_equal 'UINT2NUM(COLOR_BLACK)', constants.first.value + assert_equal 'Value of the color black', constants.first.comment + end + + def test_do_includes + content = <<-EOF +Init_foo() { + VALUE cFoo = rb_define_class("Foo", rb_cObject); + VALUE mInc = rb_define_module("Inc"); + + rb_include_module(cFoo, mInc); +} + EOF + + klass = util_get_class content, 'cFoo' + + incl = klass.includes.first + assert_equal 'Inc', incl.name + assert_equal '', incl.comment + assert_equal @top_level, incl.file + end + + # HACK parsing warning instead of setting up in file + def test_do_methods_in_c + content = <<-EOF +VALUE blah(VALUE klass, VALUE year) { +} + +void Init_Blah(void) { + cDate = rb_define_class("Date", rb_cObject); + + rb_define_method(cDate, "blah", blah, 1); /* in blah.c */ +} + EOF + + klass = nil + + _, err = capture_io do + klass = util_get_class content, 'cDate' + end + + assert_match ' blah.c ', err + end + + # HACK parsing warning instead of setting up in file + def test_do_methods_in_cpp + content = <<-EOF +VALUE blah(VALUE klass, VALUE year) { +} + +void Init_Blah(void) { + cDate = rb_define_class("Date", rb_cObject); + + rb_define_method(cDate, "blah", blah, 1); /* in blah.cpp */ +} + EOF + + klass = nil + + _, err = capture_io do + klass = util_get_class content, 'cDate' + end + + assert_match ' blah.cpp ', err + end + + # HACK parsing warning instead of setting up in file + def test_do_methods_in_y + content = <<-EOF +VALUE blah(VALUE klass, VALUE year) { +} + +void Init_Blah(void) { + cDate = rb_define_class("Date", rb_cObject); + + rb_define_method(cDate, "blah", blah, 1); /* in blah.y */ +} + EOF + + klass = nil + + _, err = capture_io do + klass = util_get_class content, 'cDate' + end + + assert_match ' blah.y ', err + end + + def test_do_methods_singleton_class + content = <<-EOF +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); +} + EOF + + klass = util_get_class content, 'cDate' + + methods = klass.method_list + assert_equal 1, methods.length + assert_equal 'blah', methods.first.name + assert methods.first.singleton + end + def test_find_alias_comment parser = util_parser '' @@ -525,6 +690,53 @@ Init_Foo(void) { assert_equal '', klass.comment end + def test_find_const_comment_rb_define + content = <<-EOF +/* + * A comment + */ +rb_define_const(cFoo, "CONST", value); + EOF + + parser = util_parser content + + comment = parser.find_const_comment 'const', 'CONST' + + assert_equal "/*\n * A comment\n */\n", comment + end + + def test_find_const_comment_document_const + content = <<-EOF +/* + * Document-const: CONST + * + * A comment + */ + EOF + + parser = util_parser content + + comment = parser.find_const_comment nil, 'CONST' + + assert_equal " *\n * A comment\n */", comment + end + + def test_find_const_comment_document_const_full_name + content = <<-EOF +/* + * Document-const: Foo::CONST + * + * A comment + */ + EOF + + parser = util_parser content + + comment = parser.find_const_comment nil, 'CONST', 'Foo' + + assert_equal " *\n * A comment\n */", comment + end + def test_find_body content = <<-EOF /* @@ -699,6 +911,81 @@ Init_Foo(void) { assert_equal "a comment for bar", baz.comment end + def test_find_body_document_method_equals + content = <<-EOF +/* + * Document-method: Zlib::GzipFile#mtime= + * + * A comment + */ +static VALUE +rb_gzfile_set_mtime(VALUE obj, VALUE mtime) +{ + +void +Init_zlib() { + mZlib = rb_define_module("Zlib"); + cGzipFile = rb_define_class_under(mZlib, "GzipFile", rb_cObject); + cGzipWriter = rb_define_class_under(mZlib, "GzipWriter", cGzipFile); + rb_define_method(cGzipWriter, "mtime=", rb_gzfile_set_mtime, 1); +} + EOF + + klass = util_get_class content, 'cGzipWriter' + assert_equal 1, klass.method_list.length + + methods = klass.method_list.sort + + bar = methods.first + assert_equal 'Zlib::GzipWriter#mtime=', bar.full_name + assert_equal 'A comment', bar.comment + end + + def test_find_body_document_method_same + content = <<-EOF +VALUE +s_bar() { +} + +VALUE +bar() { +} + +/* + * Document-method: Foo::bar + * + * a comment for Foo::bar + */ + +/* + * Document-method: Foo#bar + * + * a comment for Foo#bar + */ + +void +Init_Foo(void) { + VALUE foo = rb_define_class("Foo", rb_cObject); + + rb_define_singleton_method(foo, "bar", s_bar, 0); + rb_define_method(foo, "bar", bar, 0); +} + EOF + + klass = util_get_class content, 'foo' + assert_equal 2, klass.method_list.length + + methods = klass.method_list.sort + + s_bar = methods.first + assert_equal 'Foo::bar', s_bar.full_name + assert_equal "a comment for Foo::bar", s_bar.comment + + bar = methods.last + assert_equal 'Foo#bar', bar.full_name + assert_equal "a comment for Foo#bar", bar.comment + end + def test_find_modifiers_call_seq comment = <<-COMMENT /* call-seq: @@ -830,7 +1117,6 @@ rb_m(int argc, VALUE *argv, VALUE obj) { assert_equal '(p1)', m.params end - def test_handle_method_args_0 parser = util_parser "Document-method: BasicObject#==\n blah */" @@ -905,6 +1191,20 @@ rb_m(int argc, VALUE *argv, VALUE obj) { assert_equal :public, new.visibility end + def test_handle_singleton + parser = util_parser <<-SINGLE +void Init_Blah(void) { + cDate = rb_define_class("Date", rb_cObject); + sDate = rb_singleton_class(cDate); +} + SINGLE + + parser.scan + + assert_equal 'Date', parser.known_classes['sDate'] + assert_equal 'Date', parser.singleton_classes['sDate'] + end + def test_look_for_directives_in parser = util_parser '' diff --git a/test/rdoc/test_rdoc_parser_ruby.rb b/test/rdoc/test_rdoc_parser_ruby.rb index 87ac45a073..e02ed56beb 100644 --- a/test/rdoc/test_rdoc_parser_ruby.rb +++ b/test/rdoc/test_rdoc_parser_ruby.rb @@ -1084,6 +1084,26 @@ EOF assert_equal top_bar, bar.find_module_named('A') end + def test_parse_include + klass = RDoc::NormalClass.new 'C' + klass.parent = @top_level + + comment = "# my include\n" + + util_parser "include I" + + @parser.get_tk # include + + @parser.parse_include klass, comment + + assert_equal 1, klass.includes.length + + incl = klass.includes.first + assert_equal 'I', incl.name + assert_equal 'my include', incl.comment + assert_equal @top_level, incl.file + end + def test_parse_meta_method klass = RDoc::NormalClass.new 'Foo' klass.parent = @top_level @@ -2028,6 +2048,33 @@ end assert_equal 'm comment', m.comment end + def test_scan_block_comment_nested # Issue #41 + content = <<-CONTENT +require 'something' +=begin rdoc +findmeindoc +=end +module Foo + class Bar + end +end + CONTENT + + util_parser content + + @parser.scan + + foo = @top_level.modules.first + + assert_equal 'Foo', foo.full_name + assert_equal 'findmeindoc', foo.comment + + bar = foo.classes.first + + assert_equal 'Foo::Bar', bar.full_name + assert_equal '', bar.comment + end + def test_scan_block_comment_notflush ## # diff --git a/test/rdoc/test_rdoc_rdoc.rb b/test/rdoc/test_rdoc_rdoc.rb index 0f465d724f..aedccc9dbf 100644 --- a/test/rdoc/test_rdoc_rdoc.rb +++ b/test/rdoc/test_rdoc_rdoc.rb @@ -16,12 +16,27 @@ class TestRDocRDoc < MiniTest::Unit::TestCase @stats = RDoc::Stats.new 0, 0 @rdoc.instance_variable_set :@stats, @stats - - @tempfile = Tempfile.new 'test_rdoc_rdoc' end - def teardown - @tempfile.close rescue nil # HACK for 1.8.6 + def test_class_reset + tl = RDoc::TopLevel.new 'file.rb' + tl.add_class RDoc::NormalClass, 'C' + tl.add_class RDoc::NormalModule, 'M' + + c = RDoc::Parser::C + enclosure_classes = c.send :class_variable_get, :@@enclosure_classes + enclosure_classes['A'] = 'B' + known_bodies = c.send :class_variable_get, :@@known_bodies + known_bodies['A'] = 'B' + + RDoc::RDoc.reset + + assert_empty RDoc::TopLevel.all_classes_hash + assert_empty RDoc::TopLevel.all_files_hash + assert_empty RDoc::TopLevel.all_modules_hash + + assert_empty c.send :class_variable_get, :@@enclosure_classes + assert_empty c.send :class_variable_get, :@@known_bodies end def test_gather_files @@ -47,6 +62,20 @@ class TestRDocRDoc < MiniTest::Unit::TestCase assert_empty files end + def test_parse_file_encoding + skip "Encoding not implemented" unless Object.const_defined? :Encoding + @rdoc.options.encoding = Encoding::ISO_8859_1 + + Tempfile.open 'test.txt' do |io| + io.write 'hi' + io.rewind + + top_level = @rdoc.parse_file io.path + + assert_equal Encoding::ISO_8859_1, top_level.absolute_name.encoding + end + end + def test_remove_unparseable file_list = %w[ blah.class @@ -120,14 +149,16 @@ class TestRDocRDoc < MiniTest::Unit::TestCase end def test_setup_output_dir_exists_file - path = @tempfile.path + Tempfile.open 'test_rdoc_rdoc' do |tempfile| + path = tempfile.path - e = assert_raises RDoc::Error do - @rdoc.setup_output_dir path, false + e = assert_raises RDoc::Error do + @rdoc.setup_output_dir path, false + end + + assert_match(%r%#{Regexp.escape path} exists and is not a directory%, + e.message) end - - assert_match(%r%#{Regexp.escape path} exists and is not a directory%, - e.message) end def test_setup_output_dir_exists_not_rdoc diff --git a/test/rdoc/test_rdoc_ri_driver.rb b/test/rdoc/test_rdoc_ri_driver.rb index b0e0e787c0..da7d160047 100644 --- a/test/rdoc/test_rdoc_ri_driver.rb +++ b/test/rdoc/test_rdoc_ri_driver.rb @@ -22,11 +22,11 @@ class TestRDocRIDriver < MiniTest::Unit::TestCase ENV['HOME'] = @tmpdir ENV.delete 'RI' - options = RDoc::RI::Driver.process_args [] - options[:home] = @tmpdir - options[:use_stdout] = true - options[:formatter] = @RM::ToRdoc - @driver = RDoc::RI::Driver.new options + @options = RDoc::RI::Driver.process_args [] + @options[:home] = @tmpdir + @options[:use_stdout] = true + @options[:formatter] = @RM::ToRdoc + @driver = RDoc::RI::Driver.new @options end def teardown @@ -191,12 +191,30 @@ class TestRDocRIDriver < MiniTest::Unit::TestCase def test_add_method_list out = @RM::Document.new - @driver.add_method_list out, %w[new], 'Class methods' + @driver.add_method_list out, %w[new parse], 'Class methods' expected = @RM::Document.new( @RM::Heading.new(1, 'Class methods:'), @RM::BlankLine.new, @RM::Verbatim.new('new'), + @RM::Verbatim.new('parse'), + @RM::BlankLine.new) + + assert_equal expected, out + end + + def test_add_method_list_interative + @options[:interactive] = true + driver = RDoc::RI::Driver.new @options + + out = @RM::Document.new + + driver.add_method_list out, %w[new parse], 'Class methods' + + expected = @RM::Document.new( + @RM::Heading.new(1, 'Class methods:'), + @RM::BlankLine.new, + @RM::IndentedParagraph.new(2, 'new, parse'), @RM::BlankLine.new) assert_equal expected, out @@ -272,6 +290,8 @@ class TestRDocRIDriver < MiniTest::Unit::TestCase def test_complete_classes util_store + assert_equal %w[ ], @driver.complete('[') + assert_equal %w[ ], @driver.complete('[::') assert_equal %w[Foo ], @driver.complete('F') assert_equal %w[Foo:: Foo::Bar Foo::Baz], @driver.complete('Foo::') assert_equal %w[ Foo::Bar Foo::Baz], @driver.complete('Foo::B') @@ -621,11 +641,13 @@ Foo::Bar#bother util_store index = RDoc::AnyMethod.new nil, '[]' + index.record_location @top_level @cFoo.add_method index @store.save_method @cFoo, index c_index = RDoc::AnyMethod.new nil, '[]' c_index.singleton = true + c_index.record_location @top_level @cFoo.add_method c_index @store.save_method @cFoo, c_index @@ -858,10 +880,12 @@ Foo::Bar#bother @cFoo_Baz.parent = @cFoo @baz = RDoc::AnyMethod.new nil, 'baz' + @baz.record_location @top_level @cBar.add_method @baz @override = RDoc::AnyMethod.new nil, 'override' @override.comment = 'must be displayed' + @override.record_location @top_level @cBar.add_method @override @store2.save_class @mAmbiguous @@ -879,6 +903,8 @@ Foo::Bar#bother def util_store @store = RDoc::RI::Store.new @home_ri + @top_level = RDoc::TopLevel.new 'file.rb' + @cFoo = RDoc::NormalClass.new 'Foo' @mInc = RDoc::NormalModule.new 'Inc' @cAmbiguous = RDoc::NormalClass.new 'Ambiguous' @@ -886,6 +912,7 @@ Foo::Bar#bother doc = @RM::Document.new @RM::Paragraph.new('Include thingy') @cFooInc = RDoc::Include.new 'Inc', doc + @cFooInc.record_location @top_level @cFoo.add_include @cFooInc @cFoo_Bar = RDoc::NormalClass.new 'Bar' @@ -893,12 +920,15 @@ Foo::Bar#bother @blah = RDoc::AnyMethod.new nil, 'blah' @blah.call_seq = "blah(5) => 5\nblah(6) => 6\n" + @blah.record_location @top_level @bother = RDoc::AnyMethod.new nil, 'bother' - @bother.params = "(things)" @bother.block_params = "stuff" + @bother.params = "(things)" + @bother.record_location @top_level @new = RDoc::AnyMethod.new nil, 'new' + @new.record_location @top_level @new.singleton = true @cFoo_Bar.add_method @blah @@ -906,6 +936,7 @@ Foo::Bar#bother @cFoo_Bar.add_method @new @attr = RDoc::Attr.new nil, 'attr', 'RW', '' + @attr.record_location @top_level @cFoo_Bar.add_attribute @attr @@ -913,11 +944,13 @@ Foo::Bar#bother @cFoo_Baz.parent = @cFoo @inherit = RDoc::AnyMethod.new nil, 'inherit' + @inherit.record_location @top_level @cFoo.add_method @inherit # overriden by Bar in multi_store @overriden = RDoc::AnyMethod.new nil, 'override' @overriden.comment = 'must not be displayed' + @overriden.record_location @top_level @cFoo.add_method @overriden @store.save_class @cFoo diff --git a/test/rdoc/test_rdoc_ri_store.rb b/test/rdoc/test_rdoc_ri_store.rb index 83aebf009b..1077383d86 100644 --- a/test/rdoc/test_rdoc_ri_store.rb +++ b/test/rdoc/test_rdoc_ri_store.rb @@ -4,9 +4,12 @@ require 'rdoc/ri' require 'rdoc/markup' require 'tmpdir' require 'fileutils' +require 'pp' class TestRDocRIStore < MiniTest::Unit::TestCase + OBJECT_ANCESTORS = defined?(::BasicObject) ? %w[BasicObject] : [] + def setup RDoc::TopLevel.reset @@ -16,15 +19,20 @@ class TestRDocRIStore < MiniTest::Unit::TestCase @top_level = RDoc::TopLevel.new 'file.rb' @klass = @top_level.add_class RDoc::NormalClass, 'Object' - @klass.comment = 'original' + @klass.add_comment 'original', @top_level @cmeth = RDoc::AnyMethod.new nil, 'cmethod' @cmeth.singleton = true + @cmeth.record_location @top_level @meth = RDoc::AnyMethod.new nil, 'method' + @meth.record_location @top_level + @meth_bang = RDoc::AnyMethod.new nil, 'method!' + @meth_bang.record_location @top_level @attr = RDoc::Attr.new nil, 'attr', 'RW', '' + @attr.record_location @top_level @klass.add_method @cmeth @klass.add_method @meth @@ -33,7 +41,10 @@ class TestRDocRIStore < MiniTest::Unit::TestCase @nest_klass = @klass.add_class RDoc::NormalClass, 'SubClass' @nest_meth = RDoc::AnyMethod.new nil, 'method' + @nest_meth.record_location @top_level + @nest_incl = RDoc::Include.new 'Incl', '' + @nest_incl.record_location @top_level @nest_klass.add_method @nest_meth @nest_klass.add_include @nest_incl @@ -45,15 +56,32 @@ class TestRDocRIStore < MiniTest::Unit::TestCase FileUtils.rm_rf @tmpdir end + def mu_pp obj + s = '' + s = PP.pp obj, s + s.force_encoding Encoding.default_external if defined? Encoding + s.chomp + end + def assert_cache imethods, cmethods, attrs, modules, ancestors = {} + imethods ||= { 'Object' => %w[method method!] } + cmethods ||= { 'Object' => %w[cmethod] } + attrs ||= { 'Object' => ['attr_accessor attr'] } + + # this is sort-of a hack + @s.clean_cache_collection ancestors + expected = { - :class_methods => cmethods, - :instance_methods => imethods, + :ancestors => ancestors, :attributes => attrs, + :class_methods => cmethods, + :encoding => nil, + :instance_methods => imethods, :modules => modules, - :ancestors => ancestors } + @s.save_cache + assert_equal expected, @s.cache end @@ -138,8 +166,9 @@ class TestRDocRIStore < MiniTest::Unit::TestCase def test_load_cache cache = { - :methods => %w[Object#method], - :modules => %w[Object], + :encoding => :encoding_value, + :methods => %w[Object#method], + :modules => %w[Object], } Dir.mkdir @tmpdir @@ -151,6 +180,32 @@ class TestRDocRIStore < MiniTest::Unit::TestCase @s.load_cache assert_equal cache, @s.cache + + assert_equal :encoding_value, @s.encoding + end + + def test_load_cache_encoding_differs + skip "Encoding not implemented" unless Object.const_defined? :Encoding + + cache = { + :encoding => Encoding::ISO_8859_1, + :methods => %w[Object#method], + :modules => %w[Object], + } + + Dir.mkdir @tmpdir + + open File.join(@tmpdir, 'cache.ri'), 'wb' do |io| + Marshal.dump cache, io + end + + @s.encoding = Encoding::UTF_8 + + @s.load_cache + + assert_equal cache, @s.cache + + assert_equal Encoding::UTF_8, @s.encoding end def test_load_cache_no_cache @@ -158,6 +213,7 @@ class TestRDocRIStore < MiniTest::Unit::TestCase :ancestors => {}, :attributes => {}, :class_methods => {}, + :encoding => nil, :instance_methods => {}, :modules => [], } @@ -199,6 +255,7 @@ class TestRDocRIStore < MiniTest::Unit::TestCase @s.save_method @klass, @meth @s.save_method @klass, @cmeth @s.save_class @nest_klass + @s.encoding = :encoding_value @s.save_cache @@ -207,12 +264,15 @@ class TestRDocRIStore < MiniTest::Unit::TestCase expected = { :attributes => { 'Object' => ['attr_accessor attr'] }, :class_methods => { 'Object' => %w[cmethod] }, - :instance_methods => { 'Object' => %w[method] }, + :instance_methods => { + 'Object' => %w[method method!], + 'Object::SubClass' => %w[method], + }, :modules => %w[Object Object::SubClass], :ancestors => { - 'Object' => %w[], 'Object::SubClass' => %w[Incl Object], }, + :encoding => :encoding_value, } expected[:ancestors]['Object'] = %w[BasicObject] if defined?(::BasicObject) @@ -252,10 +312,7 @@ class TestRDocRIStore < MiniTest::Unit::TestCase assert_directory File.join(@tmpdir, 'Object') assert_file File.join(@tmpdir, 'Object', 'cdesc-Object.ri') - object_ancestors = defined?(::BasicObject) ? %w[BasicObject] : [] - - assert_cache({}, {}, { 'Object' => ['attr_accessor attr'] }, %w[Object], - 'Object' => object_ancestors) + assert_cache nil, nil, nil, %w[Object], 'Object' => OBJECT_ANCESTORS assert_equal @klass, @s.load_class('Object') end @@ -268,12 +325,45 @@ class TestRDocRIStore < MiniTest::Unit::TestCase assert_directory File.join(@tmpdir, 'Object') assert_file File.join(@tmpdir, 'Object', 'cdesc-Object.ri') - assert_cache({}, {}, { 'Object' => ['attr_accessor attr'] }, %w[Object], - 'Object' => %w[]) + assert_cache(nil, nil, nil, %w[Object]) assert_equal @klass, @s.load_class('Object') end + def test_save_class_delete + # save original + @s.save_class @klass + @s.save_method @klass, @meth + @s.save_method @klass, @meth_bang + @s.save_method @klass, @cmeth + @s.save_cache + + klass = RDoc::NormalClass.new 'Object' + + meth = klass.add_method RDoc::AnyMethod.new(nil, 'replace') + meth.record_location @top_level + + # load original, save newly updated class + @s = RDoc::RI::Store.new @tmpdir + @s.load_cache + @s.save_class klass + @s.save_cache + + # load from disk again + @s = RDoc::RI::Store.new @tmpdir + @s.load_cache + + @s.load_class 'Object' + + assert_cache({ 'Object' => %w[replace] }, {}, + { 'Object' => %w[attr_accessor\ attr] }, %w[Object], + 'Object' => OBJECT_ANCESTORS) + + refute File.exist? @s.method_file(@klass.full_name, @meth.full_name) + refute File.exist? @s.method_file(@klass.full_name, @meth_bang.full_name) + refute File.exist? @s.method_file(@klass.full_name, @cmeth.full_name) + end + def test_save_class_dry_run @s.dry_run = true @@ -287,16 +377,17 @@ class TestRDocRIStore < MiniTest::Unit::TestCase @s.save_class @klass klass = RDoc::NormalClass.new 'Object' - klass.comment = 'new class' + klass.add_comment 'new comment', @top_level s = RDoc::RI::Store.new @tmpdir s.save_class klass s = RDoc::RI::Store.new @tmpdir - document = @RM::Document.new( - @RM::Paragraph.new('original'), - @RM::Paragraph.new('new class')) + inner = @RM::Document.new @RM::Paragraph.new 'new comment' + inner.file = @top_level.absolute_name + + document = @RM::Document.new inner assert_equal document, s.load_class('Object').comment end @@ -307,10 +398,7 @@ class TestRDocRIStore < MiniTest::Unit::TestCase assert_directory File.join(@tmpdir, 'Object') assert_file File.join(@tmpdir, 'Object', 'cdesc-Object.ri') - object_ancestors = defined?(::BasicObject) ? %w[BasicObject] : [] - - assert_cache({}, {}, { 'Object' => ['attr_accessor attr'] }, %w[Object], - 'Object' => object_ancestors) + assert_cache nil, nil, nil, %w[Object], 'Object' => OBJECT_ANCESTORS assert_equal @klass, @s.load_class('Object') end @@ -321,8 +409,8 @@ class TestRDocRIStore < MiniTest::Unit::TestCase assert_directory File.join(@tmpdir, 'Object', 'SubClass') assert_file File.join(@tmpdir, 'Object', 'SubClass', 'cdesc-SubClass.ri') - assert_cache({}, {}, {}, %w[Object::SubClass], - 'Object::SubClass' => %w[Incl Object]) + assert_cache({ 'Object::SubClass' => %w[method] }, {}, {}, + %w[Object::SubClass], 'Object::SubClass' => %w[Incl Object]) end def test_save_method diff --git a/test/rdoc/test_rdoc_rubygems_hook.rb b/test/rdoc/test_rdoc_rubygems_hook.rb new file mode 100644 index 0000000000..e8f8621820 --- /dev/null +++ b/test/rdoc/test_rdoc_rubygems_hook.rb @@ -0,0 +1,201 @@ +require 'rubygems/test_case' +require 'rubygems' +require 'rdoc/rubygems_hook' + +class TestRDocRubygemsHook < Gem::TestCase + + def setup + super + skip 'requires RubyGems 1.9+' unless + Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.9') + + @a = quick_spec 'a' + + @rdoc = RDoc::RubygemsHook.new @a + + begin + RDoc::RubygemsHook.load_rdoc + rescue Gem::DocumentError => e + skip e.message + end + + Gem.configuration[:rdoc] = nil + end + + def test_initialize + assert @rdoc.generate_rdoc + assert @rdoc.generate_ri + + rdoc = RDoc::RubygemsHook.new @a, false, false + + refute rdoc.generate_rdoc + refute rdoc.generate_ri + end + + def test_delete_legacy_args + args = %w[ + --inline-source + --one-file + --promiscuous + -p + ] + + @rdoc.delete_legacy_args args + + assert_empty args + end + + def test_document + options = RDoc::Options.new + options.files = [] + + @rdoc.instance_variable_set :@rdoc, @rdoc.new_rdoc + @rdoc.instance_variable_set :@file_info, [] + + @rdoc.document 'darkfish', options, @a.doc_dir('rdoc') + + assert @rdoc.rdoc_installed? + end + + def test_generate + FileUtils.mkdir_p @a.doc_dir + FileUtils.mkdir_p File.join(@a.gem_dir, 'lib') + + @rdoc.generate + + assert @rdoc.rdoc_installed? + assert @rdoc.ri_installed? + + rdoc = @rdoc.instance_variable_get :@rdoc + + refute rdoc.options.hyperlink_all + end + + def test_generate_configuration_rdoc_array + Gem.configuration[:rdoc] = %w[-A] + + FileUtils.mkdir_p @a.doc_dir + FileUtils.mkdir_p File.join(@a.gem_dir, 'lib') + + @rdoc.generate + + rdoc = @rdoc.instance_variable_get :@rdoc + + assert rdoc.options.hyperlink_all + end + + def test_generate_configuration_rdoc_string + Gem.configuration[:rdoc] = '-A' + + FileUtils.mkdir_p @a.doc_dir + FileUtils.mkdir_p File.join(@a.gem_dir, 'lib') + + @rdoc.generate + + rdoc = @rdoc.instance_variable_get :@rdoc + + assert rdoc.options.hyperlink_all + end + + def test_generate_disabled + @rdoc.generate_rdoc = false + @rdoc.generate_ri = false + + @rdoc.generate + + refute @rdoc.rdoc_installed? + refute @rdoc.ri_installed? + end + + def test_generate_force + FileUtils.mkdir_p @a.doc_dir 'ri' + FileUtils.mkdir_p @a.doc_dir 'rdoc' + FileUtils.mkdir_p File.join(@a.gem_dir, 'lib') + + @rdoc.force = true + + @rdoc.generate + + assert_path_exists File.join(@a.doc_dir('rdoc'), 'index.html') + assert_path_exists File.join(@a.doc_dir('ri'), 'cache.ri') + end + + def test_generate_no_overwrite + FileUtils.mkdir_p @a.doc_dir 'ri' + FileUtils.mkdir_p @a.doc_dir 'rdoc' + FileUtils.mkdir_p File.join(@a.gem_dir, 'lib') + + @rdoc.generate + + refute_path_exists File.join(@a.doc_dir('rdoc'), 'index.html') + refute_path_exists File.join(@a.doc_dir('ri'), 'cache.ri') + end + + def test_new_rdoc + assert_kind_of RDoc::RDoc, @rdoc.new_rdoc + end + + def test_rdoc_installed? + refute @rdoc.rdoc_installed? + + FileUtils.mkdir_p @a.doc_dir 'rdoc' + + assert @rdoc.rdoc_installed? + end + + def test_remove + FileUtils.mkdir_p @a.doc_dir 'rdoc' + FileUtils.mkdir_p @a.doc_dir 'ri' + + @rdoc.remove + + refute @rdoc.rdoc_installed? + refute @rdoc.ri_installed? + + assert_path_exists @a.doc_dir + end + + def test_remove_unwritable + skip 'chmod not supported' if Gem.win_platform? + FileUtils.mkdir_p @a.base_dir + FileUtils.chmod 0, @a.base_dir + + e = assert_raises Gem::FilePermissionError do + @rdoc.remove + end + + assert_equal @a.base_dir, e.directory + ensure + FileUtils.chmod 0755, @a.base_dir + end + + def test_ri_installed? + refute @rdoc.ri_installed? + + FileUtils.mkdir_p @a.doc_dir 'ri' + + assert @rdoc.ri_installed? + end + + def test_setup + @rdoc.setup + + assert_path_exists @a.doc_dir + end + + def test_setup_unwritable + skip 'chmod not supported' if Gem.win_platform? + FileUtils.mkdir_p @a.doc_dir + FileUtils.chmod 0, @a.doc_dir + + e = assert_raises Gem::FilePermissionError do + @rdoc.setup + end + + assert_equal @a.doc_dir, e.directory + ensure + FileUtils.chmod 0755, @a.doc_dir + end + +end + diff --git a/test/rdoc/test_rdoc_stats.rb b/test/rdoc/test_rdoc_stats.rb index 17458adaa4..0bf08334da 100644 --- a/test/rdoc/test_rdoc_stats.rb +++ b/test/rdoc/test_rdoc_stats.rb @@ -17,7 +17,7 @@ class TestRDocStats < MiniTest::Unit::TestCase tl = RDoc::TopLevel.new 'file.rb' c = tl.add_class RDoc::NormalClass, 'C' c.record_location tl - c.comment = 'C' + c.add_comment 'C', tl a = RDoc::Attr.new nil, 'a', 'RW', nil a.record_location tl @@ -43,7 +43,7 @@ end tl = RDoc::TopLevel.new 'file.rb' c = tl.add_class RDoc::NormalClass, 'C' c.record_location tl - c.comment = 'C' + c.add_comment 'C', tl a = RDoc::Attr.new nil, 'a', 'RW', 'a' a.record_location tl @@ -60,7 +60,7 @@ end tl = RDoc::TopLevel.new 'file.rb' m = tl.add_module RDoc::NormalModule, 'M' m.record_location tl - m.comment = 'M' + m.add_comment 'M', tl c = RDoc::Constant.new 'C', nil, nil c.record_location tl @@ -152,7 +152,7 @@ end tl = RDoc::TopLevel.new 'file.rb' c = tl.add_class RDoc::NormalClass, 'C' c.record_location tl - c.comment = 'C' + c.add_comment 'C', tl m = RDoc::AnyMethod.new nil, 'm' m.record_location tl @@ -170,7 +170,7 @@ end tl = RDoc::TopLevel.new 'file.rb' c1 = tl.add_class RDoc::NormalClass, 'C1' c1.record_location tl - c1.comment = 'C1' + c1.add_comment 'C1', tl m1 = RDoc::AnyMethod.new nil, 'm1' m1.record_location tl @@ -231,7 +231,7 @@ The following items are not documented: c2 = tl.add_class RDoc::NormalClass, 'C2' c2.record_location tl - c2.comment = 'C2' + c2.add_comment 'C2', tl RDoc::TopLevel.complete :public @@ -291,7 +291,7 @@ end tl = RDoc::TopLevel.new 'file.rb' c = tl.add_class RDoc::NormalClass, 'C' c.record_location tl - c.comment = 'C' + c.add_comment 'C', tl m1 = RDoc::AnyMethod.new nil, 'm1' m1.record_location tl @@ -324,7 +324,7 @@ end tl = RDoc::TopLevel.new 'file.rb' c = tl.add_class RDoc::NormalClass, 'C' c.record_location tl - c.comment = 'C' + c.add_comment 'C', tl m = RDoc::AnyMethod.new nil, 'm' m.record_location tl @@ -342,7 +342,7 @@ end tl = RDoc::TopLevel.new 'file.rb' c = tl.add_class RDoc::NormalClass, 'C' c.record_location tl - c.comment = 'C' + c.add_comment 'C', tl m1 = RDoc::AnyMethod.new nil, 'm1' m1.record_location tl @@ -379,7 +379,7 @@ end tl = RDoc::TopLevel.new 'file.rb' c = tl.add_class RDoc::NormalClass, 'C' c.record_location tl - c.comment = 'C' + c.add_comment 'C', tl m = RDoc::AnyMethod.new nil, 'm' m.record_location tl @@ -399,7 +399,7 @@ end tl = RDoc::TopLevel.new 'file.rb' c = tl.add_class RDoc::NormalClass, 'C' c.record_location tl - c.comment = 'C' + c.add_comment 'C', tl m = RDoc::AnyMethod.new nil, 'm' m.record_location tl @@ -505,7 +505,7 @@ Total: 1 (1 undocumented) tl = RDoc::TopLevel.new 'file.rb' c = tl.add_class RDoc::NormalClass, 'C' c.record_location tl - c.comment = 'C' + c.add_comment 'C', tl m = RDoc::AnyMethod.new nil, 'm' m.record_location tl diff --git a/test/rdoc/test_rdoc_text.rb b/test/rdoc/test_rdoc_text.rb index 96635aaa8e..e11c995e59 100644 --- a/test/rdoc/test_rdoc_text.rb +++ b/test/rdoc/test_rdoc_text.rb @@ -240,6 +240,23 @@ The comments associated with assert_equal expected, strip_stars(text) end + def test_strip_stars_document_method + text = <<-TEXT +/* + * Document-method: Zlib::GzipFile#mtime= + * + * A comment + */ + TEXT + + expected = <<-EXPECTED + + A comment + EXPECTED + + assert_equal expected, strip_stars(text) + end + def test_strip_stars_encoding skip "Encoding not implemented" unless Object.const_defined? :Encoding diff --git a/test/rdoc/test_rdoc_top_level.rb b/test/rdoc/test_rdoc_top_level.rb index 89f64247f9..6c1bc43e7a 100644 --- a/test/rdoc/test_rdoc_top_level.rb +++ b/test/rdoc/test_rdoc_top_level.rb @@ -81,6 +81,15 @@ class TestRDocTopLevel < XrefTestCase RDoc::TopLevel.modules.map { |m| m.full_name }.sort end + def test_class_new + tl1 = RDoc::TopLevel.new 'file.rb' + tl2 = RDoc::TopLevel.new 'file.rb' + tl3 = RDoc::TopLevel.new 'other.rb' + + assert_same tl1, tl2 + refute_same tl1, tl3 + end + def test_class_reset RDoc::TopLevel.reset @@ -93,6 +102,24 @@ class TestRDocTopLevel < XrefTestCase assert_equal 'top_level.rb', @top_level.base_name end + def test_eql_eh + top_level2 = RDoc::TopLevel.new 'path/top_level.rb' + other_level = RDoc::TopLevel.new 'path/other_level.rb' + + assert_operator @top_level, :eql?, top_level2 + + refute_operator other_level, :eql?, @top_level + end + + def test_equals2 + top_level2 = RDoc::TopLevel.new 'path/top_level.rb' + other_level = RDoc::TopLevel.new 'path/other_level.rb' + + assert_equal @top_level, top_level2 + + refute_equal other_level, @top_level + end + def test_find_class_or_module assert_equal @c1, @xref_data.find_class_or_module('C1') assert_equal @c2_c3, @xref_data.find_class_or_module('C2::C3') @@ -104,6 +131,14 @@ class TestRDocTopLevel < XrefTestCase assert_equal 'path/top_level.rb', @top_level.full_name end + def test_hash + tl2 = RDoc::TopLevel.new 'path/top_level.rb' + tl3 = RDoc::TopLevel.new 'other/top_level.rb' + + assert_equal @top_level.hash, tl2.hash + refute_equal @top_level.hash, tl3.hash + end + def test_http_url assert_equal 'prefix/path/top_level_rb.html', @top_level.http_url('prefix') end