rdoc 6.3.4.1 → 8.0.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 (205) hide show
  1. checksums.yaml +4 -4
  2. data/CONTRIBUTING.md +195 -0
  3. data/History.rdoc +65 -63
  4. data/LEGAL.rdoc +7 -1
  5. data/LICENSE.rdoc +6 -0
  6. data/README.md +236 -0
  7. data/RI.md +842 -0
  8. data/TODO.rdoc +8 -7
  9. data/doc/markup_reference/markdown.md +659 -0
  10. data/doc/markup_reference/rdoc.rdoc +1169 -0
  11. data/exe/rdoc +0 -1
  12. data/lib/rdoc/{alias.rb → code_object/alias.rb} +4 -18
  13. data/lib/rdoc/{any_method.rb → code_object/any_method.rb} +50 -24
  14. data/lib/rdoc/{attr.rb → code_object/attr.rb} +16 -14
  15. data/lib/rdoc/{class_module.rb → code_object/class_module.rb} +225 -58
  16. data/lib/rdoc/{constant.rb → code_object/constant.rb} +44 -10
  17. data/lib/rdoc/code_object/context/section.rb +182 -0
  18. data/lib/rdoc/{context.rb → code_object/context.rb} +78 -106
  19. data/lib/rdoc/{extend.rb → code_object/extend.rb} +0 -1
  20. data/lib/rdoc/{include.rb → code_object/include.rb} +0 -1
  21. data/lib/rdoc/{method_attr.rb → code_object/method_attr.rb} +46 -48
  22. data/lib/rdoc/{mixin.rb → code_object/mixin.rb} +6 -4
  23. data/lib/rdoc/{normal_class.rb → code_object/normal_class.rb} +2 -3
  24. data/lib/rdoc/{normal_module.rb → code_object/normal_module.rb} +2 -3
  25. data/lib/rdoc/{require.rb → code_object/require.rb} +1 -2
  26. data/lib/rdoc/{single_class.rb → code_object/single_class.rb} +5 -1
  27. data/lib/rdoc/{top_level.rb → code_object/top_level.rb} +60 -63
  28. data/lib/rdoc/code_object.rb +11 -39
  29. data/lib/rdoc/code_objects.rb +1 -2
  30. data/lib/rdoc/comment.rb +202 -99
  31. data/lib/rdoc/cross_reference.rb +75 -62
  32. data/lib/rdoc/encoding.rb +8 -24
  33. data/lib/rdoc/erb_partial.rb +1 -2
  34. data/lib/rdoc/erbio.rb +3 -8
  35. data/lib/rdoc/generator/aliki.rb +200 -0
  36. data/lib/rdoc/generator/darkfish.rb +156 -122
  37. data/lib/rdoc/generator/json_index.rb +8 -24
  38. data/lib/rdoc/generator/markup.rb +70 -34
  39. data/lib/rdoc/generator/pot/message_extractor.rb +7 -7
  40. data/lib/rdoc/generator/pot/po.rb +1 -1
  41. data/lib/rdoc/generator/pot/po_entry.rb +8 -8
  42. data/lib/rdoc/generator/pot.rb +1 -5
  43. data/lib/rdoc/generator/ri.rb +1 -2
  44. data/lib/rdoc/generator/template/aliki/DESIGN.md +536 -0
  45. data/lib/rdoc/generator/template/aliki/_aside_toc.rhtml +8 -0
  46. data/lib/rdoc/generator/template/aliki/_footer.rhtml +23 -0
  47. data/lib/rdoc/generator/template/aliki/_head.rhtml +163 -0
  48. data/lib/rdoc/generator/template/aliki/_header.rhtml +56 -0
  49. data/lib/rdoc/generator/template/aliki/_icons.rhtml +208 -0
  50. data/lib/rdoc/generator/template/aliki/_sidebar_ancestors.rhtml +16 -0
  51. data/lib/rdoc/generator/template/aliki/_sidebar_classes.rhtml +15 -0
  52. data/lib/rdoc/generator/template/aliki/_sidebar_extends.rhtml +27 -0
  53. data/lib/rdoc/generator/template/aliki/_sidebar_includes.rhtml +27 -0
  54. data/lib/rdoc/generator/template/aliki/_sidebar_installed.rhtml +16 -0
  55. data/lib/rdoc/generator/template/aliki/_sidebar_methods.rhtml +41 -0
  56. data/lib/rdoc/generator/template/aliki/_sidebar_pages.rhtml +67 -0
  57. data/lib/rdoc/generator/template/aliki/_sidebar_search.rhtml +15 -0
  58. data/lib/rdoc/generator/template/aliki/_sidebar_sections.rhtml +21 -0
  59. data/lib/rdoc/generator/template/aliki/_sidebar_toggle.rhtml +3 -0
  60. data/lib/rdoc/generator/template/aliki/class.rhtml +230 -0
  61. data/lib/rdoc/generator/template/aliki/css/rdoc.css +2181 -0
  62. data/lib/rdoc/generator/template/aliki/index.rhtml +22 -0
  63. data/lib/rdoc/generator/template/aliki/js/aliki.js +513 -0
  64. data/lib/rdoc/generator/template/aliki/js/bash_highlighter.js +167 -0
  65. data/lib/rdoc/generator/template/aliki/js/c_highlighter.js +299 -0
  66. data/lib/rdoc/generator/template/aliki/js/search_controller.js +129 -0
  67. data/lib/rdoc/generator/template/aliki/js/search_navigation.js +105 -0
  68. data/lib/rdoc/generator/template/aliki/js/search_ranker.js +239 -0
  69. data/lib/rdoc/generator/template/aliki/js/theme-toggle.js +112 -0
  70. data/lib/rdoc/generator/template/aliki/page.rhtml +18 -0
  71. data/lib/rdoc/generator/template/aliki/servlet_not_found.rhtml +14 -0
  72. data/lib/rdoc/generator/template/aliki/servlet_root.rhtml +65 -0
  73. data/lib/rdoc/generator/template/darkfish/_footer.rhtml +3 -3
  74. data/lib/rdoc/generator/template/darkfish/_head.rhtml +35 -14
  75. data/lib/rdoc/generator/template/darkfish/_sidebar_classes.rhtml +1 -5
  76. data/lib/rdoc/generator/template/darkfish/_sidebar_extends.rhtml +11 -9
  77. data/lib/rdoc/generator/template/darkfish/_sidebar_includes.rhtml +11 -9
  78. data/lib/rdoc/generator/template/darkfish/_sidebar_installed.rhtml +7 -6
  79. data/lib/rdoc/generator/template/darkfish/_sidebar_methods.rhtml +20 -11
  80. data/lib/rdoc/generator/template/darkfish/_sidebar_pages.rhtml +25 -5
  81. data/lib/rdoc/generator/template/darkfish/_sidebar_parent.rhtml +4 -9
  82. data/lib/rdoc/generator/template/darkfish/_sidebar_search.rhtml +2 -1
  83. data/lib/rdoc/generator/template/darkfish/_sidebar_sections.rhtml +4 -4
  84. data/lib/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml +28 -7
  85. data/lib/rdoc/generator/template/darkfish/_sidebar_toggle.rhtml +3 -0
  86. data/lib/rdoc/generator/template/darkfish/class.rhtml +138 -90
  87. data/lib/rdoc/generator/template/darkfish/css/rdoc.css +420 -358
  88. data/lib/rdoc/generator/template/darkfish/fonts/SourceCodePro-Bold.ttf +0 -0
  89. data/lib/rdoc/generator/template/darkfish/fonts/SourceCodePro-Regular.ttf +0 -0
  90. data/lib/rdoc/generator/template/darkfish/index.rhtml +14 -12
  91. data/lib/rdoc/generator/template/darkfish/js/darkfish.js +59 -3
  92. data/lib/rdoc/generator/template/darkfish/js/search.js +15 -5
  93. data/lib/rdoc/generator/template/darkfish/page.rhtml +7 -6
  94. data/lib/rdoc/generator/template/darkfish/servlet_not_found.rhtml +12 -9
  95. data/lib/rdoc/generator/template/darkfish/servlet_root.rhtml +24 -21
  96. data/lib/rdoc/generator/template/darkfish/table_of_contents.rhtml +35 -21
  97. data/lib/rdoc/generator/template/json_index/js/navigation.js +8 -8
  98. data/lib/rdoc/generator/template/json_index/js/searcher.js +48 -6
  99. data/lib/rdoc/generator.rb +6 -5
  100. data/lib/rdoc/i18n/text.rb +3 -3
  101. data/lib/rdoc/i18n.rb +1 -1
  102. data/lib/rdoc/known_classes.rb +5 -4
  103. data/lib/rdoc/markdown/entities.rb +0 -1
  104. data/lib/rdoc/markdown/literals.kpeg +0 -2
  105. data/lib/rdoc/markdown/literals.rb +0 -1
  106. data/lib/rdoc/markdown.kpeg +140 -62
  107. data/lib/rdoc/markdown.rb +1015 -873
  108. data/lib/rdoc/markup/blank_line.rb +25 -24
  109. data/lib/rdoc/markup/block_quote.rb +1 -2
  110. data/lib/rdoc/markup/document.rb +10 -11
  111. data/lib/rdoc/markup/element.rb +21 -0
  112. data/lib/rdoc/markup/formatter.rb +134 -115
  113. data/lib/rdoc/markup/hard_break.rb +30 -28
  114. data/lib/rdoc/markup/heading.rb +163 -72
  115. data/lib/rdoc/markup/include.rb +3 -4
  116. data/lib/rdoc/markup/indented_paragraph.rb +4 -5
  117. data/lib/rdoc/markup/inline_parser.rb +312 -0
  118. data/lib/rdoc/markup/list.rb +6 -7
  119. data/lib/rdoc/markup/list_item.rb +6 -7
  120. data/lib/rdoc/markup/paragraph.rb +2 -3
  121. data/lib/rdoc/markup/parser.rb +29 -19
  122. data/lib/rdoc/markup/pre_process.rb +39 -18
  123. data/lib/rdoc/markup/raw.rb +52 -56
  124. data/lib/rdoc/markup/rule.rb +2 -3
  125. data/lib/rdoc/markup/table.rb +49 -32
  126. data/lib/rdoc/markup/to_ansi.rb +58 -8
  127. data/lib/rdoc/markup/to_bs.rb +47 -38
  128. data/lib/rdoc/markup/to_html.rb +343 -105
  129. data/lib/rdoc/markup/to_html_crossref.rb +143 -58
  130. data/lib/rdoc/markup/to_html_snippet.rb +78 -76
  131. data/lib/rdoc/markup/to_joined_paragraph.rb +3 -9
  132. data/lib/rdoc/markup/to_label.rb +34 -25
  133. data/lib/rdoc/markup/to_markdown.rb +74 -51
  134. data/lib/rdoc/markup/to_rdoc.rb +141 -82
  135. data/lib/rdoc/markup/to_table_of_contents.rb +4 -5
  136. data/lib/rdoc/markup/to_test.rb +10 -3
  137. data/lib/rdoc/markup/to_tt_only.rb +17 -31
  138. data/lib/rdoc/markup/verbatim.rb +5 -6
  139. data/lib/rdoc/markup.rb +59 -707
  140. data/lib/rdoc/options.rb +228 -101
  141. data/lib/rdoc/parser/c.rb +186 -184
  142. data/lib/rdoc/parser/changelog.rb +57 -13
  143. data/lib/rdoc/parser/markdown.rb +0 -2
  144. data/lib/rdoc/parser/rbs.rb +275 -0
  145. data/lib/rdoc/parser/rd.rb +0 -1
  146. data/lib/rdoc/parser/ruby.rb +963 -2024
  147. data/lib/rdoc/parser/ruby_colorizer.rb +253 -0
  148. data/lib/rdoc/parser/simple.rb +6 -23
  149. data/lib/rdoc/parser/text.rb +0 -1
  150. data/lib/rdoc/parser.rb +35 -14
  151. data/lib/rdoc/rbs_helper.rb +186 -0
  152. data/lib/rdoc/rd/block_parser.rb +659 -11
  153. data/lib/rdoc/rd/block_parser.ry +13 -9
  154. data/lib/rdoc/rd/inline.rb +4 -5
  155. data/lib/rdoc/rd/inline_parser.rb +646 -2
  156. data/lib/rdoc/rd.rb +4 -5
  157. data/lib/rdoc/rdoc.rb +234 -90
  158. data/lib/rdoc/ri/driver.rb +172 -172
  159. data/lib/rdoc/ri/paths.rb +3 -3
  160. data/lib/rdoc/{servlet.rb → ri/servlet.rb} +28 -27
  161. data/lib/rdoc/ri/store.rb +0 -1
  162. data/lib/rdoc/ri/task.rb +3 -3
  163. data/lib/rdoc/ri.rb +5 -5
  164. data/lib/rdoc/rubygems_hook.rb +106 -25
  165. data/lib/rdoc/server.rb +460 -0
  166. data/lib/rdoc/stats/normal.rb +1 -1
  167. data/lib/rdoc/stats/quiet.rb +1 -2
  168. data/lib/rdoc/stats/verbose.rb +3 -5
  169. data/lib/rdoc/stats.rb +158 -136
  170. data/lib/rdoc/store.rb +267 -48
  171. data/lib/rdoc/task.rb +54 -28
  172. data/lib/rdoc/text.rb +60 -120
  173. data/lib/rdoc/token_stream.rb +30 -45
  174. data/lib/rdoc/tom_doc.rb +9 -15
  175. data/lib/rdoc/version.rb +3 -1
  176. data/lib/rdoc.rb +90 -52
  177. data/lib/rubygems_plugin.rb +14 -0
  178. data/man/ri.1 +2 -0
  179. data/rdoc-logo.svg +43 -0
  180. data/rdoc.gemspec +25 -200
  181. metadata +120 -53
  182. data/CONTRIBUTING.rdoc +0 -220
  183. data/ExampleMarkdown.md +0 -37
  184. data/ExampleRDoc.rdoc +0 -208
  185. data/Gemfile +0 -12
  186. data/README.rdoc +0 -129
  187. data/RI.rdoc +0 -57
  188. data/Rakefile +0 -101
  189. data/bin/console +0 -7
  190. data/bin/setup +0 -6
  191. data/lib/rdoc/anon_class.rb +0 -11
  192. data/lib/rdoc/context/section.rb +0 -232
  193. data/lib/rdoc/generator/template/darkfish/.document +0 -0
  194. data/lib/rdoc/generator/template/darkfish/_sidebar_VCS_info.rhtml +0 -19
  195. data/lib/rdoc/generator/template/darkfish/_sidebar_in_files.rhtml +0 -9
  196. data/lib/rdoc/generator/template/json_index/.document +0 -1
  197. data/lib/rdoc/ghost_method.rb +0 -7
  198. data/lib/rdoc/markup/attr_changer.rb +0 -23
  199. data/lib/rdoc/markup/attr_span.rb +0 -36
  200. data/lib/rdoc/markup/attribute_manager.rb +0 -409
  201. data/lib/rdoc/markup/attributes.rb +0 -71
  202. data/lib/rdoc/markup/regexp_handling.rb +0 -41
  203. data/lib/rdoc/meta_method.rb +0 -7
  204. data/lib/rdoc/parser/ripper_state_lex.rb +0 -590
  205. data/lib/rdoc/parser/ruby_tools.rb +0 -167
@@ -1,79 +1,170 @@
1
1
  # frozen_string_literal: true
2
- ##
3
- # A heading with a level (1-6) and text
4
2
 
5
- RDoc::Markup::Heading =
6
- Struct.new :level, :text do
7
-
8
- @to_html = nil
9
- @to_label = nil
10
-
11
- ##
12
- # A singleton RDoc::Markup::ToLabel formatter for headings.
13
-
14
- def self.to_label
15
- @to_label ||= RDoc::Markup::ToLabel.new
16
- end
17
-
18
- ##
19
- # A singleton plain HTML formatter for headings. Used for creating labels
20
- # for the Table of Contents
21
-
22
- def self.to_html
23
- return @to_html if @to_html
24
-
25
- markup = RDoc::Markup.new
26
- markup.add_regexp_handling RDoc::CrossReference::CROSSREF_REGEXP, :CROSSREF
27
-
28
- @to_html = RDoc::Markup::ToHtml.new nil
29
-
30
- def @to_html.handle_regexp_CROSSREF target
31
- target.text.sub(/^\\/, '')
3
+ module RDoc
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)
32
30
  end
33
31
 
34
- @to_html
35
- end
36
-
37
- ##
38
- # Calls #accept_heading on +visitor+
39
-
40
- def accept visitor
41
- visitor.accept_heading self
42
- end
43
-
44
- ##
45
- # An HTML-safe anchor reference for this header.
46
-
47
- def aref
48
- "label-#{self.class.to_label.convert text.dup}"
49
- end
50
-
51
- ##
52
- # Creates a fully-qualified label which will include the label from
53
- # +context+. This helps keep ids unique in HTML.
54
-
55
- def label context = nil
56
- label = aref
57
-
58
- label = [context.aref, label].compact.join '-' if
59
- context and context.respond_to? :aref
60
-
61
- label
62
- end
63
-
64
- ##
65
- # HTML markup of the text of this label without the surrounding header
66
- # element.
67
-
68
- def plain_html
69
- self.class.to_html.to_html(text.dup)
70
- end
71
-
72
- def pretty_print q # :nodoc:
73
- q.group 2, "[head: #{level} ", ']' do
74
- q.pp text
32
+ # A heading with a level (1-6) and text
33
+ #
34
+ # RDoc syntax:
35
+ # = Heading 1
36
+ # == Heading 2
37
+ # === Heading 3
38
+ #
39
+ # Markdown syntax:
40
+ # # Heading 1
41
+ # ## Heading 2
42
+ # ### Heading 3
43
+ #
44
+ class Heading
45
+ # A singleton RDoc::Markup::ToLabel formatter for headings.
46
+ #: () -> RDoc::Markup::ToLabel
47
+ def self.to_label
48
+ @to_label ||= Markup::ToLabel.new
49
+ end
50
+
51
+ # A singleton plain HTML formatter for headings. Used for creating labels for the Table of Contents
52
+ #: () -> RDoc::Markup::ToHtml
53
+ def self.to_html
54
+ @to_html ||= begin
55
+ to_html = Markup::ToHtml.new
56
+
57
+ def to_html.handle_regexp_CROSSREF(text)
58
+ text.sub(/^\\/, '')
59
+ end
60
+
61
+ to_html
62
+ end
63
+ end
64
+
65
+ # @override
66
+ #: (untyped) -> void
67
+ def accept(visitor)
68
+ visitor.accept_heading(self)
69
+ end
70
+
71
+ # An HTML-safe anchor reference for this header using GitHub-style formatting:
72
+ # - Lowercase
73
+ # - Spaces converted to hyphens
74
+ # - Special characters removed (except hyphens)
75
+ #
76
+ # Examples:
77
+ # "Hello" -> "hello"
78
+ # "Hello World" -> "hello-world"
79
+ # "Foo Bar Baz" -> "foo-bar-baz"
80
+ #
81
+ #: () -> String
82
+ def aref
83
+ self.class.to_label.convert text.dup
84
+ end
85
+
86
+ # An HTML-safe anchor reference using legacy RDoc formatting:
87
+ # - Prefixed with "label-"
88
+ # - Original case preserved
89
+ # - Spaces converted to + (URL encoding style)
90
+ # - Special characters percent-encoded
91
+ #
92
+ # Returns nil if it would be the same as the GitHub-style aref (no alias needed).
93
+ #
94
+ # Examples:
95
+ # "hello" -> "label-hello" (different due to label- prefix)
96
+ # "Hello" -> "label-Hello"
97
+ # "Hello World" -> "label-Hello+World"
98
+ # "Foo Bar Baz" -> "label-Foo+Bar+Baz"
99
+ #
100
+ #: () -> String?
101
+ def legacy_aref
102
+ "label-#{self.class.to_label.convert_legacy text.dup}"
103
+ end
104
+
105
+ # Creates a fully-qualified label (GitHub-style) which includes the context's aref prefix.
106
+ # This helps keep IDs unique in HTML when headings appear within class/method documentation.
107
+ #
108
+ # Examples (without context):
109
+ # "Hello World" -> "hello-world"
110
+ #
111
+ # Examples (with context being class Foo):
112
+ # "Hello World" -> "class-foo-hello-world"
113
+ #
114
+ # Examples (with context being method #bar):
115
+ # "Hello World" -> "method-i-bar-hello-world"
116
+ #
117
+ #: (RDoc::Context?) -> String
118
+ def label(context = nil)
119
+ result = +""
120
+ result << "#{context.aref}-" if context&.respond_to?(:aref)
121
+ result << aref
122
+ result
123
+ end
124
+
125
+ # Creates a fully-qualified legacy label for backward compatibility.
126
+ # This is used to generate a secondary ID attribute on the heading's inner anchor,
127
+ # allowing old-style links (e.g., #label-Hello+World) to continue working.
128
+ #
129
+ # Examples (without context):
130
+ # "hello" -> "label-hello"
131
+ # "Hello World" -> "label-Hello+World"
132
+ #
133
+ # Examples (with context being class Foo):
134
+ # "hello" -> "class-Foo-label-hello"
135
+ # "Hello World" -> "class-Foo-label-Hello+World"
136
+ #
137
+ #: (RDoc::Context?) -> String
138
+ def legacy_label(context = nil)
139
+ result = +""
140
+ if context&.respond_to?(:legacy_aref)
141
+ result << "#{context.legacy_aref}-"
142
+ elsif context&.respond_to?(:aref)
143
+ result << "#{context.aref}-"
144
+ end
145
+ result << legacy_aref
146
+ result
147
+ end
148
+
149
+ # HTML markup of the text of this label without the surrounding header element.
150
+ #: () -> String
151
+ def plain_html
152
+ no_image_text = text
153
+
154
+ if matched = no_image_text.match(/rdoc-image:[^:]+:(.*)/)
155
+ no_image_text = matched[1]
156
+ end
157
+
158
+ self.class.to_html.to_html(no_image_text)
159
+ end
160
+
161
+ # @override
162
+ #: (PP) -> void
163
+ def pretty_print(q)
164
+ q.group 2, "[head: #{level} ", ']' do
165
+ q.pp text
166
+ end
167
+ end
75
168
  end
76
169
  end
77
-
78
170
  end
79
-
@@ -20,17 +20,17 @@ class RDoc::Markup::Include
20
20
  ##
21
21
  # Creates a new include that will import +file+ from +include_path+
22
22
 
23
- def initialize file, include_path
23
+ def initialize(file, include_path)
24
24
  @file = file
25
25
  @include_path = include_path
26
26
  end
27
27
 
28
- def == other # :nodoc:
28
+ def ==(other) # :nodoc:
29
29
  self.class === other and
30
30
  @file == other.file and @include_path == other.include_path
31
31
  end
32
32
 
33
- def pretty_print q # :nodoc:
33
+ def pretty_print(q) # :nodoc:
34
34
  q.group 2, '[incl ', ']' do
35
35
  q.text file
36
36
  q.breakable
@@ -40,4 +40,3 @@ class RDoc::Markup::Include
40
40
  end
41
41
 
42
42
  end
43
-
@@ -13,20 +13,20 @@ 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)
20
20
  end
21
21
 
22
- def == other # :nodoc:
22
+ def ==(other) # :nodoc:
23
23
  super and indent == other.indent
24
24
  end
25
25
 
26
26
  ##
27
27
  # Calls #accept_indented_paragraph on +visitor+
28
28
 
29
- def accept visitor
29
+ def accept(visitor)
30
30
  visitor.accept_indented_paragraph self
31
31
  end
32
32
 
@@ -34,7 +34,7 @@ class RDoc::Markup::IndentedParagraph < RDoc::Markup::Raw
34
34
  # Joins the raw paragraph text and converts inline HardBreaks to the
35
35
  # +hard_break+ text followed by the indent.
36
36
 
37
- def text hard_break = nil
37
+ def text(hard_break = nil)
38
38
  @parts.map do |part|
39
39
  if RDoc::Markup::HardBreak === part then
40
40
  '%1$s%3$*2$s' % [hard_break, @indent, ' '] if hard_break
@@ -45,4 +45,3 @@ class RDoc::Markup::IndentedParagraph < RDoc::Markup::Raw
45
45
  end
46
46
 
47
47
  end
48
-
@@ -0,0 +1,312 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+ require 'strscan'
5
+
6
+ # Parses inline markup in RDoc text.
7
+ # This parser handles em, bold, strike, tt, hard break, and tidylink.
8
+ # Block-level constructs are handled in RDoc::Markup::Parser.
9
+
10
+ class RDoc::Markup::InlineParser
11
+
12
+ # TT, BOLD_WORD, EM_WORD: regexp-handling(example: crossref) is disabled
13
+ WORD_PAIRS = {
14
+ '*' => :BOLD_WORD,
15
+ '**' => :BOLD_WORD,
16
+ '_' => :EM_WORD,
17
+ '__' => :EM_WORD,
18
+ '+' => :TT,
19
+ '++' => :TT,
20
+ '`' => :TT,
21
+ '``' => :TT
22
+ } # :nodoc:
23
+
24
+ # Other types: regexp-handling(example: crossref) is enabled
25
+ TAGS = {
26
+ 'em' => :EM,
27
+ 'i' => :EM,
28
+ 'b' => :BOLD,
29
+ 's' => :STRIKE,
30
+ 'del' => :STRIKE,
31
+ } # :nodoc:
32
+
33
+ STANDALONE_TAGS = { 'br' => :HARD_BREAK } # :nodoc:
34
+
35
+ CODEBLOCK_TAGS = %w[tt code] # :nodoc:
36
+
37
+ TOKENS = {
38
+ **WORD_PAIRS.transform_values { [:word_pair, nil] },
39
+ **TAGS.keys.to_h {|tag| ["<#{tag}>", [:open_tag, tag]] },
40
+ **TAGS.keys.to_h {|tag| ["</#{tag}>", [:close_tag, tag]] },
41
+ **CODEBLOCK_TAGS.to_h {|tag| ["<#{tag}>", [:code_start, tag]] },
42
+ **STANDALONE_TAGS.keys.to_h {|tag| ["<#{tag}>", [:standalone_tag, tag]] },
43
+ '{' => [:tidylink_start, nil],
44
+ '}' => [:tidylink_mid, nil],
45
+ '\\' => [:escape, nil],
46
+ '[' => nil # To make `label[url]` scan as separate tokens
47
+ } # :nodoc:
48
+
49
+ multi_char_tokens_regexp = Regexp.union(TOKENS.keys.select {|s| s.size > 1 }).source
50
+ token_starts_regexp = TOKENS.keys.map {|s| s[0] }.uniq.map {|s| Regexp.escape(s) }.join
51
+
52
+ SCANNER_REGEXP =
53
+ /(?:
54
+ #{multi_char_tokens_regexp}
55
+ |[^#{token_starts_regexp}\sa-zA-Z0-9\.]+ # chunk of normal text
56
+ |\s+|[a-zA-Z0-9\.]+|.
57
+ )/x # :nodoc:
58
+
59
+ # Characters that can be escaped with backslash.
60
+ ESCAPING_CHARS = '\\*_+`{}[]<>' # :nodoc:
61
+
62
+ # Pattern to match code block content until <code></tt></code> or <tt></code></tt>.
63
+ CODEBLOCK_REGEXPS = CODEBLOCK_TAGS.to_h {|name| [name, /((?:\\.|[^\\])*?)<\/#{name}>/] } # :nodoc:
64
+
65
+ # Word contains alphanumeric and <tt>_./:[]-</tt> characters.
66
+ # Word may start with <tt>#</tt> and may end with any non-space character. (e.g. <tt>#eql?</tt>).
67
+ # Underscore delimiter have special rules.
68
+ WORD_REGEXPS = {
69
+ # Words including _, longest match.
70
+ # Example: `_::A_` `_-42_` `_A::B::C.foo_bar[baz]_` `_kwarg:_`
71
+ # Content must not include _ followed by non-alphanumeric character
72
+ # Example: `_host_:_port_` will be `_host_` + `:` + `_port_`
73
+ '_' => /#?([a-zA-Z0-9.\/:\[\]-]|_+[a-zA-Z0-9])+[^\s]?_(?=[^a-zA-Z0-9_]|\z)/,
74
+ # Words allowing _ but not allowing __
75
+ '__' => /#?[a-zA-Z0-9.\/:\[\]-]*(_[a-zA-Z0-9.\/:\[\]-]+)*[^\s]?__(?=[^a-zA-Z0-9]|\z)/,
76
+ **%w[* ** + ++ ` ``].to_h do |s|
77
+ # normal words that can be used within +word+ or *word*
78
+ [s, /#?[a-zA-Z0-9_.\/:\[\]-]+[^\s]?#{Regexp.escape(s)}(?=[^a-zA-Z0-9]|\z)/]
79
+ end
80
+ } # :nodoc:
81
+
82
+ def initialize(string)
83
+ @scanner = StringScanner.new(string)
84
+ @last_match = nil
85
+ @scanner_negative_cache = Set.new
86
+ @stack = []
87
+ @delimiters = {}
88
+ end
89
+
90
+ # Return the current parsing node on <tt>@stack</tt>.
91
+
92
+ def current
93
+ @stack.last
94
+ end
95
+
96
+ # Parse and return an array of nodes.
97
+ # Node format:
98
+ # {
99
+ # type: :EM | :BOLD | :BOLD_WORD | :EM_WORD | :TT | :STRIKE | :HARD_BREAK | :TIDYLINK,
100
+ # url: string # only for :TIDYLINK
101
+ # children: [string_or_node, ...]
102
+ # }
103
+
104
+ def parse
105
+ stack_push(:root, nil)
106
+ while true
107
+ type, token, value = scan_token
108
+ close = nil
109
+ tidylink_url = nil
110
+ case type
111
+ when :node
112
+ current[:children] << value
113
+ invalidate_open_tidylinks if value[:type] == :TIDYLINK
114
+ when :eof
115
+ close = :root
116
+ when :tidylink_open
117
+ stack_push(:tidylink, token)
118
+ when :tidylink_close
119
+ close = :tidylink
120
+ if value
121
+ tidylink_url = value
122
+ else
123
+ # Tidylink closing brace without URL part. Treat opening and closing braces as normal text
124
+ # `{labelnodes}...` case.
125
+ current[:children] << token
126
+ end
127
+ when :invalidated_tidylink_close
128
+ # `{...{label}[url]...}` case. Nested tidylink invalidates outer one. The last `}` closes the invalidated tidylink.
129
+ current[:children] << token
130
+ close = :invalidated_tidylink
131
+ when :text
132
+ current[:children] << token
133
+ when :open
134
+ stack_push(value, token)
135
+ when :close
136
+ if @delimiters[value]
137
+ close = value
138
+ else
139
+ # closing tag without matching opening tag. Treat as normal text.
140
+ current[:children] << token
141
+ end
142
+ end
143
+
144
+ next unless close
145
+
146
+ while current[:delimiter] != close
147
+ children = current[:children]
148
+ open_token = current[:token]
149
+ stack_pop
150
+ current[:children] << open_token if open_token
151
+ current[:children].concat(children)
152
+ end
153
+
154
+ token = current[:token]
155
+ children = compact_string(current[:children])
156
+ stack_pop
157
+
158
+ return children if close == :root
159
+
160
+ if close == :tidylink || close == :invalidated_tidylink
161
+ if tidylink_url
162
+ current[:children] << { type: :TIDYLINK, children: children, url: tidylink_url }
163
+ invalidate_open_tidylinks
164
+ else
165
+ current[:children] << token
166
+ current[:children].concat(children)
167
+ end
168
+ else
169
+ current[:children] << { type: TAGS[close], children: children }
170
+ end
171
+ end
172
+ end
173
+
174
+ private
175
+
176
+ # When a valid tidylink node is encountered, invalidate all nested tidylinks.
177
+
178
+ def invalidate_open_tidylinks
179
+ return unless @delimiters[:tidylink]
180
+
181
+ @delimiters[:invalidated_tidylink] ||= []
182
+ @delimiters[:tidylink].each do |idx|
183
+ @delimiters[:invalidated_tidylink] << idx
184
+ @stack[idx][:delimiter] = :invalidated_tidylink
185
+ end
186
+ @delimiters.delete(:tidylink)
187
+ end
188
+
189
+ # Pop the top node off the stack when node is closed by a closing delimiter or an error.
190
+
191
+ def stack_pop
192
+ delimiter = current[:delimiter]
193
+ @delimiters[delimiter].pop
194
+ @delimiters.delete(delimiter) if @delimiters[delimiter].empty?
195
+ @stack.pop
196
+ end
197
+
198
+ # Push a new node onto the stack when encountering an opening delimiter.
199
+
200
+ def stack_push(delimiter, token)
201
+ node = { delimiter: delimiter, token: token, children: [] }
202
+ (@delimiters[delimiter] ||= []) << @stack.size
203
+ @stack << node
204
+ end
205
+
206
+ # Compacts adjacent strings in +nodes+ into a single string.
207
+
208
+ def compact_string(nodes)
209
+ nodes.chunk {|e| String === e }.flat_map do |is_str, elems|
210
+ is_str ? elems.join : elems
211
+ end
212
+ end
213
+
214
+ # Scan from StringScanner with +pattern+
215
+ # If +negative_cache+ is true, caches scan failure result. <tt>scan(pattern, negative_cache: true)</tt> return nil when it is called again after a failure.
216
+ # Be careful to use +negative_cache+ with a pattern and position that does not match after previous failure.
217
+
218
+ def strscan(pattern, negative_cache: false)
219
+ return if negative_cache && @scanner_negative_cache.include?(pattern)
220
+
221
+ string = @scanner.scan(pattern)
222
+ @last_match = string if string
223
+ @scanner_negative_cache << pattern if !string && negative_cache
224
+ string
225
+ end
226
+
227
+ # Scan and return the next token for parsing.
228
+ # Returns <tt>[token_type, token_string_or_nil, extra_info]</tt>
229
+
230
+ def scan_token
231
+ last_match = @last_match
232
+ token = strscan(SCANNER_REGEXP)
233
+ type, name = TOKENS[token]
234
+
235
+ case type
236
+ when :word_pair
237
+ # If the character before word pair delimiter is alphanumeric, do not treat as word pair.
238
+ word_pair = strscan(WORD_REGEXPS[token]) unless /[a-zA-Z0-9]\z/.match?(last_match)
239
+
240
+ if word_pair.nil?
241
+ [:text, token, nil]
242
+ elsif token == '__' && word_pair.match?(/\A[a-zA-Z]+__\z/)
243
+ # Special exception: __FILE__, __LINE__, __send__ should be treated as normal text.
244
+ [:text, "#{token}#{word_pair}", nil]
245
+ else
246
+ [:node, nil, { type: WORD_PAIRS[token], children: [word_pair.delete_suffix(token)] }]
247
+ end
248
+ when :open_tag
249
+ [:open, token, name]
250
+ when :close_tag
251
+ [:close, token, name]
252
+ when :code_start
253
+ if (codeblock = strscan(CODEBLOCK_REGEXPS[name], negative_cache: true))
254
+ # Need to unescape `\\` and `\<`.
255
+ # RDoc also unescapes backslash + word separators, but this is not really necessary.
256
+ content = codeblock.delete_suffix("</#{name}>").gsub(/\\(.)/) { '\\<*+_`'.include?($1) ? $1 : $& }
257
+ [:node, nil, { type: :TT, children: content.empty? ? [] : [content] }]
258
+ else
259
+ [:text, token, nil]
260
+ end
261
+ when :standalone_tag
262
+ [:node, nil, { type: STANDALONE_TAGS[name], children: [] }]
263
+ when :tidylink_start
264
+ [:tidylink_open, token, nil]
265
+ when :tidylink_mid
266
+ if @delimiters[:tidylink]
267
+ if (url = read_tidylink_url)
268
+ [:tidylink_close, nil, url]
269
+ else
270
+ [:tidylink_close, token, nil]
271
+ end
272
+ elsif @delimiters[:invalidated_tidylink]
273
+ [:invalidated_tidylink_close, token, nil]
274
+ else
275
+ [:text, token, nil]
276
+ end
277
+ when :escape
278
+ next_char = strscan(/./)
279
+ if next_char.nil?
280
+ # backslash at end of string
281
+ [:text, '\\', nil]
282
+ elsif next_char && ESCAPING_CHARS.include?(next_char)
283
+ # escaped character
284
+ [:text, next_char, nil]
285
+ else
286
+ # If next_char not an escaping character, it is treated as text token with backslash + next_char
287
+ # For example, backslash of `\Ruby` (suppressed crossref) remains.
288
+ [:text, "\\#{next_char}", nil]
289
+ end
290
+ else
291
+ if token.nil?
292
+ [:eof, nil, nil]
293
+ elsif token.match?(/\A[A-Za-z0-9]*\z/) && (url = read_tidylink_url)
294
+ # Simplified tidylink: label[url]
295
+ [:node, nil, { type: :TIDYLINK, children: [token], url: url }]
296
+ else
297
+ [:text, token, nil]
298
+ end
299
+ end
300
+ end
301
+
302
+ # Read the URL part of a tidylink from the current position.
303
+ # Returns nil if no valid URL part is found.
304
+ # URL part is enclosed in square brackets and may contain escaped brackets.
305
+ # Example: <tt>[http://example.com/?q=\[\]]</tt> represents <tt>http://example.com/?q=[]</tt>.
306
+ # If we're accepting rdoc-style links in markdown, url may include <tt>*+<_</tt> with backslash escape.
307
+
308
+ def read_tidylink_url
309
+ bracketed_url = strscan(/\[([^\s\[\]\\]|\\[\[\]\\*+<_])+\]/)
310
+ bracketed_url[1...-1].gsub(/\\(.)/, '\1') if bracketed_url
311
+ end
312
+ end
@@ -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
@@ -46,11 +46,11 @@ class RDoc::Markup::List
46
46
  ##
47
47
  # Appends +item+ to the list
48
48
 
49
- def << item
49
+ def <<(item)
50
50
  @items << item
51
51
  end
52
52
 
53
- def == other # :nodoc:
53
+ def ==(other) # :nodoc:
54
54
  self.class == other.class and
55
55
  @type == other.type and
56
56
  @items == other.items
@@ -59,7 +59,7 @@ class RDoc::Markup::List
59
59
  ##
60
60
  # Runs this list and all its #items through +visitor+
61
61
 
62
- def accept visitor
62
+ def accept(visitor)
63
63
  visitor.accept_list_start self
64
64
 
65
65
  @items.each do |item|
@@ -83,7 +83,7 @@ class RDoc::Markup::List
83
83
  @items.last
84
84
  end
85
85
 
86
- def pretty_print q # :nodoc:
86
+ def pretty_print(q) # :nodoc:
87
87
  q.group 2, "[list: #{@type} ", ']' do
88
88
  q.seplist @items do |item|
89
89
  q.pp item
@@ -94,9 +94,8 @@ 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
 
101
101
  end
102
-