rdoc 6.14.2 → 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 (107) hide show
  1. checksums.yaml +4 -4
  2. data/CONTRIBUTING.md +196 -0
  3. data/History.rdoc +1 -1
  4. data/LEGAL.rdoc +6 -0
  5. data/README.md +195 -0
  6. data/RI.md +1 -1
  7. data/doc/markup_reference/markdown.md +558 -0
  8. data/doc/markup_reference/rdoc.rdoc +1169 -0
  9. data/lib/rdoc/code_object/any_method.rb +15 -7
  10. data/lib/rdoc/code_object/class_module.rb +62 -11
  11. data/lib/rdoc/code_object/constant.rb +9 -0
  12. data/lib/rdoc/code_object/context/section.rb +20 -1
  13. data/lib/rdoc/code_object/method_attr.rb +13 -1
  14. data/lib/rdoc/code_object/top_level.rb +31 -26
  15. data/lib/rdoc/comment.rb +190 -8
  16. data/lib/rdoc/cross_reference.rb +31 -22
  17. data/lib/rdoc/generator/aliki.rb +183 -0
  18. data/lib/rdoc/generator/darkfish.rb +8 -2
  19. data/lib/rdoc/generator/template/aliki/_aside_toc.rhtml +8 -0
  20. data/lib/rdoc/generator/template/aliki/_footer.rhtml +23 -0
  21. data/lib/rdoc/generator/template/aliki/_head.rhtml +158 -0
  22. data/lib/rdoc/generator/template/aliki/_header.rhtml +56 -0
  23. data/lib/rdoc/generator/template/aliki/_icons.rhtml +208 -0
  24. data/lib/rdoc/generator/template/aliki/_sidebar_ancestors.rhtml +16 -0
  25. data/lib/rdoc/generator/template/aliki/_sidebar_classes.rhtml +15 -0
  26. data/lib/rdoc/generator/template/aliki/_sidebar_extends.rhtml +25 -0
  27. data/lib/rdoc/generator/template/aliki/_sidebar_includes.rhtml +25 -0
  28. data/lib/rdoc/generator/template/aliki/_sidebar_installed.rhtml +16 -0
  29. data/lib/rdoc/generator/template/aliki/_sidebar_methods.rhtml +41 -0
  30. data/lib/rdoc/generator/template/aliki/_sidebar_pages.rhtml +67 -0
  31. data/lib/rdoc/generator/template/aliki/_sidebar_search.rhtml +15 -0
  32. data/lib/rdoc/generator/template/aliki/_sidebar_sections.rhtml +21 -0
  33. data/lib/rdoc/generator/template/aliki/_sidebar_toggle.rhtml +3 -0
  34. data/lib/rdoc/generator/template/aliki/class.rhtml +220 -0
  35. data/lib/rdoc/generator/template/aliki/css/rdoc.css +1963 -0
  36. data/lib/rdoc/generator/template/aliki/index.rhtml +22 -0
  37. data/lib/rdoc/generator/template/aliki/js/aliki.js +505 -0
  38. data/lib/rdoc/generator/template/aliki/js/c_highlighter.js +299 -0
  39. data/lib/rdoc/generator/template/aliki/js/search_controller.js +129 -0
  40. data/lib/rdoc/generator/template/aliki/js/search_navigation.js +105 -0
  41. data/lib/rdoc/generator/template/aliki/js/search_ranker.js +239 -0
  42. data/lib/rdoc/generator/template/aliki/js/theme-toggle.js +112 -0
  43. data/lib/rdoc/generator/template/aliki/page.rhtml +18 -0
  44. data/lib/rdoc/generator/template/aliki/servlet_not_found.rhtml +14 -0
  45. data/lib/rdoc/generator/template/aliki/servlet_root.rhtml +65 -0
  46. data/lib/rdoc/generator/template/darkfish/_footer.rhtml +3 -3
  47. data/lib/rdoc/generator/template/darkfish/_head.rhtml +14 -19
  48. data/lib/rdoc/generator/template/darkfish/_sidebar_extends.rhtml +8 -8
  49. data/lib/rdoc/generator/template/darkfish/_sidebar_includes.rhtml +8 -8
  50. data/lib/rdoc/generator/template/darkfish/_sidebar_installed.rhtml +7 -6
  51. data/lib/rdoc/generator/template/darkfish/_sidebar_methods.rhtml +6 -6
  52. data/lib/rdoc/generator/template/darkfish/_sidebar_pages.rhtml +18 -18
  53. data/lib/rdoc/generator/template/darkfish/_sidebar_parent.rhtml +2 -2
  54. data/lib/rdoc/generator/template/darkfish/_sidebar_search.rhtml +1 -0
  55. data/lib/rdoc/generator/template/darkfish/_sidebar_sections.rhtml +3 -3
  56. data/lib/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml +14 -14
  57. data/lib/rdoc/generator/template/darkfish/class.rhtml +70 -68
  58. data/lib/rdoc/generator/template/darkfish/css/rdoc.css +19 -0
  59. data/lib/rdoc/generator/template/darkfish/index.rhtml +4 -3
  60. data/lib/rdoc/generator/template/darkfish/js/darkfish.js +21 -1
  61. data/lib/rdoc/generator/template/darkfish/js/search.js +11 -1
  62. data/lib/rdoc/generator/template/darkfish/page.rhtml +2 -1
  63. data/lib/rdoc/generator/template/darkfish/servlet_not_found.rhtml +2 -1
  64. data/lib/rdoc/generator/template/darkfish/servlet_root.rhtml +19 -19
  65. data/lib/rdoc/generator/template/darkfish/table_of_contents.rhtml +19 -17
  66. data/lib/rdoc/generator/template/json_index/js/searcher.js +48 -6
  67. data/lib/rdoc/generator.rb +1 -0
  68. data/lib/rdoc/markdown.kpeg +29 -22
  69. data/lib/rdoc/markdown.rb +366 -549
  70. data/lib/rdoc/markup/attribute_manager.rb +28 -1
  71. data/lib/rdoc/markup/blank_line.rb +25 -23
  72. data/lib/rdoc/markup/element.rb +21 -0
  73. data/lib/rdoc/markup/hard_break.rb +30 -27
  74. data/lib/rdoc/markup/heading.rb +166 -77
  75. data/lib/rdoc/markup/pre_process.rb +34 -10
  76. data/lib/rdoc/markup/raw.rb +52 -55
  77. data/lib/rdoc/markup/table.rb +48 -40
  78. data/lib/rdoc/markup/to_ansi.rb +4 -0
  79. data/lib/rdoc/markup/to_bs.rb +4 -0
  80. data/lib/rdoc/markup/to_html.rb +193 -25
  81. data/lib/rdoc/markup/to_html_crossref.rb +77 -28
  82. data/lib/rdoc/markup/to_label.rb +11 -1
  83. data/lib/rdoc/markup/to_rdoc.rb +11 -3
  84. data/lib/rdoc/markup/verbatim.rb +1 -1
  85. data/lib/rdoc/markup.rb +3 -2
  86. data/lib/rdoc/options.rb +22 -11
  87. data/lib/rdoc/parser/c.rb +15 -46
  88. data/lib/rdoc/parser/changelog.rb +8 -0
  89. data/lib/rdoc/parser/prism_ruby.rb +121 -113
  90. data/lib/rdoc/parser/ruby.rb +8 -8
  91. data/lib/rdoc/parser/ruby_tools.rb +5 -7
  92. data/lib/rdoc/parser/simple.rb +4 -21
  93. data/lib/rdoc/rdoc.rb +1 -0
  94. data/lib/rdoc/ri/task.rb +2 -2
  95. data/lib/rdoc/rubygems_hook.rb +3 -3
  96. data/lib/rdoc/store.rb +2 -2
  97. data/lib/rdoc/task.rb +4 -4
  98. data/lib/rdoc/text.rb +16 -1
  99. data/lib/rdoc/token_stream.rb +17 -9
  100. data/lib/rdoc/tom_doc.rb +1 -1
  101. data/lib/rdoc/version.rb +1 -1
  102. data/rdoc.gemspec +6 -5
  103. metadata +53 -12
  104. data/CONTRIBUTING.rdoc +0 -219
  105. data/ExampleMarkdown.md +0 -39
  106. data/ExampleRDoc.rdoc +0 -210
  107. data/README.rdoc +0 -144
@@ -1,52 +1,60 @@
1
1
  # frozen_string_literal: true
2
- ##
3
- # A section of table
4
2
 
5
- class RDoc::Markup::Table
6
- # headers of each column
7
- attr_accessor :header
3
+ module RDoc
4
+ class Markup
5
+ # A section of table
6
+ class Table < Element
7
+ # Headers of each column
8
+ #: Array[String]
9
+ attr_accessor :header
8
10
 
9
- # alignments of each column
10
- attr_accessor :align
11
+ # Alignments of each column
12
+ #: Array[Symbol?]
13
+ attr_accessor :align
11
14
 
12
- # body texts of each column
13
- attr_accessor :body
15
+ # Body texts of each column
16
+ #: Array[String]
17
+ attr_accessor :body
14
18
 
15
- # Creates new instance
16
- def initialize(header, align, body)
17
- @header, @align, @body = header, align, body
18
- end
19
-
20
- # :stopdoc:
21
- def ==(other)
22
- self.class == other.class and
23
- @header == other.header and
24
- @align == other.align and
25
- @body == other.body
26
- end
19
+ #: (Array[String], Array[Symbol?], Array[String]) -> void
20
+ def initialize(header, align, body)
21
+ @header, @align, @body = header, align, body
22
+ end
27
23
 
28
- def accept(visitor)
29
- visitor.accept_table @header, @body, @align
30
- end
24
+ #: (Object) -> bool
25
+ def ==(other)
26
+ self.class == other.class && @header == other.header &&
27
+ @align == other.align && @body == other.body
28
+ end
31
29
 
32
- def pretty_print(q)
33
- q.group 2, '[Table: ', ']' do
34
- q.group 2, '[Head: ', ']' do
35
- q.seplist @header.zip(@align) do |text, align|
36
- q.pp text
37
- if align
38
- q.text ":"
39
- q.breakable
40
- q.text align.to_s
41
- end
42
- end
30
+ # @override
31
+ #: (untyped) -> void
32
+ def accept(visitor)
33
+ visitor.accept_table(@header, @body, @align)
43
34
  end
44
- q.breakable
45
- q.group 2, '[Body: ', ']' do
46
- q.seplist @body do |body|
47
- q.group 2, '[', ']' do
48
- q.seplist body do |text|
35
+
36
+ # @override
37
+ #: (untyped) -> String
38
+ def pretty_print(q)
39
+ q.group 2, '[Table: ', ']' do
40
+ q.group 2, '[Head: ', ']' do
41
+ q.seplist @header.zip(@align) do |text, align|
49
42
  q.pp text
43
+ if align
44
+ q.text ":"
45
+ q.breakable
46
+ q.text align.to_s
47
+ end
48
+ end
49
+ end
50
+ q.breakable
51
+ q.group 2, '[Body: ', ']' do
52
+ q.seplist @body do |body|
53
+ q.group 2, '[', ']' do
54
+ q.seplist body do |text|
55
+ q.pp text
56
+ end
57
+ end
50
58
  end
51
59
  end
52
60
  end
@@ -81,6 +81,10 @@ class RDoc::Markup::ToAnsi < RDoc::Markup::ToRdoc
81
81
  end
82
82
  end
83
83
 
84
+ def calculate_text_width(text)
85
+ text.gsub(/\e\[[\d;]*m/, '').size
86
+ end
87
+
84
88
  ##
85
89
  # Starts accepting with a reset screen
86
90
 
@@ -65,6 +65,10 @@ class RDoc::Markup::ToBs < RDoc::Markup::ToRdoc
65
65
  end
66
66
  end
67
67
 
68
+ def calculate_text_width(text)
69
+ text.gsub(/_\x08/, '').gsub(/\x08./, '').size
70
+ end
71
+
68
72
  ##
69
73
  # Turns on or off regexp handling for +convert_string+
70
74
 
@@ -158,19 +158,16 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
158
158
  def handle_regexp_TIDYLINK(target)
159
159
  text = target.text
160
160
 
161
- return text unless
162
- text =~ /^\{(.*)\}\[(.*?)\]$/ or text =~ /^(\S+)\[(.*?)\]$/
163
-
164
- label = $1
165
- url = CGI.escapeHTML($2)
161
+ if tidy_link_capturing?
162
+ return finish_tidy_link(text)
163
+ end
166
164
 
167
- if /^rdoc-image:/ =~ label
168
- label = handle_RDOCLINK(label)
169
- else
170
- label = CGI.escapeHTML(label)
165
+ if text.start_with?('{') && !text.include?('}')
166
+ start_tidy_link text
167
+ return ''
171
168
  end
172
169
 
173
- gen_url url, label
170
+ convert_complete_tidy_link(text)
174
171
  end
175
172
 
176
173
  # :section: Visitor
@@ -224,10 +221,15 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
224
221
 
225
222
  def accept_verbatim(verbatim)
226
223
  text = verbatim.text.rstrip
224
+ format = verbatim.format
227
225
 
228
226
  klass = nil
229
227
 
230
- 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))
231
233
  begin
232
234
  tokens = RDoc::Parser::RipperStateLex.parse text
233
235
  klass = ' class="ruby"'
@@ -239,6 +241,7 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
239
241
  CGI.escapeHTML text
240
242
  end
241
243
  else
244
+ klass = " class=\"#{format}\"" if format
242
245
  CGI.escapeHTML text
243
246
  end
244
247
 
@@ -309,17 +312,26 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
309
312
  level = [6, heading.level].min
310
313
 
311
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
312
322
 
313
323
  @res << if @options.output_decoration
314
324
  "\n<h#{level} id=\"#{label}\">"
315
325
  else
316
326
  "\n<h#{level}>"
317
327
  end
318
- @res << to_html(heading.text)
319
- unless @options.pipe then
320
- @res << "<span><a href=\"##{label}\">&para;</a>"
321
- @res << " <a href=\"#top\">&uarr;</a></span>"
328
+
329
+ if @options.pipe
330
+ @res << to_html(heading.text)
331
+ else
332
+ @res << "<a href=\"##{label}\">#{to_html(heading.text)}</a>"
322
333
  end
334
+
323
335
  @res << "</h#{level}>\n"
324
336
  end
325
337
 
@@ -363,14 +375,18 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
363
375
  end
364
376
 
365
377
  ##
366
- # Generate a link to +url+ with content +text+. Handles the special cases
367
- # 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
368
385
 
369
386
  def gen_url(url, text)
370
387
  scheme, url, id = parse_url url
371
388
 
372
- if %w[http https link].include?(scheme) and
373
- url =~ /\.(gif|png|jpg|jpeg|bmp)$/ then
389
+ if %w[http https link].include?(scheme) && url =~ /\.(gif|png|jpg|jpeg|bmp)\z/
374
390
  "<img src=\"#{url}\" />"
375
391
  else
376
392
  if scheme != 'link' and %r%\A((?!https?:)(?:[^/#]*/)*+)([^/#]+)\.(rb|rdoc|md)(?=\z|#)%i =~ url
@@ -382,9 +398,11 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
382
398
 
383
399
  link = "<a#{id} href=\"#{url}\">#{text}</a>"
384
400
 
385
- link = "<sup>#{link}</sup>" if /"foot/ =~ id
386
-
387
- link
401
+ if /"foot/.match?(id)
402
+ "<sup>#{link}</sup>"
403
+ else
404
+ link
405
+ end
388
406
  end
389
407
  end
390
408
 
@@ -401,9 +419,10 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
401
419
  # Maps attributes to HTML tags
402
420
 
403
421
  def init_tags
404
- add_tag :BOLD, "<strong>", "</strong>"
405
- add_tag :TT, "<code>", "</code>"
406
- 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>"
407
426
  end
408
427
 
409
428
  ##
@@ -458,4 +477,153 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
458
477
  super convert_flow @am.flow item
459
478
  end
460
479
 
480
+ private
481
+
482
+ def convert_flow(flow_items)
483
+ res = []
484
+
485
+ flow_items.each do |item|
486
+ case item
487
+ when String
488
+ append_flow_fragment res, convert_string(item)
489
+ when RDoc::Markup::AttrChanger
490
+ off_tags res, item
491
+ on_tags res, item
492
+ when RDoc::Markup::RegexpHandling
493
+ append_flow_fragment res, convert_regexp_handling(item)
494
+ else
495
+ raise "Unknown flow element: #{item.inspect}"
496
+ end
497
+ end
498
+
499
+ res.join
500
+ end
501
+
502
+ def append_flow_fragment(res, fragment)
503
+ return if fragment.nil? || fragment.empty?
504
+
505
+ emit_tidy_link_fragment(res, fragment)
506
+ end
507
+
508
+ def append_to_tidy_label(fragment)
509
+ @tidy_link_buffer << fragment
510
+ end
511
+
512
+ ##
513
+ # Matches an entire tidy link with a braced label "{label}[url]".
514
+ #
515
+ # Capture 1: label contents.
516
+ # Capture 2: URL text.
517
+ # Capture 3: trailing content.
518
+ TIDY_LINK_WITH_BRACES = /\A\{(.*?)\}\[(.*?)\](.*)\z/
519
+
520
+ ##
521
+ # Matches the tail of a braced tidy link when the opening brace was
522
+ # consumed earlier while accumulating the label text.
523
+ #
524
+ # Capture 1: remaining label content.
525
+ # Capture 2: URL text.
526
+ # Capture 3: trailing content.
527
+ TIDY_LINK_WITH_BRACES_TAIL = /\A(.*?)\}\[(.*?)\](.*)\z/
528
+
529
+ ##
530
+ # Matches a tidy link with a single-word label "label[url]".
531
+ #
532
+ # Capture 1: the single-word label (no whitespace).
533
+ # Capture 2: URL text between the brackets.
534
+ TIDY_LINK_SINGLE_WORD = /\A(\S+)\[(.*?)\](.*)\z/
535
+
536
+ def convert_complete_tidy_link(text)
537
+ return text unless
538
+ text =~ TIDY_LINK_WITH_BRACES or text =~ TIDY_LINK_SINGLE_WORD
539
+
540
+ label = $1
541
+ url = CGI.escapeHTML($2)
542
+
543
+ label_html = if /^rdoc-image:/ =~ label
544
+ handle_RDOCLINK(label)
545
+ else
546
+ render_tidy_link_label(label)
547
+ end
548
+
549
+ gen_url url, label_html
550
+ end
551
+
552
+ def emit_tidy_link_fragment(res, fragment)
553
+ if tidy_link_capturing?
554
+ append_to_tidy_label fragment
555
+ else
556
+ res << fragment
557
+ end
558
+ end
559
+
560
+ def finish_tidy_link(text)
561
+ label_tail, url, trailing = extract_tidy_link_parts(text)
562
+ append_to_tidy_label CGI.escapeHTML(label_tail) unless label_tail.empty?
563
+
564
+ return '' unless url
565
+
566
+ label_html = @tidy_link_buffer
567
+ @tidy_link_buffer = nil
568
+ link = gen_url(url, label_html)
569
+
570
+ return link if trailing.empty?
571
+
572
+ link + CGI.escapeHTML(trailing)
573
+ end
574
+
575
+ def extract_tidy_link_parts(text)
576
+ if text =~ TIDY_LINK_WITH_BRACES
577
+ [$1, CGI.escapeHTML($2), $3]
578
+ elsif text =~ TIDY_LINK_WITH_BRACES_TAIL
579
+ [$1, CGI.escapeHTML($2), $3]
580
+ elsif text =~ TIDY_LINK_SINGLE_WORD
581
+ [$1, CGI.escapeHTML($2), $3]
582
+ else
583
+ [text, nil, '']
584
+ end
585
+ end
586
+
587
+ def on_tags(res, item)
588
+ each_attr_tag(item.turn_on) do |tag|
589
+ emit_tidy_link_fragment(res, annotate(tag.on))
590
+ @in_tt += 1 if tt? tag
591
+ end
592
+ end
593
+
594
+ def off_tags(res, item)
595
+ each_attr_tag(item.turn_off, true) do |tag|
596
+ emit_tidy_link_fragment(res, annotate(tag.off))
597
+ @in_tt -= 1 if tt? tag
598
+ end
599
+ end
600
+
601
+ def start_tidy_link(text)
602
+ @tidy_link_buffer = String.new
603
+ append_to_tidy_label CGI.escapeHTML(text.delete_prefix('{'))
604
+ end
605
+
606
+ def tidy_link_capturing?
607
+ !!@tidy_link_buffer
608
+ end
609
+
610
+ def render_tidy_link_label(label)
611
+ RDoc::Markup::LinkLabelToHtml.render(label, @options, @from_path)
612
+ end
613
+ end
614
+
615
+ ##
616
+ # Formatter dedicated to rendering tidy link labels without mutating the
617
+ # calling formatter's state.
618
+
619
+ class RDoc::Markup::LinkLabelToHtml < RDoc::Markup::ToHtml
620
+ def self.render(label, options, from_path)
621
+ new(options, from_path).to_html(label)
622
+ end
623
+
624
+ def initialize(options, from_path = nil)
625
+ super(options)
626
+
627
+ self.from_path = from_path if from_path
628
+ end
461
629
  end
@@ -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
 
@@ -184,38 +203,35 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
184
203
  end
185
204
  end
186
205
 
187
- def convert_flow(flow)
206
+ def convert_flow(flow_items, &block)
188
207
  res = []
189
208
 
190
209
  i = 0
191
- while i < flow.size
192
- item = flow[i]
193
- i += 1
210
+ while i < flow_items.size
211
+ item = flow_items[i]
212
+
194
213
  case item
195
- when RDoc::Markup::AttrChanger then
196
- # Make "+Class#method+" a cross reference
197
- if tt_tag?(item.turn_on) and
198
- String === (str = flow[i]) and
199
- RDoc::Markup::AttrChanger === flow[i+1] and
200
- tt_tag?(flow[i+1].turn_off, true) and
201
- (@options.hyperlink_all ? ALL_CROSSREF_REGEXP : CROSSREF_REGEXP).match?(str) and
202
- (text = cross_reference str) != str
203
- then
204
- text = yield text, res if defined?(yield)
205
- res << text
206
- i += 2
214
+ when RDoc::Markup::AttrChanger
215
+ if !tidy_link_capturing? && (text = convert_tt_crossref(flow_items, i))
216
+ text = block.call(text, res) if block
217
+ append_flow_fragment res, text
218
+ i += 3
207
219
  next
208
220
  end
221
+
209
222
  off_tags res, item
210
- on_tags res, item
211
- when String then
223
+ on_tags res, item
224
+ i += 1
225
+ when String
212
226
  text = convert_string(item)
213
- text = yield text, res if defined?(yield)
214
- res << text
215
- when RDoc::Markup::RegexpHandling then
227
+ text = block.call(text, res) if block
228
+ append_flow_fragment res, text
229
+ i += 1
230
+ when RDoc::Markup::RegexpHandling
216
231
  text = convert_regexp_handling(item)
217
- text = yield text, res if defined?(yield)
218
- res << text
232
+ text = block.call(text, res) if block
233
+ append_flow_fragment res, text
234
+ i += 1
219
235
  else
220
236
  raise "Unknown flow element: #{item.inspect}"
221
237
  end
@@ -223,4 +239,37 @@ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
223
239
 
224
240
  res.join('')
225
241
  end
242
+
243
+ private
244
+
245
+ ##
246
+ # Detects <tt>...</tt> spans that contain a single cross-reference candidate.
247
+ # When the candidate occupies the whole span (aside from trailing
248
+ # punctuation), the tt markup is replaced by the resolved cross-reference.
249
+
250
+ def convert_tt_crossref(flow_items, index)
251
+ opener = flow_items[index]
252
+ return unless tt_tag?(opener.turn_on)
253
+
254
+ string = flow_items[index + 1]
255
+ closer = flow_items[index + 2]
256
+
257
+ return unless String === string
258
+ return unless RDoc::Markup::AttrChanger === closer
259
+ return unless tt_tag?(closer.turn_off, true)
260
+
261
+ crossref_regexp = @options.hyperlink_all ? ALL_CROSSREF_REGEXP : CROSSREF_REGEXP
262
+ match = crossref_regexp.match(string)
263
+ return unless match
264
+ return unless match.begin(1).zero?
265
+
266
+ trailing = match.post_match
267
+ # Only convert when the remainder is punctuation/whitespace so other tt text stays literal.
268
+ return unless trailing.match?(/\A[[:punct:]\s]*\z/)
269
+
270
+ text = cross_reference(string)
271
+ return if text == string
272
+
273
+ text
274
+ end
226
275
  end
@@ -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
 
@@ -250,8 +250,10 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
250
250
  # Adds +table+ to the output
251
251
 
252
252
  def accept_table(header, body, aligns)
253
+ header = header.map { |h| attributes h }
254
+ body = body.map { |row| row.map { |t| attributes t } }
253
255
  widths = header.zip(*body).map do |cols|
254
- cols.map(&:size).max
256
+ cols.map { |col| calculate_text_width(col) }.max
255
257
  end
256
258
  aligns = aligns.map do |a|
257
259
  case a
@@ -264,16 +266,22 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
264
266
  end
265
267
  end
266
268
  @res << header.zip(widths, aligns).map do |h, w, a|
267
- h.__send__(a, w)
269
+ extra_width = h.size - calculate_text_width(h)
270
+ h.__send__(a, w + extra_width)
268
271
  end.join("|").rstrip << "\n"
269
272
  @res << widths.map {|w| "-" * w }.join("|") << "\n"
270
273
  body.each do |row|
271
274
  @res << row.zip(widths, aligns).map do |t, w, a|
272
- t.__send__(a, w)
275
+ extra_width = t.size - calculate_text_width(t)
276
+ t.__send__(a, w + extra_width)
273
277
  end.join("|").rstrip << "\n"
274
278
  end
275
279
  end
276
280
 
281
+ def calculate_text_width(text)
282
+ text.size
283
+ end
284
+
277
285
  ##
278
286
  # Applies attribute-specific markup to +text+ using RDoc::AttributeManager
279
287
 
@@ -70,7 +70,7 @@ class RDoc::Markup::Verbatim < RDoc::Markup::Raw
70
70
 
71
71
  def ruby?
72
72
  @format ||= nil # TODO for older ri data, switch the tree to marshal_dump
73
- @format == :ruby
73
+ @format == :ruby || @format == :rb
74
74
  end
75
75
 
76
76
  ##
data/lib/rdoc/markup.rb CHANGED
@@ -16,7 +16,7 @@
16
16
  #
17
17
  # - +rdoc+:
18
18
  # the +RDoc+ markup format;
19
- # see RDoc::MarkupReference.
19
+ # see {RDoc Markup Reference}[rdoc-ref:doc/markup_reference/rdoc.rdoc]
20
20
  # - +markdown+:
21
21
  # The +markdown+ markup format as described in
22
22
  # the {Markdown Guide}[https://www.markdownguide.org];
@@ -102,7 +102,7 @@
102
102
  #
103
103
  # = \RDoc Markup Reference
104
104
  #
105
- # See RDoc::MarkupReference.
105
+ # See {RDoc Markup Reference}[rdoc-ref:doc/markup_reference/rdoc.rdoc]
106
106
  #
107
107
  #--
108
108
  # Original Author:: Dave Thomas, dave@pragmaticprogrammer.com
@@ -210,6 +210,7 @@ https://github.com/ruby/rdoc/issues
210
210
  autoload :BlankLine, "#{__dir__}/markup/blank_line"
211
211
  autoload :BlockQuote, "#{__dir__}/markup/block_quote"
212
212
  autoload :Document, "#{__dir__}/markup/document"
213
+ autoload :Element, "#{__dir__}/markup/element"
213
214
  autoload :HardBreak, "#{__dir__}/markup/hard_break"
214
215
  autoload :Heading, "#{__dir__}/markup/heading"
215
216
  autoload :Include, "#{__dir__}/markup/include"