Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 5 additions & 25 deletions lib/rdoc/cross_reference.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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 <tt>
ref ? $' : name
elsif ref then
if ref.display? then
ref
else
text
end
else
text
end
out = ref if ref&.display?

@seen[name] = out

Expand Down
59 changes: 41 additions & 18 deletions lib/rdoc/markup/to_html_crossref.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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 <code>#{$1}</code>" if $~.begin(1))].join("")
text ||= [convert_string(CGI.unescape($')), (" at <code>#{convert_string($1)}</code>" 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

##
Expand All @@ -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

##
Expand All @@ -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
Expand All @@ -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
Expand All @@ -137,34 +142,40 @@ 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
end

##
# 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 = "<code>#{CGI.escapeHTML text}</code>"
text = "<code>#{text}</code>"
end

if label
Expand Down Expand Up @@ -203,29 +214,41 @@ def link(name, text, code = true, rdoc_ref: false)
end

def handle_TT(code)
emit_inline(tt_cross_reference(code) || "<code>#{CGI.escapeHTML code}</code>")
emit_inline(tt_cross_reference(code) || "<code>#{convert_string(code)}</code>")
end

# Applies additional special handling on top of the one defined in ToHtml.
# When a tidy link is <tt>{Foo}[rdoc-ref:Foo]</tt>, the label part is surrounded by <tt><code></code></tt>.
# TODO: reconsider this workaround.
def apply_tidylink_label_special_handling(label, url)
if url == "rdoc-ref:#{label}" && cross_reference(label).include?('<code>')
if url == "rdoc-ref:#{label}" && cross_reference(label)&.include?('<code>')
"<code>#{convert_string(label)}</code>"
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
"<code>#{convert_string(code[1..])}</code>" if cross_reference(code[1..])
else
# Even if code is not a crossref, RDoc removes prefix '#' here. Maybe bug.
# `<tt>#comment</tt>` will be rendered as `<tt>comment</tt>`
fallback_code = @show_hash ? code : code.delete_prefix('#')

cross_reference(code) || "<code>#{convert_string(fallback_code)}</code>"
end
end
end
14 changes: 13 additions & 1 deletion test/rdoc/markup/to_html_crossref_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,18 @@ def test_convert_CROSSREF_backslash_in_tt
assert_equal para('<code>.bar.hello(\\)</code>'), result
end

def test_convert_suppressed_CROSSREF_in_tt
result = @to.convert '<tt>C1</tt> <tt>\C1</tt>'
assert_equal para('<a href="C1.html"><code>C1</code></a> <code>C1</code>'), result

result = @to.convert '<tt>C1#m()</tt> <tt>\C1#m()</tt>'
assert_equal para('<a href="C1.html#method-i-m"><code>C1#m()</code></a> <code>C1#m()</code>'), result

# Keep backshash if crossref doesn't exitst
result = @to.convert '<tt>C1#&</tt> <tt>\\C1#&</tt>'
assert_equal para('<code>C1#&amp;</code> <code>\\C1#&amp;</code>'), result
end

def test_convert_CROSSREF_ignored_excluded_words
@options.autolink_excluded_words = ['C1']

Expand Down Expand Up @@ -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 '<a href="C1.html#method-c-m"><code>m</code></a>', @to.link('m', 'm')
end
Expand Down
27 changes: 13 additions & 14 deletions test/rdoc/rdoc_cross_reference_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Loading