From b2788de77afd3889c2e83e9e8089d6da7b153292 Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Sun, 4 Jan 2026 12:01:28 +0000 Subject: [PATCH 1/2] Support GitHub style markdown heading anchor and link reference (#1540) I started the work to address #1537, but I think we should just go with the GH markdown style anchor/link generation in the future. Anchors Generated: | Context | Before | After | |---------|--------|-------| | Heading `== Hello` (standalone) | `id="label-Hello"` | `id="hello"` | | Heading `== Hello` (in class Foo) | `id="class-Foo-label-Hello"` | `id="class-foo-hello"` | | Heading `== Hello` (in method #bar) | `id="method-i-bar-label-Hello"` | `id="method-i-bar-hello"` | | Class/Module anchor | `id="class-Foo::Bar"` | `id="class-foo-bar"` | **Legacy anchors preserved as hidden `` elements. So links targeting old anchors should still work.** Links Generated: | Syntax | Before | After | |--------|--------|-------| | `rdoc-ref:@foo` | `href="#label-foo"` | `href="#foo"` | | `rdoc-ref:Class@foo` | `href="#class-Class-label-foo"` | `href="#class-class-foo"` | | `rdoc-ref:Class#method@foo` | `href="#method-i-method-label-foo"` | `href="#method-i-method-foo"` | | `rdoc-ref:Class@Section Title` | `href="#Section Title"` | `href="#section-title"` | These changes now enable Markdown links to work with GitHub-style anchors. Markdown `[link](#foo)` passes through unchanged as `href="#foo"`. - Before: Anchor was `id="label-Foo"` -> link broken - After: Anchor is `id="foo"` -> link works Supportive Changes: - ToC sidebar links updated to use new anchor format Closes #1537 --- ExampleMarkdown.md | 13 +++- ExampleRDoc.rdoc | 12 +++ doc/rdoc/markup_reference.rb | 24 ++++-- lib/rdoc/code_object/class_module.rb | 20 ++++- lib/rdoc/code_object/context/section.rb | 21 ++++- lib/rdoc/generator/template/aliki/class.rhtml | 2 + .../generator/template/aliki/css/rdoc.css | 20 +++++ .../generator/template/darkfish/class.rhtml | 2 + .../generator/template/darkfish/css/rdoc.css | 19 +++++ lib/rdoc/markup/heading.rb | 77 +++++++++++++++++-- lib/rdoc/markup/to_html.rb | 27 +++++-- lib/rdoc/markup/to_html_crossref.rb | 29 +++++-- lib/rdoc/markup/to_label.rb | 12 ++- lib/rdoc/parser/changelog.rb | 8 ++ lib/rdoc/text.rb | 15 ++++ test/rdoc/code_object/normal_class_test.rb | 4 +- test/rdoc/code_object/normal_module_test.rb | 4 +- test/rdoc/generator/darkfish_test.rb | 10 ++- test/rdoc/generator/markup_test.rb | 3 +- test/rdoc/markup/heading_test.rb | 33 +++++++- test/rdoc/markup/to_html_crossref_test.rb | 35 +++++---- test/rdoc/markup/to_html_test.rb | 33 +++++--- test/rdoc/markup/to_label_test.rb | 18 +++-- test/rdoc/rdoc_context_section_test.rb | 4 +- 24 files changed, 364 insertions(+), 81 deletions(-) diff --git a/ExampleMarkdown.md b/ExampleMarkdown.md index fd4359f355..8622fcab77 100644 --- a/ExampleMarkdown.md +++ b/ExampleMarkdown.md @@ -14,6 +14,18 @@ For the following styles, see ExampleRDoc.rdoc for style examples: These items all use the same styles as RDoc format files. +## Anchor Links + +RDoc supports GitHub-style anchor links. You can link to any heading using its +anchor, which is the heading text converted to lowercase with spaces replaced +by hyphens and special characters removed. + +For example: + +* [Link to Footnotes](#footnotes) +* [Link to Blockquotes](#blockquotes) +* [Link to Anchor Links](#anchor-links) + ## Footnotes Footnotes are rendered at the bottom of the documentation section[^1]. For @@ -36,4 +48,3 @@ Here is how a blockquote looks. > > 75 years? This text is from [Riker Ipsum](http://rikeripsum.com) - diff --git a/ExampleRDoc.rdoc b/ExampleRDoc.rdoc index b5dd68c19c..750fdc4795 100644 --- a/ExampleRDoc.rdoc +++ b/ExampleRDoc.rdoc @@ -3,6 +3,18 @@ This document contains example output to show RDoc styling. This file was created from a RDoc Markup file. +== Anchor Links + +RDoc generates GitHub-style anchors for headings. The anchor is the heading +text converted to lowercase with spaces replaced by hyphens and special +characters removed. + +You can link to headings using Markdown-style syntax: + +- {Link to Headings}[#headings] +- {Link to Paragraphs}[#paragraphs] +- {Link to Verbatim sections}[#verbatim-sections] + == Headings You should not use headings beyond level 3, it is a sign of poor organization diff --git a/doc/rdoc/markup_reference.rb b/doc/rdoc/markup_reference.rb index 1ceda2656d..1729144f4c 100644 --- a/doc/rdoc/markup_reference.rb +++ b/doc/rdoc/markup_reference.rb @@ -957,22 +957,30 @@ # # [Heading] # -# - Link: RDoc::RD@LICENSE links to RDoc::RDoc::RD@LICENSE. +# Headings generate GitHub-style anchors: lowercase, spaces as hyphens, +# special characters removed. For example, == Hello World generates +# anchor hello-world. # -# Note that spaces in the actual heading are represented by + characters -# in the linkable text. +# Link to headings are recommended to use the GitHub-style anchor format: # -# - Link: RDoc::Options@Saved+Options -# links to RDoc::Options@Saved+Options. +# - RDoc::Options@saved-options links to RDoc::Options@saved-options. +# - RDoc::RD@license links to RDoc::RD@license. # -# Punctuation and other special characters must be escaped like CGI.escape. +# To link to headings on the same page, you can also use Markdown-style anchor links: +# +# - {link text}[#hello-world] links to the heading == Hello World. +# - {link text}[#saved-options] links to the heading == Saved Options. +# +# The legacy format with + for spaces is also supported, but not recommended: +# +# - RDoc::Options@Saved+Options links to RDoc::Options@Saved+Options. # # Pro tip: The link to any heading is available in the alphabetical table of contents -# at the top left of the page for the class or module. +# at the right sidebar of the page. # # [Section] # -# See {Directives for Organizing Documentation}[#class-RDoc::MarkupReference-label-Directives+for+Organizing+Documentation]. +# See {Directives for Organizing Documentation}[#class-rdoc-markupreference-directives-for-organizing-documentation]. # # - Link: RDoc::Markup::ToHtml@Visitor links to RDoc::Markup::ToHtml@Visitor. # diff --git a/lib/rdoc/code_object/class_module.rb b/lib/rdoc/code_object/class_module.rb index d7ee36f950..00e406b1a6 100644 --- a/lib/rdoc/code_object/class_module.rb +++ b/lib/rdoc/code_object/class_module.rb @@ -188,10 +188,26 @@ def aref_prefix # :nodoc: end ## - # HTML fragment reference for this module or class. See - # RDoc::NormalClass#aref and RDoc::NormalModule#aref + # HTML fragment reference for this module or class using GitHub-style + # anchor format (lowercase, :: replaced with -). + # + # Examples: + # Foo -> class-foo + # Foo::Bar -> class-foo-bar def aref + "#{aref_prefix}-#{full_name.downcase.gsub('::', '-')}" + end + + ## + # Legacy HTML fragment reference for backward compatibility. + # Returns the old RDoc-style anchor format. + # + # Examples: + # Foo -> class-Foo + # Foo::Bar -> class-Foo::Bar + + def legacy_aref "#{aref_prefix}-#{full_name}" end diff --git a/lib/rdoc/code_object/context/section.rb b/lib/rdoc/code_object/context/section.rb index 16b778174f..7600a16935 100644 --- a/lib/rdoc/code_object/context/section.rb +++ b/lib/rdoc/code_object/context/section.rb @@ -70,11 +70,30 @@ def add_comment(comment) end ## - # Anchor reference for linking to this section + # Anchor reference for linking to this section using GitHub-style format. + # + # Examples: + # "Section" -> "section" + # "One Two" -> "one-two" + # "[untitled]" -> "untitled" def aref title = @title || '[untitled]' + RDoc::Text.to_anchor(title) + end + + ## + # Legacy anchor reference for backward compatibility. + # + # Examples: + # "Section" -> "section" + # "One Two" -> "one+two" + # "[untitled]" -> "5Buntitled-5D" + + def legacy_aref + title = @title || '[untitled]' + CGI.escape(title).gsub('%', '-').sub(/^-/, '') end diff --git a/lib/rdoc/generator/template/aliki/class.rhtml b/lib/rdoc/generator/template/aliki/class.rhtml index 69adfab1ad..5a6fa35b18 100644 --- a/lib/rdoc/generator/template/aliki/class.rhtml +++ b/lib/rdoc/generator/template/aliki/class.rhtml @@ -29,6 +29,7 @@ <% end %> +

<%= klass.type %> <%= klass.full_name %>

@@ -38,6 +39,7 @@ <%- klass.each_section do |section, constants, attributes| %> + <%- klass.each_section do |section, constants, attributes| %> +