From 89b601d176a64f1293a3d3b5195b6735cbf880af Mon Sep 17 00:00:00 2001 From: drbrain Date: Sun, 31 Jul 2011 00:19:00 +0000 Subject: * lib/rdoc: Update to RDoc 3.9. Fixed `ri []`, stopdoc creating an object reference, nodoc for class aliases, verbatim === lines. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@32767 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 5 + bin/rdoc | 7 + bin/ri | 7 + lib/rdoc.rb | 2 +- lib/rdoc/class_module.rb | 3 + lib/rdoc/code_object.rb | 43 +++ lib/rdoc/context.rb | 7 +- lib/rdoc/cross_reference.rb | 173 +++++++++++ lib/rdoc/generator/darkfish.rb | 2 +- .../generator/template/darkfish/classpage.rhtml | 2 + lib/rdoc/markup.rb | 62 ++-- lib/rdoc/markup/document.rb | 16 +- lib/rdoc/markup/formatter.rb | 4 + lib/rdoc/markup/parser.rb | 18 +- lib/rdoc/markup/pre_process.rb | 137 ++++++--- lib/rdoc/markup/to_html.rb | 13 +- lib/rdoc/markup/to_html_crossref.rb | 208 ++++--------- lib/rdoc/parser.rb | 5 + lib/rdoc/parser/c.rb | 12 +- lib/rdoc/parser/ruby.rb | 54 ++-- lib/rdoc/parser/ruby_tools.rb | 2 + lib/rdoc/ri/driver.rb | 33 +- test/rdoc/test_rdoc_code_object.rb | 62 +++- test/rdoc/test_rdoc_cross_reference.rb | 154 ++++++++++ test/rdoc/test_rdoc_generator_darkfish.rb | 6 + test/rdoc/test_rdoc_markup_document.rb | 18 ++ test/rdoc/test_rdoc_markup_parser.rb | 38 +++ test/rdoc/test_rdoc_markup_pre_process.rb | 340 ++++++++++++++++----- test/rdoc/test_rdoc_markup_to_html.rb | 8 + test/rdoc/test_rdoc_markup_to_html_crossref.rb | 161 +++------- test/rdoc/test_rdoc_parser_ruby.rb | 95 +++--- test/rdoc/test_rdoc_ri_driver.rb | 159 +++++++--- test/rdoc/xref_test_case.rb | 5 + 33 files changed, 1295 insertions(+), 566 deletions(-) create mode 100644 lib/rdoc/cross_reference.rb create mode 100644 test/rdoc/test_rdoc_cross_reference.rb diff --git a/ChangeLog b/ChangeLog index 230734bf3f..6ede87c7ac 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Sun Jul 31 09:18:28 2011 Eric Hodel + + * lib/rdoc: Update to RDoc 3.9. Fixed `ri []`, stopdoc creating an + object reference, nodoc for class aliases, verbatim === lines. + Sun Jul 31 01:29:08 2011 NARUSE, Yui * io.c (rb_io_each_byte): remove unused variable e. diff --git a/bin/rdoc b/bin/rdoc index 8fafd01b0f..64601a819e 100755 --- a/bin/rdoc +++ b/bin/rdoc @@ -8,6 +8,13 @@ # # $Revision$ +begin + gem 'rdoc' +rescue NameError => e # --disable-gems + raise unless e.name == :gem +rescue Gem::LoadError +end + require 'rdoc/rdoc' begin diff --git a/bin/ri b/bin/ri index 243557403c..7fbed0c099 100755 --- a/bin/ri +++ b/bin/ri @@ -1,5 +1,12 @@ #!/usr/bin/env ruby +begin + gem 'rdoc' +rescue NameError => e # --disable-gems + raise unless e.name == :gem +rescue Gem::LoadError +end + require 'rdoc/ri/driver' RDoc::RI::Driver.run ARGV diff --git a/lib/rdoc.rb b/lib/rdoc.rb index 187b7d4d51..e05e22a3a1 100644 --- a/lib/rdoc.rb +++ b/lib/rdoc.rb @@ -104,7 +104,7 @@ module RDoc ## # RDoc version you are using - VERSION = '3.8' + VERSION = '3.9' ## # Method visibilities diff --git a/lib/rdoc/class_module.rb b/lib/rdoc/class_module.rb index e104101dcc..27066d8bd7 100644 --- a/lib/rdoc/class_module.rb +++ b/lib/rdoc/class_module.rb @@ -222,6 +222,9 @@ class RDoc::ClassModule < RDoc::Context end end + ## + # TODO: filter included items by #display? + def marshal_dump # :nodoc: attrs = attributes.sort.map do |attr| [ attr.name, attr.rw, diff --git a/lib/rdoc/code_object.rb b/lib/rdoc/code_object.rb index 606fd8ff49..54826fffbd 100644 --- a/lib/rdoc/code_object.rb +++ b/lib/rdoc/code_object.rb @@ -114,6 +114,7 @@ class RDoc::CodeObject @done_documenting = false @force_documentation = false @received_nodoc = false + @ignored = false end ## @@ -139,6 +140,13 @@ class RDoc::CodeObject end end + ## + # Should this CodeObject be shown in documentation? + + def display? + @document_self and not @ignored + end + ## # Enables or disables documentation of this CodeObject's children unless it # has been turned off by :enddoc: @@ -195,6 +203,11 @@ class RDoc::CodeObject self end + ## + # File name where this CodeObject was found. + # + # See also RDoc::Context#in_files + def file_name return unless @file @@ -220,6 +233,34 @@ class RDoc::CodeObject @full_name = full_name end + ## + # Use this to ignore a CodeObject and all its children until found again + # (#record_location is called). An ignored item will not be shown in + # documentation. + # + # See github issue #55 + # + # The ignored status is temporary in order to allow implementation details + # to be hidden. At the end of processing a file RDoc allows all classes + # and modules to add new documentation to previously created classes. + # + # If a class was ignored (via stopdoc) then reopened later with additional + # documentation it should be shown. If a class was ignored and never + # reopened it should not be shown. The ignore flag allows this to occur. + + def ignore + @ignored = true + + stop_doc + end + + ## + # Has this class been ignored? + + def ignored? + @ignored + end + ## # File name of our parent @@ -238,6 +279,7 @@ class RDoc::CodeObject # Records the RDoc::TopLevel (file) where this code object was defined def record_location top_level + @ignored = false @file = top_level end @@ -250,6 +292,7 @@ class RDoc::CodeObject @document_self = true @document_children = true + @ignored = false end ## diff --git a/lib/rdoc/context.rb b/lib/rdoc/context.rb index d2552c647c..abdab2026d 100644 --- a/lib/rdoc/context.rb +++ b/lib/rdoc/context.rb @@ -423,6 +423,7 @@ class RDoc::Context < RDoc::CodeObject if klass then # if TopLevel, it may not be registered in the classes: enclosing.classes_hash[name] = klass + # update the superclass if needed if superclass then existing = klass.superclass @@ -623,8 +624,10 @@ class RDoc::Context < RDoc::CodeObject ## # Is there any content? - # This means any of: comment, aliases, methods, attributes, - # external aliases, require, constant. + # + # This means any of: comment, aliases, methods, attributes, external + # aliases, require, constant. + # # Includes are also checked unless includes == false. def any_content(includes = true) diff --git a/lib/rdoc/cross_reference.rb b/lib/rdoc/cross_reference.rb new file mode 100644 index 0000000000..adeef2661a --- /dev/null +++ b/lib/rdoc/cross_reference.rb @@ -0,0 +1,173 @@ +## +# RDoc::CrossReference is a reusable way to create cross references for names. + +class RDoc::CrossReference + + ## + # Regular expression to match class references + # + # 1. There can be a '\\' in front of text to suppress the cross-reference + # 2. There can be a '::' in front of class names to reference from the + # top-level namespace. + # 3. The method can be followed by parenthesis (not recommended) + + CLASS_REGEXP_STR = '\\\\?((?:\:{2})?[A-Z]\w*(?:\:\:\w+)*)' + + ## + # Regular expression to match method references. + # + # See CLASS_REGEXP_STR + + METHOD_REGEXP_STR = '([a-z]\w*[!?=]?)(?:\([\w.+*/=<>-]*\))?' + + ## + # Regular expressions matching text that should potentially have + # cross-reference links generated are passed to add_special. Note that + # these expressions are meant to pick up text for which cross-references + # have been suppressed, since the suppression characters are removed by the + # code that is triggered. + + CROSSREF_REGEXP = /( + # A::B::C.meth + #{CLASS_REGEXP_STR}(?:[.#]|::)#{METHOD_REGEXP_STR} + + # Stand-alone method (preceded by a #) + | \\?\##{METHOD_REGEXP_STR} + + # Stand-alone method (preceded by ::) + | ::#{METHOD_REGEXP_STR} + + # A::B::C + # The stuff after CLASS_REGEXP_STR is a + # nasty hack. CLASS_REGEXP_STR unfortunately matches + # words like dog and cat (these are legal "class" + # names in Fortran 95). When a word is flagged as a + # potential cross-reference, limitations in the markup + # engine suppress other processing, such as typesetting. + # This is particularly noticeable for contractions. + # In order that words like "can't" not + # be flagged as potential cross-references, only + # flag potential class cross-references if the character + # after the cross-reference is a space, sentence + # punctuation, tag start character, or attribute + # marker. + | #{CLASS_REGEXP_STR}(?=[\s\)\.\?\!\,\;<\000]|\z) + + # Things that look like filenames + # The key thing is that there must be at least + # one special character (period, slash, or + # underscore). + | (?:\.\.\/)*[-\/\w]+[_\/\.][-\w\/\.]+ + + # Things that have markup suppressed + # Don't process things like '\<' in \, though. + # TODO: including < is a hack, not very satisfying. + | \\[^\s<] + )/x + + ## + # Version of CROSSREF_REGEXP used when --hyperlink-all is specified. + + ALL_CROSSREF_REGEXP = /( + # A::B::C.meth + #{CLASS_REGEXP_STR}(?:[.#]|::)#{METHOD_REGEXP_STR} + + # Stand-alone method + | \\?#{METHOD_REGEXP_STR} + + # A::B::C + | #{CLASS_REGEXP_STR}(?=[\s\)\.\?\!\,\;<\000]|\z) + + # Things that look like filenames + | (?:\.\.\/)*[-\/\w]+[_\/\.][-\w\/\.]+ + + # Things that have markup suppressed + | \\[^\s<] + )/x + + attr_accessor :seen + + ## + # Allows cross-references to be created based on the given +context+ + # (RDoc::Context). + + def initialize context + @context = context + + @seen = {} + end + + ## + # Returns a reference to +name+. + # + # If the reference is found and +name+ is not documented +text+ will be + # returned. If +name+ is escaped +name+ is returned. If +name+ is not + # found +text+ is returned. + + def resolve name, text + return @seen[name] if @seen.include? name + + # Find class, module, or method in class or module. + # + # Do not, however, use an if/elsif/else chain to do so. Instead, test + # each possible pattern until one matches. The reason for this is that a + # string like "YAML.txt" could be the txt() class method of class YAML (in + # which case it would match the first pattern, which splits the string + # into container and method components and looks up both) or a filename + # (in which case it would match the last pattern, which just checks + # whether the string as a whole is a known symbol). + + if /#{CLASS_REGEXP_STR}([.#]|::)#{METHOD_REGEXP_STR}/o =~ name then + type = $2 + type = '' if type == '.' # will find either #method or ::method + method = "#{type}#{$3}" + container = @context.find_symbol_module($1) + elsif /^([.#]|::)#{METHOD_REGEXP_STR}/o =~ name then + type = $1 + type = '' if type == '.' + method = "#{type}#{$2}" + container = @context + else + container = nil + end + + if container then + ref = container.find_local_symbol method + + unless ref || RDoc::TopLevel === container then + ref = container.find_ancestor_local_symbol method + end + end + + ref = case name + when /^\\(#{CLASS_REGEXP_STR})$/o then + ref = @context.find_symbol $1 + else + ref = @context.find_symbol name + end unless ref + + ref = nil if RDoc::Alias === ref # external alias: can't link to it + + out = if name == '\\' then + name + elsif name =~ /^\\/ then + # we remove the \ only in front of what we know: + # other backslashes are treated later, only outside of + ref ? $' : name + elsif ref then + if ref.display? then + ref + else + text + end + else + text + end + + @seen[name] = out + + out + end + +end + diff --git a/lib/rdoc/generator/darkfish.rb b/lib/rdoc/generator/darkfish.rb index 299e6b7dec..a3ffea0ce8 100644 --- a/lib/rdoc/generator/darkfish.rb +++ b/lib/rdoc/generator/darkfish.rb @@ -192,7 +192,7 @@ class RDoc::Generator::Darkfish top_level = klass.full_name.gsub( /::.*/, '' ) [nscounts[top_level] * -1, klass.full_name] end.select do |klass| - klass.document_self + klass.display? end end diff --git a/lib/rdoc/generator/template/darkfish/classpage.rhtml b/lib/rdoc/generator/template/darkfish/classpage.rhtml index 856321532b..9c74cacf0f 100644 --- a/lib/rdoc/generator/template/darkfish/classpage.rhtml +++ b/lib/rdoc/generator/template/darkfish/classpage.rhtml @@ -176,6 +176,8 @@ <% klass.each_section do |section, constants, attributes| %> + <% constants = constants.select { |const| const.display? } %> + <% attributes = attributes.select { |attr| attr.display? } %>
<% if section.title then %>

diff --git a/lib/rdoc/markup.rb b/lib/rdoc/markup.rb index 6122fcac65..3dd2459e61 100644 --- a/lib/rdoc/markup.rb +++ b/lib/rdoc/markup.rb @@ -269,40 +269,43 @@ require 'rdoc' # preceding the first character with a backslash (see Escaping # Text Markup, below). # -# === Hyperlinks +# === Links # -# Hyperlinks to the web starting with +http:+, +mailto:+, +ftp:+ or +www.+ +# Links to starting with +http:+, +https:+, +mailto:+, +ftp:+ or +www.+ # are recognized. An HTTP url that references an external image file is -# converted into an inline . Hyperlinks starting with +link:+ are -# assumed to refer to local files whose path is relative to the --op -# directory. +# converted into an inline image element. # -# Hyperlinks can also be of the form _label_[_url_], in which -# case _label_ is used in the displayed text, and _url_ is -# used as the target. If _label_ contains multiple words, -# put it in braces: {multi word label}[url]. +# Links starting with rdoc-ref: will link to the referenced class, +# module, method, file, etc. If the referenced item is not documented the +# text will be and no link will be generated. # -# Example hyperlinks: +# Links starting with +link:+ refer to local files whose path is relative to +# the --op directory. # -# link:RDoc.html -# http://rdoc.rubyforge.org +# Links can also be of the form label[url], in which case +label+ is +# used in the displayed text, and +url+ is used as the target. If +label+ +# contains multiple words, put it in braces: {multi word label}[url]. +# +# Example links: +# +# https://github.com/rdoc/rdoc # mailto:user@example.com # {RDoc Documentation}[http://rdoc.rubyforge.org] -# {RDoc Markup}[link:RDoc/Markup.html] +# {RDoc Markup}[rdoc-ref:RDoc::Markup] # # === Escaping Text Markup # # Text markup can be escaped with a backslash, as in \, which was obtained -# with "\\". Except in verbatim sections and between \ tags, -# to produce a backslash, you have to double it unless it is followed by a +# with \\. Except in verbatim sections and between \ tags, +# to produce a backslash you have to double it unless it is followed by a # space, tab or newline. Otherwise, the HTML formatter will discard it, as it -# is used to escape potential hyperlinks: +# is used to escape potential links: # # * The \ must be doubled if not followed by white space: \\. # * But not in \ tags: in a Regexp, \S matches non-space. # * This is a link to {ruby-lang}[www.ruby-lang.org]. # * This is not a link, however: \{ruby-lang.org}[www.ruby-lang.org]. -# * This will not be hyperlinked to \RDoc::RDoc#document +# * This will not be linked to \RDoc::RDoc#document # # generates: # @@ -310,16 +313,16 @@ require 'rdoc' # * But not in \ tags: in a Regexp, \S matches non-space. # * This is a link to {ruby-lang}[www.ruby-lang.org] # * This is not a link, however: \{ruby-lang.org}[www.ruby-lang.org] -# * This will not be hyperlinked to \RDoc::RDoc#document +# * This will not be linked to \RDoc::RDoc#document # -# Inside \ tags, more precisely, leading backslashes are removed -# only if followed by a markup character (<*_+), a backslash, -# or a known hyperlink reference (a known class or method). So in the -# example above, the backslash of \S would be removed -# if there was a class or module named +S+ in the current context. +# Inside \ tags, more precisely, leading backslashes are removed only if +# followed by a markup character (<*_+), a backslash, or a known link +# reference (a known class or method). So in the example above, the backslash +# of \S would be removed if there was a class or module named +S+ in +# the current context. # -# This behavior is inherited from RDoc version 1, and has been kept -# for compatibility with existing RDoc documentation. +# This behavior is inherited from RDoc version 1, and has been kept for +# compatibility with existing RDoc documentation. # # === Conversion of characters # @@ -378,11 +381,10 @@ require 'rdoc' # # ... # end # -# Names of classes, files, and any method names containing an -# underscore or preceded by a hash character are automatically hyperlinked -# from comment text to their description. This hyperlinking works inside -# the current class or module, and with ancestor methods (in included modules -# or in the superclass). +# Names of classes, files, and any method names containing an underscore or +# preceded by a hash character are automatically linked from comment text to +# their description. This linking works inside the current class or module, +# and with ancestor methods (in included modules or in the superclass). # # Method parameter lists are extracted and displayed with the method # description. If a method calls +yield+, then the parameters passed to yield diff --git a/lib/rdoc/markup/document.rb b/lib/rdoc/markup/document.rb index 6fbef33ae3..7077f357d3 100644 --- a/lib/rdoc/markup/document.rb +++ b/lib/rdoc/markup/document.rb @@ -71,9 +71,7 @@ class RDoc::Markup::Document # Does this document have no parts? def empty? - @parts.empty? or - (@parts.length == 1 and RDoc::Markup::Document === @parts.first and - @parts.first.empty?) + @parts.empty? or (@parts.length == 1 and merged? and @parts.first.empty?) end ## @@ -85,6 +83,11 @@ class RDoc::Markup::Document # The information in +other+ is preferred over the receiver def merge other + if empty? then + @parts = other.parts + return self + end + other.parts.each do |other_part| self.parts.delete_if do |self_part| self_part.file and self_part.file == other_part.file @@ -96,6 +99,13 @@ class RDoc::Markup::Document self end + ## + # Does this Document contain other Documents? + + def merged? + RDoc::Markup::Document === @parts.first + end + def pretty_print q # :nodoc: start = @file ? "[doc (#{@file}): " : '[doc: ' diff --git a/lib/rdoc/markup/formatter.rb b/lib/rdoc/markup/formatter.rb index b6e12f82e7..bf904bba68 100644 --- a/lib/rdoc/markup/formatter.rb +++ b/lib/rdoc/markup/formatter.rb @@ -4,6 +4,10 @@ require 'rdoc/markup' # Base class for RDoc markup formatters # # Formatters use a visitor pattern to convert content into output. +# +# If you'd like to write your own Formatter use +# RDoc::Markup::FormatterTestCase. If you're writing a text-output formatter +# use RDoc::Markup::TextFormatterTestCase which provides extra test cases. class RDoc::Markup::Formatter diff --git a/lib/rdoc/markup/parser.rb b/lib/rdoc/markup/parser.rb index 68616d7787..3ab72ee5d5 100644 --- a/lib/rdoc/markup/parser.rb +++ b/lib/rdoc/markup/parser.rb @@ -405,13 +405,19 @@ class RDoc::Markup::Parser @line += 1 token # === text => :HEADER then :TEXT - when s.scan(/(=+)\s*/) then + when s.scan(/(=+)(\s*)/) then level = s[1].length - level = 6 if level > 6 - @tokens << [:HEADER, level, *token_pos(pos)] - pos = s.pos - s.scan(/.*/) - [:TEXT, s.matched.sub(/\r$/, ''), *token_pos(pos)] + header = [:HEADER, level, *token_pos(pos)] + + if s[2] =~ /^\r?\n/ then + s.pos -= s[2].length + header + else + pos = s.pos + s.scan(/.*/) + @tokens << header + [:TEXT, s.matched.sub(/\r$/, ''), *token_pos(pos)] + end # --- (at least 3) and nothing else on the line => :RULE when s.scan(/(-{3,}) *$/) then [:RULE, s[1].length - 2, *token_pos(pos)] diff --git a/lib/rdoc/markup/pre_process.rb b/lib/rdoc/markup/pre_process.rb index 03f919aa0e..53e8e38ec1 100644 --- a/lib/rdoc/markup/pre_process.rb +++ b/lib/rdoc/markup/pre_process.rb @@ -13,6 +13,8 @@ require 'rdoc/encoding' class RDoc::Markup::PreProcess + attr_accessor :options + @registered = {} ## @@ -38,6 +40,7 @@ class RDoc::Markup::PreProcess def initialize(input_file_name, include_path) @input_file_name = input_file_name @include_path = include_path + @options = nil end ## @@ -54,54 +57,120 @@ class RDoc::Markup::PreProcess # If +code_object+ is given and the param is set as metadata on the # +code_object+. See RDoc::CodeObject#metadata - def handle text, code_object = nil + def handle text, code_object = nil, &block + encoding = if defined?(Encoding) then text.encoding else nil end # regexp helper (square brackets for optional) # $1 $2 $3 $4 $5 # [prefix][\]:directive:[spaces][param]newline - text.gsub!(/^([ \t]*#?[ \t]*)(\\?):(\w+):([ \t]*)(.+)?\n/) do + text.gsub!(/^([ \t]*(?:#|\/?\*)?[ \t]*)(\\?):(\w+):([ \t]*)(.+)?\n/) do # skip something like ':toto::' next $& if $4.empty? and $5 and $5[0, 1] == ':' # skip if escaped next "#$1:#$3:#$4#$5\n" unless $2.empty? - prefix = $1 - directive = $3.downcase - param = $5 - - case directive - when 'include' then - 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 + handle_directive $1, $3, $5, code_object, encoding, &block + end + + text + end + + #-- + # When 1.8.7 support is ditched prefix can be defaulted to '' + + def handle_directive prefix, directive, param, code_object = nil, + encoding = nil + blankline = "#{prefix.strip}\n" + directive = directive.downcase + + case directive + when 'arg', 'args' then + return blankline unless code_object + + code_object.params = param + + blankline + when 'category' then + if RDoc::Context === code_object then + section = code_object.add_section param, '' + code_object.temporary_section = section + end + + blankline # ignore category if we're not on an RDoc::Context + when 'doc' then + return blankline unless code_object + code_object.document_self = true + code_object.force_documentation = true + + blankline + when 'enddoc' then + return blankline unless code_object + code_object.done_documenting = true + + blankline + when 'include' then + filename = param.split.first + include_file filename, prefix, encoding + when 'main' then + @options.main_page = param if @options.respond_to? :main_page + + blankline + when 'nodoc' then + return blankline unless code_object + code_object.document_self = nil # notify nodoc + code_object.document_children = param !~ /all/i - '' # ignore category if we're not on an RDoc::Context - else - result = yield directive, param if block_given? - - case result - when nil then - code_object.metadata[directive] = param if code_object - if RDoc::Markup::PreProcess.registered.include? directive then - handler = RDoc::Markup::PreProcess.registered[directive] - result = handler.call directive, param if handler - else - result = "#{prefix}:#{directive}: #{param}\n" - end - when false then + blankline + when 'notnew', 'not_new', 'not-new' then + return blankline unless RDoc::AnyMethod === code_object + + code_object.dont_rename_initialize = true + + blankline + when 'startdoc' then + return blankline unless code_object + + code_object.start_doc + code_object.force_documentation = true + + blankline + when 'stopdoc' then + return blankline unless code_object + + code_object.stop_doc + + blankline + when 'title' then + @options.default_title = param if @options.respond_to? :default_title= + + blankline + when 'yield', 'yields' then + return blankline unless code_object + # remove parameter &block + code_object.params.sub!(/,?\s*&\w+/, '') if code_object.params + + code_object.block_params = param + + blankline + else + result = yield directive, param if block_given? + + case result + when nil then + code_object.metadata[directive] = param if code_object + + if RDoc::Markup::PreProcess.registered.include? directive then + handler = RDoc::Markup::PreProcess.registered[directive] + result = handler.call directive, param if handler + else result = "#{prefix}:#{directive}: #{param}\n" end - - result + when false then + result = "#{prefix}:#{directive}: #{param}\n" end - end - text + result + end end ## diff --git a/lib/rdoc/markup/to_html.rb b/lib/rdoc/markup/to_html.rb index b518fbf265..f87aabad6f 100644 --- a/lib/rdoc/markup/to_html.rb +++ b/lib/rdoc/markup/to_html.rb @@ -70,7 +70,7 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter @list = nil @from_path = '' - # external hyperlinks + # external links @markup.add_special(/((link:|https?:|mailto:|ftp:|www\.)\S+\w)/, :HYPERLINK) # and links of the form [] @@ -84,7 +84,7 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter # These methods handle special markup added by RDoc::Markup#add_special. ## - # +special+ is a potential hyperlink. The following schemes are handled: + # +special+ is a potential link. The following schemes are handled: # # mailto::: # Inserted as-is. @@ -97,12 +97,13 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter def handle_special_HYPERLINK(special) url = special.text + gen_url url, url end ## - # This +special+ is a hyperlink where the label is different from the URL - # label[url] or {long label}[url] + # This +special+ is a link where the label is different from the URL + # label[url] or {long label}[url] def handle_special_TIDYLINK(special) text = special.text @@ -232,8 +233,8 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter end ## - # Generate a hyperlink for +url+, labeled with +text+. Handles the special - # cases for img: and link: described under handle_special_HYPERLINK + # Generate a link for +url+, labeled with +text+. Handles the special cases + # for img: and link: described under handle_special_HYPERLINK def gen_url(url, text) if url =~ /([A-Za-z]+):(.*)/ then diff --git a/lib/rdoc/markup/to_html_crossref.rb b/lib/rdoc/markup/to_html_crossref.rb index aeecc3f4d0..5175b79192 100644 --- a/lib/rdoc/markup/to_html_crossref.rb +++ b/lib/rdoc/markup/to_html_crossref.rb @@ -1,92 +1,19 @@ require 'rdoc/markup/to_html' +require 'rdoc/cross_reference' ## -# Subclass of the RDoc::Markup::ToHtml class that supports looking up words -# from a context. Those that are found will be hyperlinked. +# Subclass of the RDoc::Markup::ToHtml class that supports looking up method +# names, classes, etc to create links. RDoc::CrossReference is used to +# generate those links based on the current context. class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml - ## - # Regular expression to match class references - # - # 1. There can be a '\\' in front of text to suppress the cross-reference - # 2. There can be a '::' in front of class names to reference from the - # top-level namespace. - # 3. The method can be followed by parenthesis (not recommended) - - CLASS_REGEXP_STR = '\\\\?((?:\:{2})?[A-Z]\w*(?:\:\:\w+)*)' - - ## - # Regular expression to match method references. - # - # See CLASS_REGEXP_STR - - METHOD_REGEXP_STR = '([a-z]\w*[!?=]?)(?:\([\w.+*/=<>-]*\))?' - - ## - # Regular expressions matching text that should potentially have - # cross-reference links generated are passed to add_special. Note that - # these expressions are meant to pick up text for which cross-references - # have been suppressed, since the suppression characters are removed by the - # code that is triggered. - - CROSSREF_REGEXP = /( - # A::B::C.meth - #{CLASS_REGEXP_STR}(?:[.#]|::)#{METHOD_REGEXP_STR} - - # Stand-alone method (preceded by a #) - | \\?\##{METHOD_REGEXP_STR} - - # Stand-alone method (preceded by ::) - | ::#{METHOD_REGEXP_STR} - - # A::B::C - # The stuff after CLASS_REGEXP_STR is a - # nasty hack. CLASS_REGEXP_STR unfortunately matches - # words like dog and cat (these are legal "class" - # names in Fortran 95). When a word is flagged as a - # potential cross-reference, limitations in the markup - # engine suppress other processing, such as typesetting. - # This is particularly noticeable for contractions. - # In order that words like "can't" not - # be flagged as potential cross-references, only - # flag potential class cross-references if the character - # after the cross-reference is a space, sentence - # punctuation, tag start character, or attribute - # marker. - | #{CLASS_REGEXP_STR}(?=[\s\)\.\?\!\,\;<\000]|\z) - - # Things that look like filenames - # The key thing is that there must be at least - # one special character (period, slash, or - # underscore). - | (?:\.\.\/)*[-\/\w]+[_\/\.][-\w\/\.]+ - - # Things that have markup suppressed - # Don't process things like '\<' in \, though. - # TODO: including < is a hack, not very satisfying. - | \\[^\s<] - )/x - - ## - # Version of CROSSREF_REGEXP used when --hyperlink-all is specified. - - ALL_CROSSREF_REGEXP = /( - # A::B::C.meth - #{CLASS_REGEXP_STR}(?:[.#]|::)#{METHOD_REGEXP_STR} - - # Stand-alone method - | \\?#{METHOD_REGEXP_STR} - - # A::B::C - | #{CLASS_REGEXP_STR}(?=[\s\)\.\?\!\,\;<\000]|\z) - - # Things that look like filenames - | (?:\.\.\/)*[-\/\w]+[_\/\.][-\w\/\.]+ - - # Things that have markup suppressed - | \\[^\s<] - )/x + # :stopdoc: + ALL_CROSSREF_REGEXP = RDoc::CrossReference::ALL_CROSSREF_REGEXP + CLASS_REGEXP_STR = RDoc::CrossReference::CLASS_REGEXP_STR + CROSSREF_REGEXP = RDoc::CrossReference::CROSSREF_REGEXP + METHOD_REGEXP_STR = RDoc::CrossReference::METHOD_REGEXP_STR + # :startdoc: ## # RDoc::CodeObject for generating references @@ -102,7 +29,7 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml # Creates a new crossref resolver that generates links relative to +context+ # which lives at +from_path+ in the generated files. '#' characters on # references are removed unless +show_hash+ is true. Only method names - # preceded by '#' or '::' are hyperlinked, unless +hyperlink_all+ is true. + # preceded by '#' or '::' are linked, unless +hyperlink_all+ is true. def initialize(from_path, context, show_hash, hyperlink_all = false, markup = nil) @@ -111,22 +38,36 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml crossref_re = hyperlink_all ? ALL_CROSSREF_REGEXP : CROSSREF_REGEXP + @cross_reference = RDoc::CrossReference.new context + @markup.add_special crossref_re, :CROSSREF + @markup.add_special(/rdoc-ref:\S+\w/, :HYPERLINK) - @from_path = from_path - @context = context - @show_hash = show_hash + @from_path = from_path @hyperlink_all = hyperlink_all + @show_hash = show_hash + end + + ## + # Creates a link to the reference +name+ if the name exists. If +text+ is + # given it is used as the link text, otherwise +name+ is used. + + def cross_reference name, text = nil + lookup = name - @seen = {} + name = name[1..-1] unless @show_hash if name[0, 1] == '#' + + text = name unless text + + link lookup, text end ## # We're invoked when any text matches the CROSSREF pattern. If we find the - # corresponding reference, generate a hyperlink. If the name we're looking - # for contains no punctuation, we look for it up the module/class chain. - # For example, ToHtml is found, even without the RDoc::Markup:: - # prefix, because we look for it in module Markup first. + # corresponding reference, generate a link. If the name we're looking for + # contains no punctuation, we look for it up the module/class chain. For + # example, ToHtml is found, even without the RDoc::Markup:: prefix, + # because we look for it in module Markup first. def handle_special_CROSSREF(special) name = special.text @@ -138,66 +79,41 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml return name if name =~ /\A[a-z]*\z/ end - return @seen[name] if @seen.include? name + cross_reference name + end - lookup = name + ## + # Handles rdoc-ref: scheme links and allows RDoc::Markup::ToHtml to + # handle other schemes. - name = name[1..-1] unless @show_hash if name[0, 1] == '#' + def handle_special_HYPERLINK special + return cross_reference $' if special.text =~ /\Ardoc-ref:/ - # Find class, module, or method in class or module. - # - # Do not, however, use an if/elsif/else chain to do so. Instead, test - # each possible pattern until one matches. The reason for this is that a - # string like "YAML.txt" could be the txt() class method of class YAML (in - # which case it would match the first pattern, which splits the string - # into container and method components and looks up both) or a filename - # (in which case it would match the last pattern, which just checks - # whether the string as a whole is a known symbol). - - if /#{CLASS_REGEXP_STR}([.#]|::)#{METHOD_REGEXP_STR}/ =~ lookup then - type = $2 - type = '' if type == '.' # will find either #method or ::method - method = "#{type}#{$3}" - container = @context.find_symbol_module($1) - elsif /^([.#]|::)#{METHOD_REGEXP_STR}/ =~ lookup then - type = $1 - type = '' if type == '.' - method = "#{type}#{$2}" - container = @context - else - container = nil - end + super + end - if container then - ref = container.find_local_symbol method + ## + # Generates links for rdoc-ref: scheme URLs and allows + # RDoc::Markup::ToHtml to handle other schemes. - unless ref || RDoc::TopLevel === container then - ref = container.find_ancestor_local_symbol method - end - end + def gen_url url, text + super unless url =~ /\Ardoc-ref:/ - ref = @context.find_symbol lookup unless ref - ref = nil if RDoc::Alias === ref # external alias: can't link to it - - out = if lookup == '\\' then - lookup - elsif lookup =~ /^\\/ then - # we remove the \ only in front of what we know: - # other backslashes are treated later, only outside of - ref ? $' : lookup - elsif ref then - if ref.document_self then - "#{name}" - else - name - end - else - lookup - end - - @seen[lookup] = out - - out + cross_reference $', text + end + + ## + # Creates an HTML link to +name+ with the given +text+. + + def link name, text + ref = @cross_reference.resolve name, text + + case ref + when String then + ref + else + "#{text}" + end end end diff --git a/lib/rdoc/parser.rb b/lib/rdoc/parser.rb index b2559fa3a9..ba7b858595 100644 --- a/lib/rdoc/parser.rb +++ b/lib/rdoc/parser.rb @@ -106,6 +106,8 @@ class RDoc::Parser # Applies +directive+'s +value+ to +code_object+, if appropriate def self.process_directive code_object, directive, value + warn "RDoc::Parser::process_directive is deprecated and wil be removed in RDoc 4. Use RDoc::Markup::PreProcess#handle_directive instead" if $-w + case directive when 'nodoc' then code_object.document_self = nil # notify nodoc @@ -196,6 +198,9 @@ class RDoc::Parser @content = content @options = options @stats = stats + + @preprocess = RDoc::Markup::PreProcess.new @file_name, @options.rdoc_include + @preprocess.options = @options end end diff --git a/lib/rdoc/parser/c.rb b/lib/rdoc/parser/c.rb index a0282d69f1..3da1820c50 100644 --- a/lib/rdoc/parser/c.rb +++ b/lib/rdoc/parser/c.rb @@ -645,9 +645,7 @@ class RDoc::Parser::C < RDoc::Parser meth_obj.call_seq = $1.strip end - if comment.sub!(/\s*:(nodoc|doc|yields?|args?):\s*(.*)/, '') then - RDoc::Parser.process_directive meth_obj, $1, $2 - end + look_for_directives_in meth_obj, comment end ## @@ -913,12 +911,10 @@ class RDoc::Parser::C < RDoc::Parser # * :title: My Awesome Project # */ # - # This routine modifies its parameter - - def look_for_directives_in(context, comment) - preprocess = RDoc::Markup::PreProcess.new @file_name, @options.rdoc_include + # This method modifies the +comment+ - preprocess.handle comment, context do |directive, param| + def look_for_directives_in context, comment + @preprocess.handle comment, context do |directive, param| case directive when 'main' then @options.main_page = param diff --git a/lib/rdoc/parser/ruby.rb b/lib/rdoc/parser/ruby.rb index 8b7c9c3eff..c9a12a8fe8 100644 --- a/lib/rdoc/parser/ruby.rb +++ b/lib/rdoc/parser/ruby.rb @@ -405,17 +405,9 @@ class RDoc::Parser::Ruby < RDoc::Parser # # This routine modifies its +comment+ parameter. - def look_for_directives_in(context, comment) - preprocess = RDoc::Markup::PreProcess.new @file_name, @options.rdoc_include - - preprocess.handle comment, context do |directive, param| + def look_for_directives_in context, comment + @preprocess.handle comment, context do |directive, param| case directive - when 'enddoc' then - context.done_documenting = true - '' - when 'main' then - @options.main_page = param if @options.respond_to? :main_page - '' when 'method', 'singleton-method', 'attr', 'attr_accessor', 'attr_reader', 'attr_writer' then false # handled elsewhere @@ -423,16 +415,6 @@ class RDoc::Parser::Ruby < RDoc::Parser context.set_current_section param, comment comment.replace '' break - when 'startdoc' then - context.start_doc - context.force_documentation = true - '' - when 'stopdoc' then - context.stop_doc - '' - when 'title' then - @options.default_title = param if @options.respond_to? :default_title= - '' end end @@ -629,6 +611,7 @@ class RDoc::Parser::Ruby < RDoc::Parser cls_type = single == SINGLE ? RDoc::SingleClass : RDoc::NormalClass cls = declaration_context.add_class cls_type, given_name, superclass + cls.ignore unless container.document_children read_documentation_modifiers cls, RDoc::CLASS_MODIFIERS cls.record_location @top_level @@ -679,7 +662,7 @@ class RDoc::Parser::Ruby < RDoc::Parser ## # Parses a constant in +context+ with +comment+ - def parse_constant(container, tk, comment) + def parse_constant container, tk, comment offset = tk.seek line_no = tk.line_no @@ -718,7 +701,8 @@ class RDoc::Parser::Ruby < RDoc::Parser when TkRPAREN, TkRBRACE, TkRBRACK, TkEND then nest -= 1 when TkCOMMENT then - if nest <= 0 && @scanner.lex_state == EXPR_END + if nest <= 0 && + (@scanner.lex_state == EXPR_END || !@scanner.continue) then unget_tk tk break end @@ -733,7 +717,6 @@ class RDoc::Parser::Ruby < RDoc::Parser end container.add_module_alias mod, name, @top_level if mod - get_tk # TkNL break end when TkNL then @@ -1327,11 +1310,7 @@ class RDoc::Parser::Ruby < RDoc::Parser keep_comment = true when TkCLASS then - if container.document_children then - parse_class container, single, tk, comment - else - nest += 1 - end + parse_class container, single, tk, comment when TkMODULE then if container.document_children then @@ -1516,11 +1495,13 @@ class RDoc::Parser::Ruby < RDoc::Parser ## # Parses statements in the top-level +container+ - def parse_top_level_statements(container) + def parse_top_level_statements container comment = collect_first_comment - look_for_directives_in(container, comment) + look_for_directives_in container, comment + # HACK move if to RDoc::Context#comment= container.comment = comment if container.document_self unless comment.empty? + parse_statements container, NORMAL, nil, comment end @@ -1643,16 +1624,17 @@ class RDoc::Parser::Ruby < RDoc::Parser # Handles the directive for +context+ if the directive is listed in +allow+. # This method is called for directives following a definition. - def read_documentation_modifiers(context, allow) + def read_documentation_modifiers context, allow directive, value = read_directive allow return unless directive - case directive - when 'notnew', 'not_new', 'not-new' then - context.dont_rename_initialize = true - else - RDoc::Parser.process_directive context, directive, value + @preprocess.handle_directive '', directive, value, context do |dir, param| + if %w[notnew not_new not-new].include? dir then + context.dont_rename_initialize = true + + true + end end end diff --git a/lib/rdoc/parser/ruby_tools.rb b/lib/rdoc/parser/ruby_tools.rb index 3f6190884e..678f721624 100644 --- a/lib/rdoc/parser/ruby_tools.rb +++ b/lib/rdoc/parser/ruby_tools.rb @@ -153,6 +153,8 @@ module RDoc::Parser::RubyTools @token_listeners.each do |obj| obj.pop_token end if @token_listeners + + nil end end diff --git a/lib/rdoc/ri/driver.rb b/lib/rdoc/ri/driver.rb index a99f96cb56..26304dca96 100644 --- a/lib/rdoc/ri/driver.rb +++ b/lib/rdoc/ri/driver.rb @@ -387,6 +387,8 @@ Options may also be set in the 'RI' environment variable. klass.superclass unless klass.module? end.compact.shift || 'Object' + superclass = superclass.full_name unless String === superclass + "#{name} < #{superclass}" end @@ -451,7 +453,7 @@ Options may also be set in the 'RI' environment variable. # Adds a list of +methods+ to +out+ with a heading of +name+ def add_method_list out, methods, name - return unless methods + return if methods.empty? out << RDoc::Markup::Heading.new(1, "#{name}:") out << RDoc::Markup::BlankLine.new @@ -518,11 +520,13 @@ Options may also be set in the 'RI' environment variable. found.each do |store, klass| comment = klass.comment - class_methods = store.class_methods[klass.full_name] - instance_methods = store.instance_methods[klass.full_name] - attributes = store.attributes[klass.full_name] + # TODO the store's cache should always return an empty Array + class_methods = store.class_methods[klass.full_name] || [] + instance_methods = store.instance_methods[klass.full_name] || [] + attributes = store.attributes[klass.full_name] || [] - if comment.empty? and !(instance_methods or class_methods) then + if comment.empty? and + instance_methods.empty? and class_methods.empty? then also_in << store next end @@ -531,7 +535,17 @@ Options may also be set in the 'RI' environment variable. unless comment.empty? then out << RDoc::Markup::Rule.new(1) - out << comment + + if comment.merged? then + parts = comment.parts + parts = parts.zip [RDoc::Markup::BlankLine.new] * parts.length + parts.flatten! + parts.pop + + out.push(*parts) + else + out << comment + end end if class_methods or instance_methods or not klass.constants.empty? then @@ -554,13 +568,12 @@ Options may also be set in the 'RI' environment variable. end) out << list + out << RDoc::Markup::BlankLine.new end add_method_list out, class_methods, 'Class methods' add_method_list out, instance_methods, 'Instance methods' add_method_list out, attributes, 'Attributes' - - out << RDoc::Markup::BlankLine.new end add_also_in out, also_in @@ -1090,11 +1103,11 @@ Options may also be set in the 'RI' environment variable. # NOTE: Given Foo::Bar, Bar is considered a class even though it may be a # method - def parse_name(name) + def parse_name name parts = name.split(/(::|#|\.)/) if parts.length == 1 then - if parts.first =~ /^[a-z]/ then + if parts.first =~ /^[a-z]|^([%&*+\/<>^`|~-]|\+@|-@|<<|<=>?|===?|=>|=~|>>|\[\]=?|~@)$/ then type = '.' meth = parts.pop else diff --git a/test/rdoc/test_rdoc_code_object.rb b/test/rdoc/test_rdoc_code_object.rb index cdac7eeed6..89dc4b1744 100644 --- a/test/rdoc/test_rdoc_code_object.rb +++ b/test/rdoc/test_rdoc_code_object.rb @@ -67,6 +67,30 @@ class TestRDocCodeObject < XrefTestCase assert_equal Encoding::UTF_8, @co.comment.encoding end + def test_display_eh_document_self + assert @co.display? + + @co.document_self = false + + refute @co.display? + end + + def test_display_eh_ignore + assert @co.display? + + @co.ignore + + refute @co.display? + + @co.stop_doc + + refute @co.display? + + @co.done_documenting = false + + refute @co.display? + end + def test_document_children_equals @co.document_children = false refute @co.document_children @@ -156,6 +180,22 @@ class TestRDocCodeObject < XrefTestCase assert_nil @co.instance_variable_get(:@full_name) end + def test_ignore + @co.ignore + + refute @co.document_self + refute @co.document_children + assert @co.ignored? + end + + def test_ignore_eh + refute @co.ignored? + + @co.ignore + + assert @co.ignored? + end + def test_line @c1_m.line = 5 @@ -202,10 +242,16 @@ class TestRDocCodeObject < XrefTestCase end def test_record_location - c = RDoc::CodeObject.new - c.record_location @xref_data + @co.record_location @xref_data - assert_equal 'xref_data.rb', c.file.relative_name + assert_equal 'xref_data.rb', @co.file.relative_name + end + + def test_record_location_ignored + @co.ignore + @co.record_location @xref_data + + refute @co.ignored? end def test_start_doc @@ -218,6 +264,16 @@ class TestRDocCodeObject < XrefTestCase assert @co.document_children end + def test_start_doc_ignored + @co.ignore + + @co.start_doc + + assert @co.document_self + assert @co.document_children + refute @co.ignored? + end + def test_stop_doc @co.document_self = true @co.document_children = true diff --git a/test/rdoc/test_rdoc_cross_reference.rb b/test/rdoc/test_rdoc_cross_reference.rb new file mode 100644 index 0000000000..06062ba125 --- /dev/null +++ b/test/rdoc/test_rdoc_cross_reference.rb @@ -0,0 +1,154 @@ +require 'rubygems' +require 'minitest/autorun' +require File.expand_path '../xref_test_case', __FILE__ + +class TestRDocCrossReference < XrefTestCase + + def setup + super + + @xref = RDoc::CrossReference.new @c1 + end + + def assert_ref expected, name + assert_equal expected, @xref.resolve(name, 'fail') + end + + def refute_ref name + assert_equal name, @xref.resolve(name, name) + end + + def test_resolve_C2 + @xref = RDoc::CrossReference.new @c2 + + refute_ref '#m' + + assert_ref @c1__m, 'C1::m' + assert_ref @c2_c3, 'C2::C3' + assert_ref @c2_c3_m, 'C2::C3#m' + assert_ref @c2_c3_h1, 'C3::H1' + assert_ref @c4, 'C4' + + assert_ref @c3_h2, 'C3::H2' + refute_ref 'H1' + end + + def test_resolve_C2_C3 + @xref = RDoc::CrossReference.new @c2_c3 + + assert_ref @c2_c3_m, '#m' + + assert_ref @c2_c3, 'C3' + assert_ref @c2_c3_m, 'C3#m' + + assert_ref @c2_c3_h1, 'H1' + assert_ref @c2_c3_h1, 'C3::H1' + + assert_ref @c4, 'C4' + + assert_ref @c3_h2, 'C3::H2' + end + + def test_resolve_C3 + @xref = RDoc::CrossReference.new @c3 + + assert_ref @c3, 'C3' + + refute_ref '#m' + refute_ref 'C3#m' + + assert_ref @c3_h1, 'H1' + + assert_ref @c3_h1, 'C3::H1' + assert_ref @c3_h2, 'C3::H2' + + assert_ref @c4, 'C4' + end + + def test_resolve_C4 + @xref = RDoc::CrossReference.new @c4 + + # C4 ref inside a C4 containing a C4 should resolve to the contained class + assert_ref @c4_c4, 'C4' + end + + def test_resolve_C4_C4 + @xref = RDoc::CrossReference.new @c4_c4 + + # A C4 reference inside a C4 class contained within a C4 class should + # resolve to the inner C4 class. + assert_ref @c4_c4, 'C4' + end + + def test_resolve_class + assert_ref @c1, 'C1' + refute_ref 'H1' + + assert_ref @c2, 'C2' + assert_ref @c2_c3, 'C2::C3' + assert_ref @c2_c3_h1, 'C2::C3::H1' + + assert_ref @c3, '::C3' + assert_ref @c3_h1, '::C3::H1' + + assert_ref @c4_c4, 'C4::C4' + end + + def test_resolve_file + assert_ref @xref_data, 'xref_data.rb' + end + + def test_resolve_method + assert_ref @c1__m, 'm' + assert_ref @c1_m, '#m' + assert_ref @c1__m, '::m' + + assert_ref @c1_m, 'C1#m' + assert_ref @c1__m, 'C1.m' + assert_ref @c1__m, 'C1::m' + + assert_ref @c1_m, 'C1#m' + assert_ref @c1_m, 'C1#m()' + assert_ref @c1_m, 'C1#m(*)' + + assert_ref @c1__m, 'C1.m' + assert_ref @c1__m, 'C1.m()' + assert_ref @c1__m, 'C1.m(*)' + + assert_ref @c1__m, 'C1::m' + assert_ref @c1__m, 'C1::m()' + assert_ref @c1__m, 'C1::m(*)' + + assert_ref @c2_c3_m, 'C2::C3#m' + + assert_ref @c2_c3_m, 'C2::C3.m' + + # TODO stop escaping - HTML5 allows anything but space + assert_ref @c2_c3_h1_meh, 'C2::C3::H1#m?' + + assert_ref @c2_c3_m, '::C2::C3#m' + assert_ref @c2_c3_m, '::C2::C3#m()' + assert_ref @c2_c3_m, '::C2::C3#m(*)' + end + + def test_resolve_no_ref + assert_equal '', @xref.resolve('', '') + + assert_equal "bogus", @xref.resolve("bogus", "bogus") + assert_equal "\\bogus", @xref.resolve("\\bogus", "\\bogus") + assert_equal "\\\\bogus", @xref.resolve("\\\\bogus", "\\\\bogus") + + assert_equal "\\#n", @xref.resolve("\\#n", "fail") + assert_equal "\\#n()", @xref.resolve("\\#n()", "fail") + assert_equal "\\#n(*)", @xref.resolve("\\#n(*)", "fail") + + assert_equal "C1", @xref.resolve("\\C1", "fail") + assert_equal "::C3", @xref.resolve("\\::C3", "fail") + + assert_equal "succeed", @xref.resolve("::C3::H1#n", "succeed") + assert_equal "succeed", @xref.resolve("::C3::H1#n(*)", "succeed") + assert_equal "\\::C3::H1#n", @xref.resolve("\\::C3::H1#n", "fail") + end + +end + diff --git a/test/rdoc/test_rdoc_generator_darkfish.rb b/test/rdoc/test_rdoc_generator_darkfish.rb index b99803bac1..faea1ae34a 100644 --- a/test/rdoc/test_rdoc_generator_darkfish.rb +++ b/test/rdoc/test_rdoc_generator_darkfish.rb @@ -38,6 +38,7 @@ class TestRDocGeneratorDarkfish < 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_bang = RDoc::AnyMethod.new nil, 'method!' @attr = RDoc::Attr.new nil, 'attr', 'RW', '' @@ -45,6 +46,9 @@ class TestRDocGeneratorDarkfish < MiniTest::Unit::TestCase @klass.add_method @meth @klass.add_method @meth_bang @klass.add_attribute @attr + + @ignored = @top_level.add_class RDoc::NormalClass, 'Ignored' + @ignored.ignore end def teardown @@ -83,6 +87,8 @@ class TestRDocGeneratorDarkfish < MiniTest::Unit::TestCase File.read('Object.html')) assert_match(/README.txt', link + end + def test_list_verbatim_2 str = "* one\n verb1\n verb2\n* two\n" diff --git a/test/rdoc/test_rdoc_markup_to_html_crossref.rb b/test/rdoc/test_rdoc_markup_to_html_crossref.rb index 8c97941727..4611e45309 100644 --- a/test/rdoc/test_rdoc_markup_to_html_crossref.rb +++ b/test/rdoc/test_rdoc_markup_to_html_crossref.rb @@ -10,159 +10,86 @@ class TestRDocMarkupToHtmlCrossref < XrefTestCase def setup super - @xref = RDoc::Markup::ToHtmlCrossref.new 'index.html', @c1, true + @to = RDoc::Markup::ToHtmlCrossref.new 'index.html', @c1, true end - def assert_ref(path, ref) - assert_equal "\n

#{ref}

\n", @xref.convert(ref) - end + def test_convert_CROSSREF + result = @to.convert 'C1' - def refute_ref(body, ref) - assert_equal "\n

#{body}

\n", @xref.convert(ref) + assert_equal "\n

C1

\n", result end - def test_handle_special_CROSSREF_C2 - @xref = RDoc::Markup::ToHtmlCrossref.new 'classes/C2.html', @c2, true - - refute_ref '#m', '#m' + def test_convert_HYPERLINK_rdoc_ref + result = @to.convert 'rdoc-ref:C1' - assert_ref '../C1.html#method-c-m', 'C1::m' - assert_ref '../C2/C3.html', 'C2::C3' - assert_ref '../C2/C3.html#method-i-m', 'C2::C3#m' - assert_ref '../C2/C3/H1.html', 'C3::H1' - assert_ref '../C4.html', 'C4' - - assert_ref '../C3/H2.html', 'C3::H2' - refute_ref 'H1', 'H1' + assert_equal "\n

C1

\n", result end - def test_handle_special_CROSSREF_C2_C3 - @xref = RDoc::Markup::ToHtmlCrossref.new 'classes/C2/C3.html', @c2_c3, true - - assert_ref '../../C2/C3.html#method-i-m', '#m' - - assert_ref '../../C2/C3.html', 'C3' - assert_ref '../../C2/C3.html#method-i-m', 'C3#m' - - assert_ref '../../C2/C3/H1.html', 'H1' - assert_ref '../../C2/C3/H1.html', 'C3::H1' - - assert_ref '../../C4.html', 'C4' - - assert_ref '../../C3/H2.html', 'C3::H2' + def test_handle_special_CROSSREF + assert_equal "C2::C3", SPECIAL('C2::C3') end - def test_handle_special_CROSSREF_C3 - @xref = RDoc::Markup::ToHtmlCrossref.new 'classes/C3.html', @c3, true - - assert_ref '../C3.html', 'C3' - - refute_ref '#m', '#m' - refute_ref 'C3#m', 'C3#m' - - assert_ref '../C3/H1.html', 'H1' - - assert_ref '../C3/H1.html', 'C3::H1' - assert_ref '../C3/H2.html', 'C3::H2' - - assert_ref '../C4.html', 'C4' - end - - def test_handle_special_CROSSREF_C4 - @xref = RDoc::Markup::ToHtmlCrossref.new 'classes/C4.html', @c4, true + def test_handle_special_CROSSREF_show_hash_false + @to.show_hash = false - # C4 ref inside a C4 containing a C4 should resolve to the contained class - assert_ref '../C4/C4.html', 'C4' + assert_equal "m", + SPECIAL('#m') end - def test_handle_special_CROSSREF_C4_C4 - @xref = RDoc::Markup::ToHtmlCrossref.new 'classes/C4/C4.html', @c4_c4, true + def test_handle_special_HYPERLINK_rdoc + RDoc::TopLevel.new 'README.txt' + @to = RDoc::Markup::ToHtmlCrossref.new 'C2.html', @c2, true - # A C4 reference inside a C4 class contained within a C4 class should - # resolve to the inner C4 class. - assert_ref '../../C4/C4.html', 'C4' - end + link = @to.handle_special_HYPERLINK hyper 'C2::C3' - def test_handle_special_CROSSREF_class - assert_ref 'C1.html', 'C1' - refute_ref 'H1', 'H1' + assert_equal 'C2::C3', link - assert_ref 'C2.html', 'C2' - assert_ref 'C2/C3.html', 'C2::C3' - assert_ref 'C2/C3/H1.html', 'C2::C3::H1' + link = @to.handle_special_HYPERLINK hyper 'C4' - assert_ref 'C3.html', '::C3' - assert_ref 'C3/H1.html', '::C3::H1' + assert_equal 'C4', link - assert_ref 'C4/C4.html', 'C4::C4' - end + link = @to.handle_special_HYPERLINK hyper 'README.txt' - def test_handle_special_CROSSREF_file - assert_ref 'xref_data_rb.html', 'xref_data.rb' + assert_equal 'README.txt', link end - def test_handle_special_CROSSREF_method - refute_ref 'm', 'm' - assert_ref 'C1.html#method-i-m', '#m' - assert_ref 'C1.html#method-c-m', '::m' - - assert_ref 'C1.html#method-i-m', 'C1#m' - assert_ref 'C1.html#method-c-m', 'C1.m' - assert_ref 'C1.html#method-c-m', 'C1::m' + def test_handle_special_TIDYLINK_rdoc + RDoc::TopLevel.new 'README.txt' + @to = RDoc::Markup::ToHtmlCrossref.new 'C2.html', @c2, true - assert_ref 'C1.html#method-i-m', 'C1#m' - assert_ref 'C1.html#method-i-m', 'C1#m()' - assert_ref 'C1.html#method-i-m', 'C1#m(*)' + link = @to.handle_special_TIDYLINK tidy 'C2::C3' - assert_ref 'C1.html#method-c-m', 'C1.m' - assert_ref 'C1.html#method-c-m', 'C1.m()' - assert_ref 'C1.html#method-c-m', 'C1.m(*)' + assert_equal 'tidy', link - assert_ref 'C1.html#method-c-m', 'C1::m' - assert_ref 'C1.html#method-c-m', 'C1::m()' - assert_ref 'C1.html#method-c-m', 'C1::m(*)' + link = @to.handle_special_TIDYLINK tidy 'C4' - assert_ref 'C2/C3.html#method-i-m', 'C2::C3#m' + assert_equal 'tidy', link - assert_ref 'C2/C3.html#method-i-m', 'C2::C3.m' + link = @to.handle_special_TIDYLINK tidy 'README.txt' - # TODO stop escaping - HTML5 allows anything but space - assert_ref 'C2/C3/H1.html#method-i-m-3F', 'C2::C3::H1#m?' - - assert_ref 'C2/C3.html#method-i-m', '::C2::C3#m' - assert_ref 'C2/C3.html#method-i-m', '::C2::C3#m()' - assert_ref 'C2/C3.html#method-i-m', '::C2::C3#m(*)' + assert_equal 'tidy', link end - def test_handle_special_CROSSREF_no_ref - assert_equal '', @xref.convert('') - - refute_ref 'bogus', 'bogus' - refute_ref 'bogus', '\bogus' - refute_ref '\bogus', '\\\bogus' - - refute_ref '#n', '\#n' - refute_ref '#n()', '\#n()' - refute_ref '#n(*)', '\#n(*)' + def test_link + assert_equal 'n', @to.link('n', 'n') - refute_ref 'C1', '\C1' - refute_ref '::C3', '\::C3' + assert_equal 'm', @to.link('m', 'm') + end - refute_ref '::C3::H1#n', '::C3::H1#n' - refute_ref '::C3::H1#n(*)', '::C3::H1#n(*)' - refute_ref '::C3::H1#n', '\::C3::H1#n' + def SPECIAL text + @to.handle_special_CROSSREF special text end - def test_handle_special_CROSSREF_show_hash_false - @xref.show_hash = false + def hyper reference + RDoc::Markup::Special.new 0, "rdoc-ref:#{reference}" + end - assert_equal "\n

m

\n", - @xref.convert('#m') + def special text + RDoc::Markup::Special.new 0, text end - def test_handle_special_CROSSREF_special - assert_equal "\n

C2::C3;method(*)

\n", - @xref.convert('C2::C3;method(*)') + def tidy reference + RDoc::Markup::Special.new 0, "{tidy}[rdoc-ref:#{reference}]" end end diff --git a/test/rdoc/test_rdoc_parser_ruby.rb b/test/rdoc/test_rdoc_parser_ruby.rb index 4904d5dfca..6086b3ec13 100644 --- a/test/rdoc/test_rdoc_parser_ruby.rb +++ b/test/rdoc/test_rdoc_parser_ruby.rb @@ -313,22 +313,6 @@ class C; end comment end - def test_look_for_directives_in_enddoc - util_parser "" - - @parser.look_for_directives_in @top_level, "# :enddoc:\n" - - assert @top_level.done_documenting - end - - def test_look_for_directives_in_main - util_parser "" - - @parser.look_for_directives_in @top_level, "# :main: new main page\n" - - assert_equal 'new main page', @options.main_page - end - def test_look_for_directives_in_method util_parser "" @@ -345,31 +329,6 @@ class C; end assert_equal "# :singleton-method: my_method\n", comment end - def test_look_for_directives_in_startdoc - util_parser "" - - @top_level.stop_doc - assert !@top_level.document_self - assert !@top_level.document_children - - @parser.look_for_directives_in @top_level, "# :startdoc:\n" - - assert @top_level.document_self - assert @top_level.document_children - end - - def test_look_for_directives_in_stopdoc - util_parser "" - - assert @top_level.document_self - assert @top_level.document_children - - @parser.look_for_directives_in @top_level, "# :stopdoc:\n" - - assert !@top_level.document_self - assert !@top_level.document_children - end - def test_look_for_directives_in_section util_parser "" @@ -384,14 +343,6 @@ class C; end assert_equal '', comment end - def test_look_for_directives_in_title - util_parser "" - - @parser.look_for_directives_in @top_level, "# :title: new title\n" - - assert_equal 'new title', @options.title - end - def test_look_for_directives_in_unhandled util_parser "" @@ -797,12 +748,7 @@ end @parser.parse_class @top_level, RDoc::Parser::Ruby::NORMAL, tk, comment - foo = @top_level.classes.first - assert_equal 'Foo', foo.full_name - assert_equal 'my class', foo.comment - assert_equal [@top_level], foo.in_files - assert_equal 0, foo.offset - assert_equal 1, foo.line + assert_empty @top_level.classes.first.comment end def test_parse_multi_ghost_methods @@ -2227,6 +2173,45 @@ end assert_empty @top_level.comment end + def test_parse_top_level_statements_stopdoc_integration + content = <<-CONTENT +# :stopdoc: + +class Example + def method_name + end +end + CONTENT + + util_parser content + + @parser.parse_top_level_statements @top_level + + assert_equal 1, @top_level.classes.length + assert_empty @top_level.modules + + assert @top_level.find_module_named('Example').ignored? + end + + # This tests parse_comment + def test_parse_top_level_statements_constant_nodoc_integration + content = <<-CONTENT +class A + C = A # :nodoc: +end + CONTENT + + util_parser content + + @parser.parse_top_level_statements @top_level + + klass = @top_level.find_module_named('A') + + c = klass.constants.first + + assert_nil c.document_self, 'C should not be documented' + end + def test_parse_yield_in_braces_with_parens klass = RDoc::NormalClass.new 'Foo' klass.parent = @top_level diff --git a/test/rdoc/test_rdoc_ri_driver.rb b/test/rdoc/test_rdoc_ri_driver.rb index da7d160047..e219993e57 100644 --- a/test/rdoc/test_rdoc_ri_driver.rb +++ b/test/rdoc/test_rdoc_ri_driver.rb @@ -5,6 +5,7 @@ require 'tmpdir' require 'fileutils' require 'stringio' require 'rdoc/ri/driver' +require 'rdoc/rdoc' class TestRDocRIDriver < MiniTest::Unit::TestCase @@ -223,7 +224,7 @@ class TestRDocRIDriver < MiniTest::Unit::TestCase def test_add_method_list_none out = @RM::Document.new - @driver.add_method_list out, nil, 'Class' + @driver.add_method_list out, [], 'Class' assert_equal @RM::Document.new, out end @@ -249,6 +250,46 @@ class TestRDocRIDriver < MiniTest::Unit::TestCase assert_equal expected, @driver.classes end + def test_class_document + util_store + + tl1 = RDoc::TopLevel.new 'one.rb' + tl2 = RDoc::TopLevel.new 'two.rb' + + @cFoo.add_comment 'one', tl1 + @cFoo.add_comment 'two', tl2 + @store.save_class @cFoo + + found = [ + [@store, @store.load_class(@cFoo.full_name)] + ] + + out = @driver.class_document @cFoo.full_name, found, [], [] + + expected = @RM::Document.new + @driver.add_class expected, 'Foo', [] + @driver.add_includes expected, [] + @driver.add_from expected, @store + expected << @RM::Rule.new(1) + + doc = @RM::Document.new(@RM::Paragraph.new('one')) + doc.file = 'one.rb' + expected.push doc + expected << @RM::BlankLine.new + doc = @RM::Document.new(@RM::Paragraph.new('two')) + doc.file = 'two.rb' + expected.push doc + + expected << @RM::Rule.new(1) + expected << @RM::Heading.new(1, 'Instance methods:') + expected << @RM::BlankLine.new + expected << @RM::Verbatim.new('inherit') + expected << @RM::Verbatim.new('override') + expected << @RM::BlankLine.new + + assert_equal expected, out + end + def test_complete store = RDoc::RI::Store.new @home_ri store.cache[:ancestors] = { @@ -633,8 +674,24 @@ Foo::Bar#bother def test_list_methods_matching util_store - assert_equal %w[Foo::Bar#attr Foo::Bar#blah Foo::Bar#bother Foo::Bar::new], - @driver.list_methods_matching('Foo::Bar.') + assert_equal %w[ + Foo::Bar#attr + Foo::Bar#blah + Foo::Bar#bother + Foo::Bar::new + ], + @driver.list_methods_matching('Foo::Bar.').sort + end + + def test_list_methods_matching_inherit + util_multi_store + + assert_equal %w[ + Bar#baz + Bar#inherit + Bar#override + ], + @driver.list_methods_matching('Bar.').sort end def test_list_methods_matching_regexp @@ -805,6 +862,42 @@ Foo::Bar#bother assert_equal 'baz', meth, 'Foo::Bar#baz method' end + def test_parse_name_special + specials = %w[ + % + & + * + + + +@ + - + -@ + / + < + << + <= + <=> + == + === + => + =~ + > + >> + [] + []= + ^ + ` + | + ~ + ~@ + ] + + specials.each do |special| + parsed = @driver.parse_name special + + assert_equal ['', '.', special], parsed + end + end + def _test_setup_pager # this test doesn't do anything anymore :( @driver.use_stdout = false @@ -864,29 +957,28 @@ Foo::Bar#bother def util_multi_store util_store + @store1 = @store + @top_level = RDoc::TopLevel.new 'file.rb' + @home_ri2 = "#{@home_ri}2" @store2 = RDoc::RI::Store.new @home_ri2 # as if seen in a namespace like class Ambiguous::Other - @mAmbiguous = RDoc::NormalModule.new 'Ambiguous' + @mAmbiguous = @top_level.add_module RDoc::NormalModule, 'Ambiguous' - @cFoo = RDoc::NormalClass.new 'Foo' + @cFoo = @top_level.add_class RDoc::NormalClass, 'Foo' - @cBar = RDoc::NormalClass.new 'Bar' - @cBar.superclass = 'Foo' - @cFoo_Baz = RDoc::NormalClass.new 'Baz' - @cFoo_Baz.parent = @cFoo + @cBar = @top_level.add_class RDoc::NormalClass, 'Bar', 'Foo' + @cFoo_Baz = @cFoo.add_class RDoc::NormalClass, 'Baz' - @baz = RDoc::AnyMethod.new nil, 'baz' + @baz = @cBar.add_method RDoc::AnyMethod.new(nil, 'baz') @baz.record_location @top_level - @cBar.add_method @baz - @override = RDoc::AnyMethod.new nil, 'override' + @override = @cBar.add_method RDoc::AnyMethod.new(nil, 'override') @override.comment = 'must be displayed' @override.record_location @top_level - @cBar.add_method @override @store2.save_class @mAmbiguous @store2.save_class @cBar @@ -898,6 +990,8 @@ Foo::Bar#bother @store2.save_cache @driver.stores = [@store1, @store2] + + RDoc::RDoc.reset end def util_store @@ -905,53 +999,42 @@ Foo::Bar#bother @top_level = RDoc::TopLevel.new 'file.rb' - @cFoo = RDoc::NormalClass.new 'Foo' - @mInc = RDoc::NormalModule.new 'Inc' - @cAmbiguous = RDoc::NormalClass.new 'Ambiguous' + @cFoo = @top_level.add_class RDoc::NormalClass, 'Foo' + @mInc = @top_level.add_module RDoc::NormalModule, 'Inc' + @cAmbiguous = @top_level.add_class RDoc::NormalClass, 'Ambiguous' doc = @RM::Document.new @RM::Paragraph.new('Include thingy') - - @cFooInc = RDoc::Include.new 'Inc', doc + @cFooInc = @cFoo.add_include RDoc::Include.new('Inc', doc) @cFooInc.record_location @top_level - @cFoo.add_include @cFooInc - @cFoo_Bar = RDoc::NormalClass.new 'Bar' - @cFoo_Bar.parent = @cFoo + @cFoo_Bar = @cFoo.add_class RDoc::NormalClass, 'Bar' - @blah = RDoc::AnyMethod.new nil, 'blah' + @blah = @cFoo_Bar.add_method 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 = @cFoo_Bar.add_method RDoc::AnyMethod.new(nil, 'bother') @bother.block_params = "stuff" @bother.params = "(things)" @bother.record_location @top_level - @new = RDoc::AnyMethod.new nil, 'new' + @new = @cFoo_Bar.add_method RDoc::AnyMethod.new nil, 'new' @new.record_location @top_level @new.singleton = true - @cFoo_Bar.add_method @blah - @cFoo_Bar.add_method @bother - @cFoo_Bar.add_method @new - - @attr = RDoc::Attr.new nil, 'attr', 'RW', '' + @attr = @cFoo_Bar.add_attribute RDoc::Attr.new nil, 'attr', 'RW', '' @attr.record_location @top_level - @cFoo_Bar.add_attribute @attr - - @cFoo_Baz = RDoc::NormalClass.new 'Baz' - @cFoo_Baz.parent = @cFoo + @cFoo_Baz = @cFoo.add_class RDoc::NormalClass, 'Baz' + @cFoo_Baz.record_location @top_level - @inherit = RDoc::AnyMethod.new nil, 'inherit' + @inherit = @cFoo.add_method 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 = @cFoo.add_method RDoc::AnyMethod.new(nil, 'override') @overriden.comment = 'must not be displayed' @overriden.record_location @top_level - @cFoo.add_method @overriden @store.save_class @cFoo @store.save_class @cFoo_Bar @@ -970,6 +1053,8 @@ Foo::Bar#bother @store.save_cache @driver.stores = [@store] + + RDoc::RDoc.reset end end diff --git a/test/rdoc/xref_test_case.rb b/test/rdoc/xref_test_case.rb index 00c6e8e09d..a001700d3b 100644 --- a/test/rdoc/xref_test_case.rb +++ b/test/rdoc/xref_test_case.rb @@ -43,6 +43,11 @@ class XrefTestCase < MiniTest::Unit::TestCase @c2_b = @c2.method_list.first @c2_c3 = @xref_data.find_module_named 'C2::C3' + @c2_c3_m = @c2_c3.method_list.first # C2::C3#m + + @c2_c3_h1 = @xref_data.find_module_named 'C2::C3::H1' + @c2_c3_h1_meh = @c2_c3_h1.method_list.first # C2::C3::H1#m? + @c3 = @xref_data.find_module_named 'C3' @c4 = @xref_data.find_module_named 'C4' @c4_c4 = @xref_data.find_module_named 'C4::C4' -- cgit v1.2.3