From b709f0bc86cd5f28be552e46a2a7a5071e9e763b Mon Sep 17 00:00:00 2001 From: mmporong Date: Tue, 27 Oct 2026 16:31:31 +0900 Subject: [PATCH] Don't create links inside heading anchors Heading text is wrapped in the heading's own ``. Cross references, URLs and `rdoc-ref:` links in the text were turned into links too, which produced nested `` tags (invalid HTML). Convert the heading text as a link label, the way tidy link labels already are, so it is rendered as plain text. Tidy links now restore the previous label state instead of clearing it, so text after a tidy link in a heading stays unlinked as well. --- lib/rdoc/markup/to_html.rb | 13 ++++++++++--- test/rdoc/markup/to_html_crossref_test.rb | 15 +++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/lib/rdoc/markup/to_html.rb b/lib/rdoc/markup/to_html.rb index 6477f98910..6baab3ff4e 100644 --- a/lib/rdoc/markup/to_html.rb +++ b/lib/rdoc/markup/to_html.rb @@ -300,9 +300,10 @@ def handle_TIDYLINK(label_part, url) if label_part.size == 1 && String === label_part[0] raw_label = label_part[0] + in_tidylink_label = @in_tidylink_label @in_tidylink_label = true special = apply_tidylink_label_special_handling(raw_label, url) - @in_tidylink_label = false + @in_tidylink_label = in_tidylink_label if special tag = gen_url(CGI.escapeHTML(url), special) @@ -317,9 +318,10 @@ def handle_TIDYLINK(label_part, url) open_tag, close_tag = tag.split(/(?=<\/a>)/, 2) valid_tag = open_tag && close_tag emit_inline(open_tag) if valid_tag + in_tidylink_label = @in_tidylink_label @in_tidylink_label = true traverse_inline_nodes(label_part) - @in_tidylink_label = false + @in_tidylink_label = in_tidylink_label emit_inline(close_tag) if valid_tag end @@ -544,7 +546,12 @@ def accept_heading(heading) if @pipe @res << to_html(heading.text) else - @res << "#{to_html(heading.text)}" + # The heading text is the label of the heading's own link, so it must + # not contain links itself (nested tags are invalid HTML). + @in_tidylink_label = true + text = to_html(heading.text) + @in_tidylink_label = false + @res << "#{text}" end @res << "\n" diff --git a/test/rdoc/markup/to_html_crossref_test.rb b/test/rdoc/markup/to_html_crossref_test.rb index 70242bb37f..83c0f56457 100644 --- a/test/rdoc/markup/to_html_crossref_test.rb +++ b/test/rdoc/markup/to_html_crossref_test.rb @@ -32,6 +32,21 @@ def test_convert_CROSSREF assert_equal para("# :stopdoc::"), result end + def test_convert_CROSSREF_in_heading + result = @to.convert "= C1 and EXAMPLE.md\n\nC1" + + assert_equal "\n" \ + "\n

C1 and EXAMPLE.md

\n" + + para("C1"), result + end + + def test_convert_CROSSREF_in_heading_after_tidylink + result = @to.convert "= {Foo}[https://example.com] C1" + + assert_include result, 'Foo' + assert_not_include result, 'C1.html' + end + def test_convert_CROSSREF_backslash_in_tt formatter = RDoc::Markup::ToHtmlCrossref.new('C9.html', @c9_b)