From 9c85568eb43b8514e6c9b0eb7cd638002029184c Mon Sep 17 00:00:00 2001 From: anyingiit <49945850+anyingiit@users.noreply.github.com> Date: Fri, 23 Oct 2026 05:08:41 +0800 Subject: [PATCH] Keep link targets out of meta description excerpts The excerpt used for the description meta tags is taken from the raw comment text, so link URLs could end up in it. For example, a Markdown comment "Foo." followed by "[Example Link](https://example.com)" got the excerpt "Foo. example.", the second sentence being the "example." part of the URL. Before taking the excerpt, replace links with their text (Markdown [text](url) for Markdown comments, {text}[url] and text[url] for RDoc markup), drop Markdown images, and remove bare URLs. Fixes #1857 --- lib/rdoc/generator/darkfish.rb | 22 ++++++++++++++ test/rdoc/generator/darkfish_test.rb | 43 ++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/lib/rdoc/generator/darkfish.rb b/lib/rdoc/generator/darkfish.rb index c097bab70c..9256f66deb 100644 --- a/lib/rdoc/generator/darkfish.rb +++ b/lib/rdoc/generator/darkfish.rb @@ -714,6 +714,17 @@ def template_for(file, page = true, klass = ERB) ParagraphExcerptRegexpOther = %r[\b\w[^./:]++\.] # use \p/\P{letter} instead of \w/\W in Unicode ParagraphExcerptRegexpUnicode = %r[\b\p{letter}[^./:]++\.] + + # Link markup to replace with its text (or remove) before taking an excerpt + ExcerptMarkdownLinks = [ + [/(?]*[^\s>.,;:!?)]>?] # :startdoc: # Returns an excerpt of the comment for usage in meta description tags @@ -725,6 +736,8 @@ def excerpt(comment) comment end + text = strip_links(text, comment.respond_to?(:format) ? comment.format : nil) + # Match from a capital letter to the first period, discarding any links, so # that we don't end up matching badges in the README pattern = ParagraphExcerptRegexpUnicode @@ -745,6 +758,15 @@ def excerpt(comment) extracted_text[0...150].tr_s("\n", " ").squeeze(" ") end + # Replaces links in +text+ written in +format+ markup with their text, and + # removes images and bare URLs, so that link targets don't end up in excerpts + def strip_links(text, format) # :nodoc: + links = format == 'markdown' ? ExcerptMarkdownLinks : ExcerptRDocLinks + + text = links.inject(text) { |t, (regexp, replacement)| t.gsub(regexp, replacement) } + text.gsub(ExcerptURL, '') + end + def generate_ancestor_list(ancestors, klass) return '' if ancestors.empty? diff --git a/test/rdoc/generator/darkfish_test.rb b/test/rdoc/generator/darkfish_test.rb index d853101312..60857c547e 100644 --- a/test/rdoc/generator/darkfish_test.rb +++ b/test/rdoc/generator/darkfish_test.rb @@ -581,6 +581,49 @@ def test_meta_tags_for_markdown_files ) end + def test_meta_tags_for_markdown_links + top_level = @store.add_file("file.rb") + klass = top_level.add_class(RDoc::NormalClass, "Linked") + comment = RDoc::Comment.new(<<~MARKDOWN, top_level) + Foo. + + [Example Link](https://example.com) and ![Badge](https://example.com/badge.svg) + MARKDOWN + comment.format = "markdown" + klass.add_comment(comment, top_level) + + @g.generate + + content = File.binread("Linked.html") + assert_include(content, '') + end + + def test_meta_tags_for_rdoc_links + top_level = @store.add_file("file.rb") + klass = top_level.add_class(RDoc::NormalClass, "Linked") + klass.add_comment(<<~RDOC, top_level) + See {the docs}[https://example.com/docs.html] or RDoc[https://ruby.github.io/rdoc/] for more. + RDOC + + @g.generate + + content = File.binread("Linked.html") + assert_include(content, '') + end + + def test_meta_tags_for_bare_urls + top_level = @store.add_file("file.rb") + klass = top_level.add_class(RDoc::NormalClass, "Linked") + klass.add_comment(<<~RDOC, top_level) + Details are at https://example.com/docs. Use it. + RDOC + + @g.generate + + content = File.binread("Linked.html") + assert_include(content, '') + end + def test_meta_tags_for_raw_pages top_level = @store.add_file("MyPage", parser: RDoc::Parser::Simple) comment = RDoc::Comment.new('this is a comment')