rdoc 7.0.4 → 7.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +70 -4
  3. data/doc/markup_reference/markdown.md +558 -0
  4. data/doc/markup_reference/rdoc.rdoc +1169 -0
  5. data/lib/rdoc/code_object/class_module.rb +24 -3
  6. data/lib/rdoc/code_object/context/section.rb +20 -1
  7. data/lib/rdoc/cross_reference.rb +30 -21
  8. data/lib/rdoc/generator/darkfish.rb +1 -1
  9. data/lib/rdoc/generator/json_index.rb +1 -1
  10. data/lib/rdoc/generator/template/aliki/_head.rhtml +1 -1
  11. data/lib/rdoc/generator/template/aliki/_sidebar_extends.rhtml +6 -8
  12. data/lib/rdoc/generator/template/aliki/_sidebar_includes.rhtml +6 -8
  13. data/lib/rdoc/generator/template/aliki/_sidebar_installed.rhtml +1 -1
  14. data/lib/rdoc/generator/template/aliki/_sidebar_pages.rhtml +1 -1
  15. data/lib/rdoc/generator/template/aliki/_sidebar_sections.rhtml +1 -1
  16. data/lib/rdoc/generator/template/aliki/class.rhtml +20 -18
  17. data/lib/rdoc/generator/template/aliki/css/rdoc.css +20 -0
  18. data/lib/rdoc/generator/template/aliki/js/c_highlighter.js +1 -1
  19. data/lib/rdoc/generator/template/aliki/servlet_root.rhtml +1 -1
  20. data/lib/rdoc/generator/template/darkfish/_sidebar_extends.rhtml +6 -8
  21. data/lib/rdoc/generator/template/darkfish/_sidebar_includes.rhtml +6 -8
  22. data/lib/rdoc/generator/template/darkfish/_sidebar_installed.rhtml +1 -1
  23. data/lib/rdoc/generator/template/darkfish/_sidebar_pages.rhtml +1 -1
  24. data/lib/rdoc/generator/template/darkfish/_sidebar_sections.rhtml +1 -1
  25. data/lib/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml +5 -5
  26. data/lib/rdoc/generator/template/darkfish/class.rhtml +19 -17
  27. data/lib/rdoc/generator/template/darkfish/css/rdoc.css +19 -0
  28. data/lib/rdoc/generator/template/darkfish/table_of_contents.rhtml +3 -3
  29. data/lib/rdoc/markdown.kpeg +1 -5
  30. data/lib/rdoc/markdown.rb +1 -5
  31. data/lib/rdoc/markup/attribute_manager.rb +28 -1
  32. data/lib/rdoc/markup/document.rb +2 -2
  33. data/lib/rdoc/markup/formatter.rb +1 -1
  34. data/lib/rdoc/markup/heading.rb +99 -27
  35. data/lib/rdoc/markup/indented_paragraph.rb +1 -1
  36. data/lib/rdoc/markup/list.rb +2 -2
  37. data/lib/rdoc/markup/list_item.rb +2 -2
  38. data/lib/rdoc/markup/to_html.rb +31 -11
  39. data/lib/rdoc/markup/to_html_crossref.rb +24 -5
  40. data/lib/rdoc/markup/to_label.rb +11 -1
  41. data/lib/rdoc/markup/verbatim.rb +2 -2
  42. data/lib/rdoc/markup.rb +2 -2
  43. data/lib/rdoc/parser/c.rb +1 -1
  44. data/lib/rdoc/parser/changelog.rb +8 -0
  45. data/lib/rdoc/ri/paths.rb +1 -1
  46. data/lib/rdoc/servlet.rb +1 -1
  47. data/lib/rdoc/text.rb +15 -0
  48. data/lib/rdoc/token_stream.rb +4 -8
  49. data/lib/rdoc/version.rb +1 -1
  50. data/rdoc.gemspec +2 -2
  51. metadata +5 -7
  52. data/ExampleMarkdown.md +0 -39
  53. data/ExampleRDoc.rdoc +0 -210
@@ -26,13 +26,14 @@
26
26
  <% if namespace[:self] %>
27
27
  <span><%= namespace[:name] %></span>
28
28
  <% else %>
29
- <a href="<%= namespace[:path] %>"><%= namespace[:name] %></a><span class="separator">::</span>
29
+ <a href="<%= namespace[:path] %>"><%= namespace[:name] %></a><span>::</span>
30
30
  <% end %>
31
31
  </li>
32
32
  <% end %>
33
33
  </ol>
34
34
  <% end %>
35
35
 
36
+ <span id="<%= h klass.legacy_aref %>" class="legacy-anchor"></span>
36
37
  <h1 id="<%= h klass.aref %>" class="anchor-link <%= klass.type %>">
37
38
  <%= klass.type %> <%= klass.full_name %>
38
39
  </h1>
@@ -42,8 +43,9 @@
42
43
  </section>
43
44
 
44
45
  <%- klass.each_section do |section, constants, attributes| %>
46
+ <span id="<%= section.legacy_aref %>" class="legacy-anchor"></span>
45
47
  <section id="<%= section.aref %>" class="documentation-section anchor-link">
46
- <%- if section.title %>
48
+ <%- if section.title then %>
47
49
  <header class="documentation-section-title">
48
50
  <h2>
49
51
  <%= section.title %>
@@ -54,13 +56,13 @@
54
56
  </header>
55
57
  <%- end %>
56
58
 
57
- <%- if section.comment %>
59
+ <%- if section.comment then %>
58
60
  <div>
59
61
  <%= section.description %>
60
62
  </div>
61
63
  <%- end %>
62
64
 
63
- <%- unless constants.empty? %>
65
+ <%- unless constants.empty? then %>
64
66
  <section class="constants-list">
65
67
  <header>
66
68
  <h3>Constants</h3>
@@ -68,9 +70,9 @@
68
70
  <dl>
69
71
  <%- constants.each do |const| %>
70
72
  <dt id="<%= const.name %>"><%= const.name %></dt>
71
- <%- if const.comment %>
73
+ <%- if const.comment then %>
72
74
  <dd>
73
- <%- if const.mixin_from %>
75
+ <%- if const.mixin_from then %>
74
76
  <div class="mixin-from">
75
77
  Included from <a href="<%= klass.aref_to(const.mixin_from.path) %>"><%= const.mixin_from.full_name %></a>
76
78
  </div>
@@ -85,7 +87,7 @@
85
87
  </section>
86
88
  <%- end %>
87
89
 
88
- <%- unless attributes.empty? %>
90
+ <%- unless attributes.empty? then %>
89
91
  <section class="attribute-method-details method-section">
90
92
  <header>
91
93
  <h3>Attributes</h3>
@@ -101,12 +103,12 @@
101
103
  </div>
102
104
 
103
105
  <div class="method-description">
104
- <%- if attrib.mixin_from %>
106
+ <%- if attrib.mixin_from then %>
105
107
  <div class="mixin-from">
106
108
  <%= attrib.singleton ? "Extended" : "Included" %> from <a href="<%= klass.aref_to(attrib.mixin_from.path) %>"><%= attrib.mixin_from.full_name %></a>
107
109
  </div>
108
110
  <%- end %>
109
- <%- if attrib.comment %>
111
+ <%- if attrib.comment then %>
110
112
  <%= attrib.description.strip %>
111
113
  <%- else %>
112
114
  <p class="missing-docs">(Not documented)</p>
@@ -129,7 +131,7 @@
129
131
  <%- methods.each do |method| %>
130
132
  <div id="<%= method.aref %>" class="method-detail anchor-link <%= method.is_alias_for ? "method-alias" : '' %>">
131
133
  <div class="method-header">
132
- <%- if (call_seq = method.call_seq) %>
134
+ <%- if (call_seq = method.call_seq) then %>
133
135
  <%- call_seq.strip.split("\n").each_with_index do |call_seq, i| %>
134
136
  <div class="method-heading">
135
137
  <a href="#<%= method.aref %>" title="Link to this method">
@@ -141,7 +143,7 @@
141
143
  </a>
142
144
  </div>
143
145
  <%- end %>
144
- <%- elsif method.has_call_seq? %>
146
+ <%- elsif method.has_call_seq? then %>
145
147
  <div class="method-heading">
146
148
  <a href="#<%= method.aref %>" title="Link to this method">
147
149
  <span class="method-name"><%= h method.name %></span>
@@ -168,19 +170,19 @@
168
170
  </div>
169
171
  <%- end %>
170
172
 
171
- <%- unless method.skip_description? %>
173
+ <%- unless method.skip_description? then %>
172
174
  <div class="method-description">
173
- <%- if method.mixin_from %>
175
+ <%- if method.mixin_from then %>
174
176
  <div class="mixin-from">
175
177
  <%= method.singleton ? "Extended" : "Included" %> from <a href="<%= klass.aref_to(method.mixin_from.path) %>"><%= method.mixin_from.full_name %></a>
176
178
  </div>
177
179
  <%- end %>
178
- <%- if method.comment %>
180
+ <%- if method.comment then %>
179
181
  <%= method.description.strip %>
180
182
  <%- else %>
181
183
  <p class="missing-docs">(Not documented)</p>
182
184
  <%- end %>
183
- <%- if method.calls_super %>
185
+ <%- if method.calls_super then %>
184
186
  <div class="method-calls-super">
185
187
  Calls superclass method
186
188
  <%=
@@ -192,7 +194,7 @@
192
194
  </div>
193
195
  <%- end %>
194
196
 
195
- <%- unless method.aliases.empty? %>
197
+ <%- unless method.aliases.empty? then %>
196
198
  <div class="aliases">
197
199
  Also aliased as: <%= method.aliases.map do |aka|
198
200
  if aka.parent then # HACK lib/rexml/encodings
@@ -204,7 +206,7 @@
204
206
  </div>
205
207
  <%- end %>
206
208
 
207
- <%- if method.is_alias_for %>
209
+ <%- if method.is_alias_for then %>
208
210
  <div class="aliases">
209
211
  Alias for: <a href="<%= klass.aref_to method.is_alias_for.path %>"><%= h method.is_alias_for.name %></a>
210
212
  </div>
@@ -92,6 +92,25 @@ main .anchor-link:target {
92
92
  scroll-margin-top: 1rem;
93
93
  }
94
94
 
95
+ /* Legacy anchor for backward compatibility with old label- prefix links */
96
+ .legacy-anchor {
97
+ display: block;
98
+ position: relative;
99
+ visibility: hidden;
100
+ scroll-margin-top: 1rem;
101
+ }
102
+
103
+ /* When a legacy anchor is targeted, highlight the next heading sibling */
104
+ .legacy-anchor:target + h1,
105
+ .legacy-anchor:target + h2,
106
+ .legacy-anchor:target + h3,
107
+ .legacy-anchor:target + h4,
108
+ .legacy-anchor:target + h5,
109
+ .legacy-anchor:target + h6 {
110
+ margin-left: -10px;
111
+ border-left: 10px solid var(--border-color);
112
+ }
113
+
95
114
  /* 4. Links */
96
115
  a {
97
116
  color: var(--link-color);
@@ -14,7 +14,7 @@
14
14
  <h1 class="class"><%= h @title %></h1>
15
15
 
16
16
  <%- simple_files = @files.select { |f| f.text? } %>
17
- <%- unless simple_files.empty? %>
17
+ <%- unless simple_files.empty? then %>
18
18
  <h2 id="pages">Pages</h2>
19
19
  <ul>
20
20
  <%- simple_files.sort.each do |file| %>
@@ -23,7 +23,7 @@
23
23
  <%
24
24
  # HACK table_of_contents should not exist on Document
25
25
  table = file.parse(file.comment).table_of_contents
26
- unless table.empty? %>
26
+ unless table.empty? then %>
27
27
  <ul>
28
28
  <%- table.each do |heading| %>
29
29
  <li><a href="<%= h file.path %>#<%= heading.aref %>"><%= heading.plain_html %></a></li>
@@ -44,7 +44,7 @@
44
44
  table.concat klass.parse(klass.comment_location).table_of_contents
45
45
  table.concat klass.section_contents
46
46
 
47
- unless table.empty? %>
47
+ unless table.empty? then %>
48
48
  <ul>
49
49
  <%- table.each do |item| %>
50
50
  <%- label = item.respond_to?(:label) ? item.label(klass) : item.aref %>
@@ -490,11 +490,7 @@
490
490
  # Wraps `text` in strike markup for rdoc inline formatting
491
491
 
492
492
  def strike text
493
- if text =~ /\A[a-z\d.\/-]+\z/i then
494
- "~#{text}~"
495
- else
496
- "<s>#{text}</s>"
497
- end
493
+ "<del>#{text}</del>"
498
494
  end
499
495
 
500
496
  ##
data/lib/rdoc/markdown.rb CHANGED
@@ -875,11 +875,7 @@ class RDoc::Markdown
875
875
  # Wraps `text` in strike markup for rdoc inline formatting
876
876
 
877
877
  def strike text
878
- if text =~ /\A[a-z\d.\/-]+\z/i then
879
- "~#{text}~"
880
- else
881
- "<s>#{text}</s>"
882
- end
878
+ "<del>#{text}</del>"
883
879
  end
884
880
 
885
881
  ##
@@ -89,12 +89,21 @@ class RDoc::Markup::AttributeManager
89
89
  add_word_pair "*", "*", :BOLD, true
90
90
  add_word_pair "_", "_", :EM, true
91
91
  add_word_pair "+", "+", :TT, true
92
+ add_word_pair "`", "`", :TT, true
92
93
 
93
94
  add_html "em", :EM, true
94
95
  add_html "i", :EM, true
95
96
  add_html "b", :BOLD, true
96
97
  add_html "tt", :TT, true
97
98
  add_html "code", :TT, true
99
+ add_html "s", :STRIKE, true
100
+ add_html "del", :STRIKE, true
101
+
102
+ @word_pair_chars = @matching_word_pairs.keys.join
103
+
104
+ # Matches a word pair delimiter (*, _, +, `) that is NOT already protected.
105
+ # Used by #protect_code_markup to escape delimiters inside <code>/<tt> tags.
106
+ @unprotected_word_pair_regexp = /([#{@word_pair_chars}])(?!#{PROTECT_ATTR})/
98
107
  end
99
108
 
100
109
  ##
@@ -164,7 +173,7 @@ class RDoc::Markup::AttributeManager
164
173
  }.keys
165
174
  return if tags.empty?
166
175
  tags = "[#{tags.join("")}](?!#{PROTECT_ATTR})"
167
- all_tags = "[#{@matching_word_pairs.keys.join("")}](?!#{PROTECT_ATTR})"
176
+ all_tags = "[#{@word_pair_chars}](?!#{PROTECT_ATTR})"
168
177
 
169
178
  re = /(?:^|\W|#{all_tags})\K(#{tags})(\1*[#\\]?[\w:#{PROTECT_ATTR}.\/\[\]-]+?\S?)\1(?!\1)(?=#{all_tags}|\W|$)/
170
179
 
@@ -245,6 +254,23 @@ class RDoc::Markup::AttributeManager
245
254
  @str.gsub!(/\\(\\[#{Regexp.escape @protectable.join}])/m, "\\1")
246
255
  end
247
256
 
257
+ ##
258
+ # Protects word pair delimiters (*, _, +) inside
259
+ # <code> and <tt> tags from being processed as inline formatting.
260
+ # For example, *bold* in +*bold*+ will NOT be rendered as bold.
261
+
262
+ def protect_code_markup
263
+ @str.gsub!(/<(code|tt)>(.*?)<\/\1>/im) do
264
+ tag = $1
265
+ content = $2
266
+ # Protect word pair delimiters (*, _, +) from being processed
267
+ escaped = content.gsub(@unprotected_word_pair_regexp, "\\1#{PROTECT_ATTR}")
268
+ # Protect HTML-like tags from being processed (e.g., <del> inside code)
269
+ escaped = escaped.gsub(/<(?!#{PROTECT_ATTR})/, "<#{PROTECT_ATTR}")
270
+ "<#{tag}>#{escaped}</#{tag}>"
271
+ end
272
+ end
273
+
248
274
  ##
249
275
  # Unescapes regexp handling sequences of text
250
276
 
@@ -308,6 +334,7 @@ class RDoc::Markup::AttributeManager
308
334
  @str = str.dup
309
335
 
310
336
  mask_protected_sequences
337
+ protect_code_markup
311
338
 
312
339
  @attrs = RDoc::Markup::AttrSpan.new @str.length, @exclusive_bitmap
313
340
 
@@ -26,7 +26,7 @@ class RDoc::Markup::Document
26
26
  ##
27
27
  # Creates a new Document with +parts+
28
28
 
29
- def initialize(*parts)
29
+ def initialize *parts
30
30
  @parts = []
31
31
  @parts.concat parts
32
32
 
@@ -148,7 +148,7 @@ class RDoc::Markup::Document
148
148
  ##
149
149
  # Appends +parts+ to the document
150
150
 
151
- def push(*parts)
151
+ def push *parts
152
152
  self.parts.concat parts
153
153
  end
154
154
 
@@ -185,7 +185,7 @@ class RDoc::Markup::Formatter
185
185
  #
186
186
  # alias accept_raw ignore
187
187
 
188
- def ignore(*node)
188
+ def ignore *node
189
189
  end
190
190
 
191
191
  ##
@@ -2,6 +2,33 @@
2
2
 
3
3
  module RDoc
4
4
  class Markup
5
+ # IMPORTANT! This weird workaround is required to ensure that RDoc can correctly deserializing Marshal data from
6
+ # older rubies. Older rubies have `Heading` as a struct, so if we change it to a class, deserialization fails
7
+ if RUBY_VERSION.start_with?("4.")
8
+ class Heading < Element
9
+ #: String
10
+ attr_reader :text
11
+
12
+ #: Integer
13
+ attr_accessor :level
14
+
15
+ #: (Integer, String) -> void
16
+ def initialize(level, text)
17
+ super()
18
+
19
+ @level = level
20
+ @text = text
21
+ end
22
+
23
+ #: (Object) -> bool
24
+ def ==(other)
25
+ other.is_a?(Heading) && other.level == @level && other.text == @text
26
+ end
27
+ end
28
+ else
29
+ Heading = Struct.new(:level, :text)
30
+ end
31
+
5
32
  # A heading with a level (1-6) and text
6
33
  #
7
34
  # RDoc syntax:
@@ -13,13 +40,8 @@ module RDoc
13
40
  # # Heading 1
14
41
  # ## Heading 2
15
42
  # ### Heading 3
16
- class Heading < Element
17
- #: String
18
- attr_reader :text
19
-
20
- #: Integer
21
- attr_accessor :level
22
-
43
+ #
44
+ class Heading
23
45
  # A singleton RDoc::Markup::ToLabel formatter for headings.
24
46
  #: () -> RDoc::Markup::ToLabel
25
47
  def self.to_label
@@ -43,38 +65,88 @@ module RDoc
43
65
  end
44
66
  end
45
67
 
46
- #: (Integer, String) -> void
47
- def initialize(level, text)
48
- super()
49
-
50
- @level = level
51
- @text = text
52
- end
53
-
54
- #: (Object) -> bool
55
- def ==(other)
56
- other.is_a?(Heading) && other.level == @level && other.text == @text
57
- end
58
-
59
68
  # @override
60
69
  #: (untyped) -> void
61
70
  def accept(visitor)
62
71
  visitor.accept_heading(self)
63
72
  end
64
73
 
65
- # An HTML-safe anchor reference for this header.
74
+ # An HTML-safe anchor reference for this header using GitHub-style formatting:
75
+ # - Lowercase
76
+ # - Spaces converted to hyphens
77
+ # - Special characters removed (except hyphens)
78
+ #
79
+ # Examples:
80
+ # "Hello" -> "hello"
81
+ # "Hello World" -> "hello-world"
82
+ # "Foo Bar Baz" -> "foo-bar-baz"
83
+ #
66
84
  #: () -> String
67
85
  def aref
68
- "label-#{self.class.to_label.convert text.dup}"
86
+ self.class.to_label.convert text.dup
87
+ end
88
+
89
+ # An HTML-safe anchor reference using legacy RDoc formatting:
90
+ # - Prefixed with "label-"
91
+ # - Original case preserved
92
+ # - Spaces converted to + (URL encoding style)
93
+ # - Special characters percent-encoded
94
+ #
95
+ # Returns nil if it would be the same as the GitHub-style aref (no alias needed).
96
+ #
97
+ # Examples:
98
+ # "hello" -> "label-hello" (different due to label- prefix)
99
+ # "Hello" -> "label-Hello"
100
+ # "Hello World" -> "label-Hello+World"
101
+ # "Foo Bar Baz" -> "label-Foo+Bar+Baz"
102
+ #
103
+ #: () -> String?
104
+ def legacy_aref
105
+ "label-#{self.class.to_label.convert_legacy text.dup}"
69
106
  end
70
107
 
71
- # Creates a fully-qualified label which will include the label from +context+. This helps keep ids unique in HTML.
108
+ # Creates a fully-qualified label (GitHub-style) which includes the context's aref prefix.
109
+ # This helps keep IDs unique in HTML when headings appear within class/method documentation.
110
+ #
111
+ # Examples (without context):
112
+ # "Hello World" -> "hello-world"
113
+ #
114
+ # Examples (with context being class Foo):
115
+ # "Hello World" -> "class-foo-hello-world"
116
+ #
117
+ # Examples (with context being method #bar):
118
+ # "Hello World" -> "method-i-bar-hello-world"
119
+ #
72
120
  #: (RDoc::Context?) -> String
73
121
  def label(context = nil)
74
- label = +""
75
- label << "#{context.aref}-" if context&.respond_to?(:aref)
76
- label << aref
77
- label
122
+ result = +""
123
+ result << "#{context.aref}-" if context&.respond_to?(:aref)
124
+ result << aref
125
+ result
126
+ end
127
+
128
+ # Creates a fully-qualified legacy label for backward compatibility.
129
+ # This is used to generate a secondary ID attribute on the heading's inner anchor,
130
+ # allowing old-style links (e.g., #label-Hello+World) to continue working.
131
+ #
132
+ # Examples (without context):
133
+ # "hello" -> "label-hello"
134
+ # "Hello World" -> "label-Hello+World"
135
+ #
136
+ # Examples (with context being class Foo):
137
+ # "hello" -> "class-Foo-label-hello"
138
+ # "Hello World" -> "class-Foo-label-Hello+World"
139
+ #
140
+ #: (RDoc::Context?) -> String
141
+ def legacy_label(context = nil)
142
+ result = +""
143
+ if context&.respond_to?(:legacy_aref)
144
+ result << "#{context.legacy_aref}-"
145
+ elsif context&.respond_to?(:aref)
146
+ result << "#{context.aref}-"
147
+ end
148
+ result << legacy_aref
149
+ result
78
150
  end
79
151
 
80
152
  # HTML markup of the text of this label without the surrounding header element.
@@ -13,7 +13,7 @@ class RDoc::Markup::IndentedParagraph < RDoc::Markup::Raw
13
13
  # Creates a new IndentedParagraph containing +parts+ indented with +indent+
14
14
  # spaces
15
15
 
16
- def initialize(indent, *parts)
16
+ def initialize indent, *parts
17
17
  @indent = indent
18
18
 
19
19
  super(*parts)
@@ -37,7 +37,7 @@ class RDoc::Markup::List
37
37
  # Creates a new list of +type+ with +items+. Valid list types are:
38
38
  # +:BULLET+, +:LABEL+, +:LALPHA+, +:NOTE+, +:NUMBER+, +:UALPHA+
39
39
 
40
- def initialize(type = nil, *items)
40
+ def initialize type = nil, *items
41
41
  @type = type
42
42
  @items = []
43
43
  @items.concat items
@@ -94,7 +94,7 @@ class RDoc::Markup::List
94
94
  ##
95
95
  # Appends +items+ to the list
96
96
 
97
- def push(*items)
97
+ def push *items
98
98
  @items.concat items
99
99
  end
100
100
 
@@ -24,7 +24,7 @@ class RDoc::Markup::ListItem
24
24
  ##
25
25
  # Creates a new ListItem with an optional +label+ containing +parts+
26
26
 
27
- def initialize(label = nil, *parts)
27
+ def initialize label = nil, *parts
28
28
  @label = label
29
29
  @parts = []
30
30
  @parts.concat parts
@@ -92,7 +92,7 @@ class RDoc::Markup::ListItem
92
92
  ##
93
93
  # Adds +parts+ to the ListItem
94
94
 
95
- def push(*parts)
95
+ def push *parts
96
96
  @parts.concat parts
97
97
  end
98
98
 
@@ -221,10 +221,15 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
221
221
 
222
222
  def accept_verbatim(verbatim)
223
223
  text = verbatim.text.rstrip
224
+ format = verbatim.format
224
225
 
225
226
  klass = nil
226
227
 
227
- content = if verbatim.ruby? or parseable? text then
228
+ # Apply Ruby syntax highlighting if
229
+ # - explicitly marked as Ruby (via ruby? which accepts :ruby or :rb)
230
+ # - no format specified but the text is parseable as Ruby
231
+ # Otherwise, add language class when applicable and skip Ruby highlighting
232
+ content = if verbatim.ruby? || (format.nil? && parseable?(text))
228
233
  begin
229
234
  tokens = RDoc::Parser::RipperStateLex.parse text
230
235
  klass = ' class="ruby"'
@@ -236,6 +241,7 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
236
241
  CGI.escapeHTML text
237
242
  end
238
243
  else
244
+ klass = " class=\"#{format}\"" if format
239
245
  CGI.escapeHTML text
240
246
  end
241
247
 
@@ -306,6 +312,13 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
306
312
  level = [6, heading.level].min
307
313
 
308
314
  label = heading.label @code_object
315
+ legacy_label = heading.legacy_label @code_object
316
+
317
+ # Add legacy anchor before the heading for backward compatibility.
318
+ # This allows old links with label- prefix to still work.
319
+ if @options.output_decoration && !@options.pipe
320
+ @res << "\n<span id=\"#{legacy_label}\" class=\"legacy-anchor\"></span>"
321
+ end
309
322
 
310
323
  @res << if @options.output_decoration
311
324
  "\n<h#{level} id=\"#{label}\">"
@@ -362,14 +375,18 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
362
375
  end
363
376
 
364
377
  ##
365
- # Generate a link to +url+ with content +text+. Handles the special cases
366
- # for img: and link: described under handle_regexp_HYPERLINK
378
+ # Generates an HTML link or image tag for the given +url+ and +text+.
379
+ #
380
+ # - Image URLs (http/https/link ending in .gif, .png, .jpg, .jpeg, .bmp)
381
+ # become <img> tags
382
+ # - File references (.rb, .rdoc, .md) are converted to .html paths
383
+ # - Anchor URLs (#foo) pass through unchanged for GitHub-style header linking
384
+ # - Footnote links get wrapped in <sup> tags
367
385
 
368
386
  def gen_url(url, text)
369
387
  scheme, url, id = parse_url url
370
388
 
371
- if %w[http https link].include?(scheme) and
372
- url =~ /\.(gif|png|jpg|jpeg|bmp)$/ then
389
+ if %w[http https link].include?(scheme) && url =~ /\.(gif|png|jpg|jpeg|bmp)\z/
373
390
  "<img src=\"#{url}\" />"
374
391
  else
375
392
  if scheme != 'link' and %r%\A((?!https?:)(?:[^/#]*/)*+)([^/#]+)\.(rb|rdoc|md)(?=\z|#)%i =~ url
@@ -381,9 +398,11 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
381
398
 
382
399
  link = "<a#{id} href=\"#{url}\">#{text}</a>"
383
400
 
384
- link = "<sup>#{link}</sup>" if /"foot/ =~ id
385
-
386
- link
401
+ if /"foot/.match?(id)
402
+ "<sup>#{link}</sup>"
403
+ else
404
+ link
405
+ end
387
406
  end
388
407
  end
389
408
 
@@ -400,9 +419,10 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
400
419
  # Maps attributes to HTML tags
401
420
 
402
421
  def init_tags
403
- add_tag :BOLD, "<strong>", "</strong>"
404
- add_tag :TT, "<code>", "</code>"
405
- add_tag :EM, "<em>", "</em>"
422
+ add_tag :BOLD, "<strong>", "</strong>"
423
+ add_tag :TT, "<code>", "</code>"
424
+ add_tag :EM, "<em>", "</em>"
425
+ add_tag :STRIKE, "<del>", "</del>"
406
426
  end
407
427
 
408
428
  ##
@@ -169,14 +169,33 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
169
169
  end
170
170
 
171
171
  if label
172
+ # Convert label to GitHub-style anchor format
173
+ # First convert + to space (URL encoding), then apply GitHub-style rules
174
+ formatted_label = RDoc::Text.to_anchor(label.tr('+', ' '))
175
+
176
+ # Case 1: Path already has an anchor (e.g., method link)
177
+ # Input: C1#method@label -> path="C1.html#method-i-m"
178
+ # Output: C1.html#method-i-m-label
172
179
  if path =~ /#/
173
- path << "-label-#{label}"
174
- elsif ref&.sections&.any? { |section| label == section.title }
175
- path << "##{label}"
180
+ path << "-#{formatted_label}"
181
+
182
+ # Case 2: Label matches a section title
183
+ # Input: C1@Section -> path="C1.html", section "Section" exists
184
+ # Output: C1.html#section (uses section.aref for GitHub-style)
185
+ elsif (section = ref&.sections&.find { |s| label.tr('+', ' ') == s.title })
186
+ path << "##{section.aref}"
187
+
188
+ # Case 3: Ref has an aref (class/module context)
189
+ # Input: C1@heading -> path="C1.html", ref=C1 class
190
+ # Output: C1.html#class-c1-heading
176
191
  elsif ref.respond_to?(:aref)
177
- path << "##{ref.aref}-label-#{label}"
192
+ path << "##{ref.aref}-#{formatted_label}"
193
+
194
+ # Case 4: No context, just the label (e.g., TopLevel/file)
195
+ # Input: README@section -> path="README_md.html"
196
+ # Output: README_md.html#section
178
197
  else
179
- path << "#label-#{label}"
198
+ path << "##{formatted_label}"
180
199
  end
181
200
  end
182
201
 
@@ -28,11 +28,21 @@ class RDoc::Markup::ToLabel < RDoc::Markup::Formatter
28
28
  end
29
29
 
30
30
  ##
31
- # Converts +text+ to an HTML-safe label
31
+ # Converts +text+ to an HTML-safe label using GitHub-style anchor formatting.
32
32
 
33
33
  def convert(text)
34
34
  label = convert_flow @am.flow text
35
35
 
36
+ RDoc::Text.to_anchor(label)
37
+ end
38
+
39
+ ##
40
+ # Converts +text+ to an HTML-safe label using legacy RDoc formatting.
41
+ # Used for generating backward-compatible anchor aliases.
42
+
43
+ def convert_legacy(text)
44
+ label = convert_flow @am.flow text
45
+
36
46
  CGI.escape(label).gsub('%', '-').sub(/^-/, '')
37
47
  end
38
48