rdoc 7.2.0 → 8.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 (150) hide show
  1. checksums.yaml +4 -4
  2. data/CONTRIBUTING.md +4 -7
  3. data/LICENSE.rdoc +4 -0
  4. data/README.md +43 -2
  5. data/RI.md +75 -75
  6. data/doc/markup_reference/markdown.md +104 -3
  7. data/exe/rdoc +2 -2
  8. data/lib/rdoc/code_object/alias.rb +70 -74
  9. data/lib/rdoc/code_object/any_method.rb +305 -298
  10. data/lib/rdoc/code_object/attr.rb +150 -143
  11. data/lib/rdoc/code_object/class_module.rb +801 -765
  12. data/lib/rdoc/code_object/constant.rb +178 -150
  13. data/lib/rdoc/code_object/context/section.rb +133 -160
  14. data/lib/rdoc/code_object/context.rb +925 -952
  15. data/lib/rdoc/code_object/extend.rb +7 -5
  16. data/lib/rdoc/code_object/include.rb +7 -5
  17. data/lib/rdoc/code_object/method_attr.rb +325 -324
  18. data/lib/rdoc/code_object/mixin.rb +97 -95
  19. data/lib/rdoc/code_object/normal_class.rb +77 -78
  20. data/lib/rdoc/code_object/normal_module.rb +61 -59
  21. data/lib/rdoc/code_object/require.rb +23 -39
  22. data/lib/rdoc/code_object/single_class.rb +21 -19
  23. data/lib/rdoc/code_object/top_level.rb +212 -213
  24. data/lib/rdoc/code_object.rb +305 -305
  25. data/lib/rdoc/comment.rb +274 -337
  26. data/lib/rdoc/cross_reference.rb +194 -212
  27. data/lib/rdoc/encoding.rb +105 -103
  28. data/lib/rdoc/erb_partial.rb +13 -11
  29. data/lib/rdoc/erbio.rb +29 -27
  30. data/lib/rdoc/generator/aliki.rb +165 -140
  31. data/lib/rdoc/generator/darkfish.rb +647 -631
  32. data/lib/rdoc/generator/json_index.rb +233 -229
  33. data/lib/rdoc/generator/markup.rb +165 -122
  34. data/lib/rdoc/generator/pot/message_extractor.rb +57 -51
  35. data/lib/rdoc/generator/pot/po.rb +52 -51
  36. data/lib/rdoc/generator/pot/po_entry.rb +138 -132
  37. data/lib/rdoc/generator/pot.rb +85 -81
  38. data/lib/rdoc/generator/ri.rb +23 -19
  39. data/lib/rdoc/generator/template/aliki/DESIGN.md +538 -0
  40. data/lib/rdoc/generator/template/aliki/_aside_toc.rhtml +1 -1
  41. data/lib/rdoc/generator/template/aliki/_footer.rhtml +1 -1
  42. data/lib/rdoc/generator/template/aliki/_head.rhtml +11 -11
  43. data/lib/rdoc/generator/template/aliki/_header.rhtml +29 -44
  44. data/lib/rdoc/generator/template/aliki/_sidebar_extends.rhtml +8 -6
  45. data/lib/rdoc/generator/template/aliki/_sidebar_includes.rhtml +8 -6
  46. data/lib/rdoc/generator/template/aliki/_sidebar_installed.rhtml +1 -1
  47. data/lib/rdoc/generator/template/aliki/_sidebar_pages.rhtml +2 -2
  48. data/lib/rdoc/generator/template/aliki/_sidebar_search.rhtml +4 -4
  49. data/lib/rdoc/generator/template/aliki/_sidebar_sections.rhtml +1 -1
  50. data/lib/rdoc/generator/template/aliki/_sidebar_toggle.rhtml +1 -1
  51. data/lib/rdoc/generator/template/aliki/class.rhtml +56 -46
  52. data/lib/rdoc/generator/template/aliki/css/rdoc.css +538 -283
  53. data/lib/rdoc/generator/template/aliki/index.rhtml +1 -1
  54. data/lib/rdoc/generator/template/aliki/js/aliki.js +80 -102
  55. data/lib/rdoc/generator/template/aliki/page.rhtml +1 -1
  56. data/lib/rdoc/generator/template/aliki/servlet_not_found.rhtml +1 -1
  57. data/lib/rdoc/generator/template/aliki/servlet_root.rhtml +2 -2
  58. data/lib/rdoc/generator/template/darkfish/_footer.rhtml +1 -1
  59. data/lib/rdoc/generator/template/darkfish/_sidebar_extends.rhtml +8 -6
  60. data/lib/rdoc/generator/template/darkfish/_sidebar_includes.rhtml +8 -6
  61. data/lib/rdoc/generator/template/darkfish/_sidebar_installed.rhtml +1 -1
  62. data/lib/rdoc/generator/template/darkfish/_sidebar_pages.rhtml +1 -1
  63. data/lib/rdoc/generator/template/darkfish/_sidebar_sections.rhtml +1 -1
  64. data/lib/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml +5 -5
  65. data/lib/rdoc/generator/template/darkfish/class.rhtml +18 -21
  66. data/lib/rdoc/generator/template/darkfish/css/rdoc.css +0 -1
  67. data/lib/rdoc/generator/template/darkfish/table_of_contents.rhtml +3 -3
  68. data/lib/rdoc/generator.rb +48 -46
  69. data/lib/rdoc/i18n/locale.rb +99 -95
  70. data/lib/rdoc/i18n/text.rb +109 -105
  71. data/lib/rdoc/i18n.rb +7 -5
  72. data/lib/rdoc/markdown/byte_runtime.rb +80 -0
  73. data/lib/rdoc/markdown.kpeg +30 -21
  74. data/lib/rdoc/markdown.rb +329 -151
  75. data/lib/rdoc/markup/block_quote.rb +12 -8
  76. data/lib/rdoc/markup/document.rb +127 -123
  77. data/lib/rdoc/markup/formatter.rb +215 -221
  78. data/lib/rdoc/markup/heading.rb +1 -4
  79. data/lib/rdoc/markup/include.rb +33 -29
  80. data/lib/rdoc/markup/indented_paragraph.rb +37 -33
  81. data/lib/rdoc/markup/inline_parser.rb +281 -277
  82. data/lib/rdoc/markup/list.rb +80 -88
  83. data/lib/rdoc/markup/list_item.rb +73 -85
  84. data/lib/rdoc/markup/paragraph.rb +23 -19
  85. data/lib/rdoc/markup/parser.rb +501 -497
  86. data/lib/rdoc/markup/pre_process.rb +284 -305
  87. data/lib/rdoc/markup/raw.rb +2 -2
  88. data/lib/rdoc/markup/rule.rb +16 -12
  89. data/lib/rdoc/markup/to_ansi.rb +143 -139
  90. data/lib/rdoc/markup/to_bs.rb +72 -68
  91. data/lib/rdoc/markup/to_html.rb +600 -493
  92. data/lib/rdoc/markup/to_html_crossref.rb +221 -191
  93. data/lib/rdoc/markup/to_html_snippet.rb +232 -227
  94. data/lib/rdoc/markup/to_joined_paragraph.rb +40 -41
  95. data/lib/rdoc/markup/to_label.rb +63 -59
  96. data/lib/rdoc/markup/to_markdown.rb +212 -208
  97. data/lib/rdoc/markup/to_rdoc.rb +336 -332
  98. data/lib/rdoc/markup/to_table_of_contents.rb +66 -62
  99. data/lib/rdoc/markup/to_test.rb +60 -56
  100. data/lib/rdoc/markup/to_tt_only.rb +83 -86
  101. data/lib/rdoc/markup/verbatim.rb +62 -58
  102. data/lib/rdoc/markup.rb +198 -196
  103. data/lib/rdoc/options.rb +1063 -1076
  104. data/lib/rdoc/parser/c.rb +1039 -1036
  105. data/lib/rdoc/parser/changelog.rb +319 -315
  106. data/lib/rdoc/parser/markdown.rb +17 -13
  107. data/lib/rdoc/parser/rbs.rb +279 -0
  108. data/lib/rdoc/parser/rd.rb +17 -13
  109. data/lib/rdoc/parser/ruby.rb +1231 -2222
  110. data/lib/rdoc/parser/ruby_colorizer.rb +303 -0
  111. data/lib/rdoc/parser/simple.rb +31 -27
  112. data/lib/rdoc/parser/text.rb +12 -8
  113. data/lib/rdoc/parser.rb +230 -221
  114. data/lib/rdoc/rbs_helper.rb +186 -0
  115. data/lib/rdoc/rd/inline.rb +57 -53
  116. data/lib/rdoc/rd.rb +90 -88
  117. data/lib/rdoc/rdoc.rb +547 -366
  118. data/lib/rdoc/ri/driver.rb +1141 -1130
  119. data/lib/rdoc/ri/formatter.rb +7 -3
  120. data/lib/rdoc/ri/paths.rb +140 -136
  121. data/lib/rdoc/ri/servlet.rb +456 -0
  122. data/lib/rdoc/ri/store.rb +4 -2
  123. data/lib/rdoc/ri/task.rb +55 -51
  124. data/lib/rdoc/ri.rb +14 -11
  125. data/lib/rdoc/rubygems_hook.rb +194 -192
  126. data/lib/rdoc/server.rb +462 -0
  127. data/lib/rdoc/stats/normal.rb +46 -42
  128. data/lib/rdoc/stats/quiet.rb +39 -35
  129. data/lib/rdoc/stats/verbose.rb +35 -31
  130. data/lib/rdoc/stats.rb +363 -338
  131. data/lib/rdoc/store.rb +919 -725
  132. data/lib/rdoc/task.rb +260 -255
  133. data/lib/rdoc/text.rb +130 -245
  134. data/lib/rdoc/token_stream.rb +101 -115
  135. data/lib/rdoc/tom_doc.rb +203 -201
  136. data/lib/rdoc/version.rb +1 -1
  137. data/lib/rdoc.rb +35 -7
  138. data/lib/rubygems_plugin.rb +2 -11
  139. data/rdoc-logo.svg +43 -0
  140. data/rdoc.gemspec +6 -4
  141. metadata +36 -20
  142. data/lib/rdoc/code_object/anon_class.rb +0 -10
  143. data/lib/rdoc/code_object/ghost_method.rb +0 -6
  144. data/lib/rdoc/code_object/meta_method.rb +0 -6
  145. data/lib/rdoc/markdown/literals.kpeg +0 -21
  146. data/lib/rdoc/markdown/literals.rb +0 -454
  147. data/lib/rdoc/parser/prism_ruby.rb +0 -1112
  148. data/lib/rdoc/parser/ripper_state_lex.rb +0 -302
  149. data/lib/rdoc/parser/ruby_tools.rb +0 -163
  150. data/lib/rdoc/servlet.rb +0 -452
@@ -1,235 +1,265 @@
1
1
  # frozen_string_literal: true
2
- ##
3
- # Subclass of the RDoc::Markup::ToHtml class that supports looking up method
4
- # names, classes, etc to create links. RDoc::CrossReference is used to
5
- # generate those links based on the current context.
2
+ module RDoc
3
+ class Markup
4
+ ##
5
+ # Subclass of the RDoc::Markup::ToHtml class that supports looking up method
6
+ # names, classes, etc to create links. RDoc::CrossReference is used to
7
+ # generate those links based on the current context.
6
8
 
7
- class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
9
+ class ToHtmlCrossref < Markup::ToHtml
8
10
 
9
- # :stopdoc:
10
- ALL_CROSSREF_REGEXP = RDoc::CrossReference::ALL_CROSSREF_REGEXP
11
- CLASS_REGEXP_STR = RDoc::CrossReference::CLASS_REGEXP_STR
12
- CROSSREF_REGEXP = RDoc::CrossReference::CROSSREF_REGEXP
13
- METHOD_REGEXP_STR = RDoc::CrossReference::METHOD_REGEXP_STR
14
- # :startdoc:
11
+ # :stopdoc:
12
+ ALL_CROSSREF_REGEXP = CrossReference::ALL_CROSSREF_REGEXP
13
+ CROSSREF_REGEXP = CrossReference::CROSSREF_REGEXP
14
+ # :startdoc:
15
15
 
16
- ##
17
- # RDoc::CodeObject for generating references
16
+ ##
17
+ # RDoc::CodeObject for generating references
18
18
 
19
- attr_accessor :context
19
+ attr_accessor :context
20
20
 
21
- ##
22
- # Should we show '#' characters on method references?
21
+ ##
22
+ # Should we show '#' characters on method references?
23
23
 
24
- attr_accessor :show_hash
24
+ attr_accessor :show_hash
25
25
 
26
- ##
27
- # Creates a new crossref resolver that generates links relative to +context+
28
- # which lives at +from_path+ in the generated files. '#' characters on
29
- # references are removed unless +show_hash+ is true. Only method names
30
- # preceded by '#' or '::' are linked, unless +hyperlink_all+ is true.
26
+ ##
27
+ # Creates a new crossref resolver that generates links relative to +context+
28
+ # which lives at +from_path+ in the generated files. '#' characters on
29
+ # references are removed unless +show_hash+ is true. Only method names
30
+ # preceded by '#' or '::' are linked, unless +hyperlink_all+ is true.
31
31
 
32
- def initialize(options, from_path, context, markup = nil)
33
- raise ArgumentError, 'from_path cannot be nil' if from_path.nil?
32
+ def initialize(from_path, context, pipe: false, output_decoration: true,
33
+ hyperlink_all: false, show_hash: false,
34
+ autolink_excluded_words: [], warn_missing_rdoc_ref: true)
35
+ raise ArgumentError, 'from_path cannot be nil' if from_path.nil?
34
36
 
35
- super options, markup
37
+ super(pipe: pipe, output_decoration: output_decoration)
36
38
 
37
- @context = context
38
- @from_path = from_path
39
- @hyperlink_all = @options.hyperlink_all
40
- @show_hash = @options.show_hash
39
+ @context = context
40
+ @from_path = from_path
41
+ @hyperlink_all = hyperlink_all
42
+ @show_hash = show_hash
43
+ @autolink_excluded_words = autolink_excluded_words
44
+ @warn_missing_rdoc_ref = warn_missing_rdoc_ref
41
45
 
42
- @cross_reference = RDoc::CrossReference.new @context
43
- end
44
-
45
- # :nodoc:
46
- def init_link_notation_regexp_handlings
47
- add_regexp_handling_RDOCLINK
48
-
49
- # The crossref must be linked before tidylink because Klass.method[:sym]
50
- # will be processed as a tidylink first and will be broken.
51
- crossref_re = @options.hyperlink_all ? ALL_CROSSREF_REGEXP : CROSSREF_REGEXP
52
- @markup.add_regexp_handling crossref_re, :CROSSREF
53
- end
46
+ @cross_reference = CrossReference.new @context
47
+ end
54
48
 
55
- ##
56
- # Creates a link to the reference +name+ if the name exists. If +text+ is
57
- # given it is used as the link text, otherwise +name+ is used.
49
+ # :nodoc:
50
+ def init_link_notation_regexp_handlings
51
+ add_regexp_handling_RDOCLINK
58
52
 
59
- def cross_reference(name, text = nil, code = true, rdoc_ref: false)
60
- lookup = name
53
+ # The crossref must be linked before tidylink because Klass.method[:sym]
54
+ # will be processed as a tidylink first and will be broken.
55
+ crossref_re = @hyperlink_all ? ALL_CROSSREF_REGEXP : CROSSREF_REGEXP
56
+ @markup.add_regexp_handling crossref_re, :CROSSREF
57
+ end
61
58
 
62
- name = name[1..-1] unless @show_hash if name[0, 1] == '#'
59
+ ##
60
+ # Creates a link to the reference +name+ if the name exists. If +text+ is
61
+ # given it is used as the link text, otherwise +name+ is used.
62
+ # Returns +nil+ if the link target could not be resolved.
63
+
64
+ def cross_reference(name, text = nil, code = true, rdoc_ref: false)
65
+ # Strip '#' for link display text (e.g. #method shows as "method" in links)
66
+ display = !@show_hash && name.start_with?('#') ? name[1..] : name
67
+
68
+ if !display.end_with?('+@', '-@') && match = display.match(/(.*[^#:])?@(.*)/)
69
+ context_name = match[1]
70
+ label = convert_string(Text.decode_legacy_label(match[2]))
71
+ text ||= "#{label} at <code>#{convert_string(context_name)}</code>" if context_name
72
+ text ||= label
73
+ code = false
74
+ else
75
+ text ||= convert_string(display)
76
+ end
63
77
 
64
- if !name.end_with?('+@', '-@') && match = name.match(/(.*[^#:])?@(.*)/)
65
- context_name = match[1]
66
- label = RDoc::Text.decode_legacy_label(match[2])
67
- text ||= "#{label} at <code>#{context_name}</code>" if context_name
68
- text ||= label
69
- code = false
70
- else
71
- text ||= name
72
- end
78
+ link(name, text, code, rdoc_ref: rdoc_ref)
79
+ end
73
80
 
74
- link lookup, text, code, rdoc_ref: rdoc_ref
75
- end
81
+ ##
82
+ # We're invoked when any text matches the CROSSREF pattern. If we find the
83
+ # corresponding reference, generate a link. If the name we're looking for
84
+ # contains no punctuation, we look for it up the module/class chain. For
85
+ # example, ToHtml is found, even without the <tt>RDoc::Markup::</tt> prefix,
86
+ # because we look for it in module Markup first.
76
87
 
77
- ##
78
- # We're invoked when any text matches the CROSSREF pattern. If we find the
79
- # corresponding reference, generate a link. If the name we're looking for
80
- # contains no punctuation, we look for it up the module/class chain. For
81
- # example, ToHtml is found, even without the <tt>RDoc::Markup::</tt> prefix,
82
- # because we look for it in module Markup first.
88
+ def handle_regexp_CROSSREF(name)
89
+ return convert_string(name) if in_tidylink_label?
90
+ return convert_string(name) if @autolink_excluded_words&.include?(name)
83
91
 
84
- def handle_regexp_CROSSREF(name)
85
- return convert_string(name) if in_tidylink_label?
86
- return name if @options.autolink_excluded_words&.include?(name)
92
+ return convert_string(name) if name =~ /@[\w-]+\.[\w-]/ # labels that look like emails
87
93
 
88
- return name if name =~ /@[\w-]+\.[\w-]/ # labels that look like emails
94
+ unless @hyperlink_all
95
+ # This ensures that words entirely consisting of lowercase letters will
96
+ # not have cross-references generated (to suppress lots of erroneous
97
+ # cross-references to "new" in text, for instance)
98
+ return name if name =~ /\A[a-z]*\z/
99
+ end
100
+ cross_reference(name, rdoc_ref: false) || convert_string(name)
101
+ end
89
102
 
90
- unless @hyperlink_all then
91
- # This ensures that words entirely consisting of lowercase letters will
92
- # not have cross-references generated (to suppress lots of erroneous
93
- # cross-references to "new" in text, for instance)
94
- return name if name =~ /\A[a-z]*\z/
95
- end
103
+ ##
104
+ # Handles <tt>rdoc-ref:</tt> scheme links and allows RDoc::Markup::ToHtml to
105
+ # handle other schemes.
96
106
 
97
- cross_reference name, rdoc_ref: false
98
- end
107
+ def handle_regexp_HYPERLINK(url)
108
+ return convert_string(url) if in_tidylink_label?
99
109
 
100
- ##
101
- # Handles <tt>rdoc-ref:</tt> scheme links and allows RDoc::Markup::ToHtml to
102
- # handle other schemes.
110
+ case url
111
+ when /\Ardoc-ref:/
112
+ ref = $'
113
+ cross_reference(ref, rdoc_ref: true) || convert_string(ref)
114
+ else
115
+ super
116
+ end
117
+ end
103
118
 
104
- def handle_regexp_HYPERLINK(url)
105
- return convert_string(url) if in_tidylink_label?
119
+ ##
120
+ # +target+ is an rdoc-schemed link that will be converted into a hyperlink.
121
+ # For the rdoc-ref scheme the cross-reference will be looked up and the
122
+ # given name will be used.
123
+ #
124
+ # All other contents are handled by
125
+ # {the superclass}[rdoc-ref:RDoc::Markup::ToHtml#handle_regexp_RDOCLINK]
126
+
127
+ def handle_regexp_RDOCLINK(url)
128
+ case url
129
+ when /\Ardoc-ref:/
130
+ if in_tidylink_label?
131
+ convert_string(url)
132
+ else
133
+ ref = $'
134
+ cross_reference(ref, rdoc_ref: true) || convert_string(ref)
135
+ end
136
+ else
137
+ super
138
+ end
139
+ end
106
140
 
107
- case url
108
- when /\Ardoc-ref:/
109
- cross_reference $', rdoc_ref: true
110
- else
111
- super
112
- end
113
- end
141
+ ##
142
+ # Generates links for <tt>rdoc-ref:</tt> scheme URLs and allows
143
+ # RDoc::Markup::ToHtml to handle other schemes.
114
144
 
115
- ##
116
- # +target+ is an rdoc-schemed link that will be converted into a hyperlink.
117
- # For the rdoc-ref scheme the cross-reference will be looked up and the
118
- # given name will be used.
119
- #
120
- # All other contents are handled by
121
- # {the superclass}[rdoc-ref:RDoc::Markup::ToHtml#handle_regexp_RDOCLINK]
122
-
123
- def handle_regexp_RDOCLINK(url)
124
- case url
125
- when /\Ardoc-ref:/
126
- if in_tidylink_label?
127
- convert_string(url)
128
- else
129
- cross_reference $', rdoc_ref: true
145
+ def gen_url(url, text)
146
+ if url =~ /\Ardoc-ref:/
147
+ name = $'
148
+ cross_reference(name, text, name == text, rdoc_ref: true) || text
149
+ else
150
+ super
151
+ end
130
152
  end
131
- else
132
- super
133
- end
134
- end
135
153
 
136
- ##
137
- # Generates links for <tt>rdoc-ref:</tt> scheme URLs and allows
138
- # RDoc::Markup::ToHtml to handle other schemes.
154
+ ##
155
+ # Creates an HTML link to +name+ with the given +html_string+.
156
+ # +html_string+ should be already escaped and may contain HTML tags.
157
+ # Returns the link HTML string, or +nil+ if the reference could not be resolved.
139
158
 
140
- def gen_url(url, text)
141
- if url =~ /\Ardoc-ref:/
142
- name = $'
143
- cross_reference name, text, name == text, rdoc_ref: true
144
- else
145
- super
146
- end
147
- end
159
+ def link(name, html_string, code = true, rdoc_ref: false)
160
+ if !(name.end_with?('+@', '-@')) and name =~ /(.*[^#:])?@/
161
+ name = $1
162
+ label = $'
163
+ end
148
164
 
149
- ##
150
- # Creates an HTML link to +name+ with the given +text+.
165
+ ref = @cross_reference.resolve name if name
151
166
 
152
- def link(name, text, code = true, rdoc_ref: false)
153
- if !(name.end_with?('+@', '-@')) and name =~ /(.*[^#:])?@/
154
- name = $1
155
- label = $'
156
- end
167
+ # Non-text source files (C, Ruby, etc.) don't get HTML pages generated,
168
+ # so don't auto-link to them. Explicit rdoc-ref: links are still allowed.
169
+ if !rdoc_ref && TopLevel === ref && !ref.text?
170
+ return
171
+ end
172
+
173
+ if ref
174
+ path = ref.as_href(@from_path)
175
+
176
+ if code and CodeObject === ref and !(TopLevel === ref)
177
+ html_string = "<code>#{html_string}</code>"
178
+ end
179
+ elsif name
180
+ if rdoc_ref && @warn_missing_rdoc_ref
181
+ puts "#{@from_path}: `rdoc-ref:#{name}` can't be resolved for `#{html_string}`"
182
+ end
183
+ return
184
+ else
185
+ # A bare label reference like @foo still produces a valid anchor link
186
+ return unless label
187
+ path = +""
188
+ end
157
189
 
158
- ref = @cross_reference.resolve name, text if name
190
+ if label
191
+ # Decode legacy labels (e.g., "What-27s+Here" -> "What's Here")
192
+ # then convert to GitHub-style anchor format
193
+ decoded_label = Text.decode_legacy_label(label)
194
+ formatted_label = Text.to_anchor(decoded_label)
195
+
196
+ # Case 1: Path already has an anchor (e.g., method link)
197
+ # Input: C1#method@label -> path="C1.html#method-i-m"
198
+ # Output: C1.html#method-i-m-label
199
+ if path =~ /#/
200
+ path << "-#{formatted_label}"
201
+
202
+ # Case 2: Label matches a section title
203
+ # Input: C1@Section -> path="C1.html", section "Section" exists
204
+ # Output: C1.html#section (uses section.aref for GitHub-style)
205
+ elsif (section = ref&.sections&.find { |s| decoded_label == s.title })
206
+ path << "##{section.aref}"
207
+
208
+ # Case 3: Ref has an aref (class/module context)
209
+ # Input: C1@heading -> path="C1.html", ref=C1 class
210
+ # Output: C1.html#class-c1-heading
211
+ elsif ref.respond_to?(:aref)
212
+ path << "##{ref.aref}-#{formatted_label}"
213
+
214
+ # Case 4: No context, just the label (e.g., TopLevel/file)
215
+ # Input: README@section -> path="README_md.html"
216
+ # Output: README_md.html#section
217
+ else
218
+ path << "##{formatted_label}"
219
+ end
220
+ end
159
221
 
160
- case ref
161
- when String then
162
- if rdoc_ref && @options.warn_missing_rdoc_ref
163
- puts "#{@from_path}: `rdoc-ref:#{name}` can't be resolved for `#{text}`"
222
+ "<a href=\"#{path}\">#{html_string}</a>"
164
223
  end
165
- ref
166
- else
167
- path = ref ? ref.as_href(@from_path) : +""
168
224
 
169
- if code and RDoc::CodeObject === ref and !(RDoc::TopLevel === ref)
170
- text = "<code>#{CGI.escapeHTML text}</code>"
225
+ def handle_TT(code)
226
+ emit_inline(tt_cross_reference(code) || "<code>#{convert_string(code)}</code>")
171
227
  end
172
228
 
173
- if label
174
- # Decode legacy labels (e.g., "What-27s+Here" -> "What's Here")
175
- # then convert to GitHub-style anchor format
176
- decoded_label = RDoc::Text.decode_legacy_label(label)
177
- formatted_label = RDoc::Text.to_anchor(decoded_label)
178
-
179
- # Case 1: Path already has an anchor (e.g., method link)
180
- # Input: C1#method@label -> path="C1.html#method-i-m"
181
- # Output: C1.html#method-i-m-label
182
- if path =~ /#/
183
- path << "-#{formatted_label}"
184
-
185
- # Case 2: Label matches a section title
186
- # Input: C1@Section -> path="C1.html", section "Section" exists
187
- # Output: C1.html#section (uses section.aref for GitHub-style)
188
- elsif (section = ref&.sections&.find { |s| decoded_label == s.title })
189
- path << "##{section.aref}"
190
-
191
- # Case 3: Ref has an aref (class/module context)
192
- # Input: C1@heading -> path="C1.html", ref=C1 class
193
- # Output: C1.html#class-c1-heading
194
- elsif ref.respond_to?(:aref)
195
- path << "##{ref.aref}-#{formatted_label}"
196
-
197
- # Case 4: No context, just the label (e.g., TopLevel/file)
198
- # Input: README@section -> path="README_md.html"
199
- # Output: README_md.html#section
229
+ # Applies additional special handling on top of the one defined in ToHtml.
230
+ # When a tidy link is <tt>{Foo}[rdoc-ref:Foo]</tt>, the label part is surrounded by <tt><code></code></tt>.
231
+ # TODO: reconsider this workaround.
232
+ def apply_tidylink_label_special_handling(label, url)
233
+ if url == "rdoc-ref:#{label}" && cross_reference(label)&.include?('<code>')
234
+ "<code>#{convert_string(label)}</code>"
200
235
  else
201
- path << "##{formatted_label}"
236
+ super
202
237
  end
203
238
  end
204
239
 
205
- "<a href=\"#{path}\">#{text}</a>"
206
- end
207
- end
208
-
209
- def handle_TT(code)
210
- emit_inline(tt_cross_reference(code) || "<code>#{CGI.escapeHTML code}</code>")
211
- end
212
-
213
- # Applies additional special handling on top of the one defined in ToHtml.
214
- # When a tidy link is <tt>{Foo}[rdoc-ref:Foo]</tt>, the label part is surrounded by <tt><code></code></tt>.
215
- # TODO: reconsider this workaround.
216
- def apply_tidylink_label_special_handling(label, url)
217
- if url == "rdoc-ref:#{label}" && cross_reference(label).include?('<code>')
218
- "<code>#{convert_string(label)}</code>"
219
- else
220
- super
240
+ # Handles cross-reference and suppressed-crossref inside tt tag.
241
+ # Returns nil if code is not an existing cross-reference nor a suppressed-crossref.
242
+ def tt_cross_reference(code)
243
+ return if in_tidylink_label?
244
+
245
+ crossref_regexp = @hyperlink_all ? ALL_CROSSREF_REGEXP : CROSSREF_REGEXP
246
+ # REGEXP sometimes matches a string that starts with a backslash but is not a
247
+ # suppressed cross-reference (for example, `\+`), so the backslash-removed
248
+ # part needs to be checked against crossref_regexp.
249
+ match = crossref_regexp.match(code.delete_prefix('\\'))
250
+ return unless match && match.begin(1).zero?
251
+ return unless match.post_match.match?(/\A[[:punct:]\s]*\z/)
252
+
253
+ # cross_reference(file_page) may return a link without code tag.
254
+ # We need to check it because this method shouldn't return an html text without code tag.
255
+ if code.start_with?('\\')
256
+ # Remove leading backslash if crossref exists
257
+ "<code>#{convert_string(code[1..])}</code>" if cross_reference(code[1..])&.include?('<code>')
258
+ else
259
+ html = cross_reference(code)
260
+ html if html&.include?('<code>')
261
+ end
262
+ end
221
263
  end
222
264
  end
223
-
224
- def tt_cross_reference(code)
225
- return if in_tidylink_label?
226
-
227
- crossref_regexp = @options.hyperlink_all ? ALL_CROSSREF_REGEXP : CROSSREF_REGEXP
228
- match = crossref_regexp.match(code)
229
- return unless match && match.begin(1).zero?
230
- return unless match.post_match.match?(/\A[[:punct:]\s]*\z/)
231
-
232
- ref = cross_reference(code)
233
- ref if ref != code
234
- end
235
265
  end