diff --git a/lib/rdoc/cross_reference.rb b/lib/rdoc/cross_reference.rb index a5e4fc879f..0e7aa0b209 100644 --- a/lib/rdoc/cross_reference.rb +++ b/lib/rdoc/cross_reference.rb @@ -190,19 +190,13 @@ def resolve_local_symbol(name) ## # 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. + # If the reference is found and +name+ is not documented +nil+ will be + # returned. If +name+ is not found +nil+ is returned. - def resolve(name, text) + def resolve(name) return @seen[name] if @seen.include? name - ref = case name - when /^\\(#{CLASS_REGEXP_STR})$/o then - @context.find_symbol $1 - else - @context.find_symbol name - end + ref = @context.find_symbol name ref = resolve_local_symbol name unless ref @@ -211,21 +205,7 @@ def resolve(name, text) 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 + out = ref if ref&.display? @seen[name] = out diff --git a/lib/rdoc/markup/to_html_crossref.rb b/lib/rdoc/markup/to_html_crossref.rb index fad87a5802..d7f695ae52 100644 --- a/lib/rdoc/markup/to_html_crossref.rb +++ b/lib/rdoc/markup/to_html_crossref.rb @@ -62,13 +62,13 @@ def cross_reference(name, text = nil, code = true, rdoc_ref: false) name = name[1..-1] unless @show_hash if name[0, 1] == '#' if !(name.end_with?('+@', '-@')) and name =~ /(.*[^#:])?@/ - text ||= [CGI.unescape($'), (" at #{$1}" if $~.begin(1))].join("") + text ||= [convert_string(CGI.unescape($')), (" at #{convert_string($1)}" if $~.begin(1))].join("") code = false else - text ||= name + text ||= convert_string(name) end - link lookup, text, code, rdoc_ref: rdoc_ref + create_html_link lookup, text, code, rdoc_ref: rdoc_ref end ## @@ -91,7 +91,10 @@ def handle_regexp_CROSSREF(name) return name if name =~ /\A[a-z]*\z/ end - cross_reference name, rdoc_ref: false + # Even if name is not a crossref, RDoc removes prefix '#' here. Maybe bug. + fallback_name = @show_hash ? name : name.delete_prefix('#') + + cross_reference(name, rdoc_ref: false) || convert_string(fallback_name) end ## @@ -103,7 +106,8 @@ def handle_regexp_HYPERLINK(url) case url when /\Ardoc-ref:/ - cross_reference $', rdoc_ref: true + ref = $' + cross_reference(ref, rdoc_ref: true) || convert_string(ref) else super end @@ -123,7 +127,8 @@ def handle_regexp_RDOCLINK(url) if in_tidylink_label? convert_string(url) else - cross_reference $', rdoc_ref: true + ref = $' + cross_reference(ref, rdoc_ref: true) || convert_string(ref) end else super @@ -137,7 +142,7 @@ def handle_regexp_RDOCLINK(url) def gen_url(url, text) if url =~ /\Ardoc-ref:/ name = $' - cross_reference name, text, name == text, rdoc_ref: true + cross_reference(name, text, name == text, rdoc_ref: true) || text else super end @@ -145,26 +150,32 @@ def gen_url(url, text) ## # Creates an HTML link to +name+ with the given +text+. + # Called from html generators. + + def link(name, text) + create_html_link(name, convert_string(text)) + end + + # Creates an HTML link to +name+ with the given html +text+. - def link(name, text, code = true, rdoc_ref: false) + def create_html_link(name, text, code = true, rdoc_ref: false) if !(name.end_with?('+@', '-@')) and name =~ /(.*[^#:])?@/ name = $1 label = $' end - ref = @cross_reference.resolve name, text if name + ref = @cross_reference.resolve(name) if name - case ref - when String then + if name && ref.nil? if rdoc_ref && @options.warn_missing_rdoc_ref puts "#{@from_path}: `rdoc-ref:#{name}` can't be resolved for `#{text}`" end - ref + nil else path = ref ? ref.as_href(@from_path) : +"" if code and RDoc::CodeObject === ref and !(RDoc::TopLevel === ref) - text = "#{CGI.escapeHTML text}" + text = "#{text}" end if label @@ -203,29 +214,41 @@ def link(name, text, code = true, rdoc_ref: false) end def handle_TT(code) - emit_inline(tt_cross_reference(code) || "#{CGI.escapeHTML code}") + emit_inline(tt_cross_reference(code) || "#{convert_string(code)}") end # Applies additional special handling on top of the one defined in ToHtml. # When a tidy link is {Foo}[rdoc-ref:Foo], the label part is surrounded by . # TODO: reconsider this workaround. def apply_tidylink_label_special_handling(label, url) - if url == "rdoc-ref:#{label}" && cross_reference(label).include?('') + if url == "rdoc-ref:#{label}" && cross_reference(label)&.include?('') "#{convert_string(label)}" else super end end + # Handles cross-reference and suppressed-crossref inside tt tag. + # Returns nil if code is not a cross-reference nor a suppressed-crossref. def tt_cross_reference(code) return if in_tidylink_label? crossref_regexp = @options.hyperlink_all ? ALL_CROSSREF_REGEXP : CROSSREF_REGEXP - match = crossref_regexp.match(code) + # REGEXP sometimes matches to a string starts with backslash which is not a suppressed crossref. (e.g. `\+`) + # We need to check the backslash removed part matches to crossref_regexp + match = crossref_regexp.match(code.delete_prefix('\\')) return unless match && match.begin(1).zero? return unless match.post_match.match?(/\A[[:punct:]\s]*\z/) - ref = cross_reference(code) - ref if ref != code + if code.start_with?('\\') + # Remove leading backslash if crossref exists + "#{convert_string(code[1..])}" if cross_reference(code[1..]) + else + # Even if code is not a crossref, RDoc removes prefix '#' here. Maybe bug. + # `#comment` will be rendered as `comment` + fallback_code = @show_hash ? code : code.delete_prefix('#') + + cross_reference(code) || "#{convert_string(fallback_code)}" + end end end diff --git a/test/rdoc/markup/to_html_crossref_test.rb b/test/rdoc/markup/to_html_crossref_test.rb index ce69aa50db..7f72872066 100644 --- a/test/rdoc/markup/to_html_crossref_test.rb +++ b/test/rdoc/markup/to_html_crossref_test.rb @@ -52,6 +52,18 @@ def test_convert_CROSSREF_backslash_in_tt assert_equal para('.bar.hello(\\)'), result end + def test_convert_suppressed_CROSSREF_in_tt + result = @to.convert 'C1 \C1' + assert_equal para('C1 C1'), result + + result = @to.convert 'C1#m() \C1#m()' + assert_equal para('C1#m() C1#m()'), result + + # Keep backshash if crossref doesn't exitst + result = @to.convert 'C1#& \\C1#&' + assert_equal para('C1#& \\C1#&'), result + end + def test_convert_CROSSREF_ignored_excluded_words @options.autolink_excluded_words = ['C1'] @@ -338,7 +350,7 @@ def test_to_html_CROSSREF_email_hyperlink_all end def test_link - assert_equal 'n', @to.link('n', 'n') + assert_nil @to.link('n', 'n') assert_equal 'm', @to.link('m', 'm') end diff --git a/test/rdoc/rdoc_cross_reference_test.rb b/test/rdoc/rdoc_cross_reference_test.rb index ebb70bb564..40108ffad9 100644 --- a/test/rdoc/rdoc_cross_reference_test.rb +++ b/test/rdoc/rdoc_cross_reference_test.rb @@ -13,11 +13,11 @@ def setup end def assert_ref(expected, name) - assert_equal expected, @xref.resolve(name, 'fail') + assert_equal expected, @xref.resolve(name) end def refute_ref(name) - assert_equal name, @xref.resolve(name, name) + assert_nil @xref.resolve(name) end def test_METHOD_REGEXP_STR @@ -202,22 +202,21 @@ def assert_resolve_method(x) end def test_resolve_no_ref - assert_equal '', @xref.resolve('', '') + refute_ref('') - assert_equal "bogus", @xref.resolve("bogus", "bogus") - assert_equal "\\bogus", @xref.resolve("\\bogus", "\\bogus") - assert_equal "\\\\bogus", @xref.resolve("\\\\bogus", "\\\\bogus") + refute_ref("bogus") + refute_ref("\\bogus") - assert_equal "\\#n", @xref.resolve("\\#n", "fail") - assert_equal "\\#n()", @xref.resolve("\\#n()", "fail") - assert_equal "\\#n(*)", @xref.resolve("\\#n(*)", "fail") + refute_ref("\\#n") + refute_ref("\\#n()") + refute_ref("\\#n(*)") - assert_equal "C1", @xref.resolve("\\C1", "fail") - assert_equal "::C3", @xref.resolve("\\::C3", "fail") + refute_ref("\\C1") + refute_ref("\\::C3") - 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") + refute_ref("::C3::H1#n") + refute_ref("::C3::H1#n(*)") + refute_ref("\\::C3::H1#n") end end