rdoc 7.0.3 → 7.2.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 (54) 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/attr.rb +2 -1
  6. data/lib/rdoc/code_object/class_module.rb +24 -3
  7. data/lib/rdoc/code_object/context/section.rb +46 -9
  8. data/lib/rdoc/code_object/context.rb +15 -4
  9. data/lib/rdoc/code_object/mixin.rb +3 -0
  10. data/lib/rdoc/code_object/top_level.rb +2 -0
  11. data/lib/rdoc/comment.rb +1 -1
  12. data/lib/rdoc/cross_reference.rb +31 -24
  13. data/lib/rdoc/generator/template/aliki/_head.rhtml +5 -0
  14. data/lib/rdoc/generator/template/aliki/class.rhtml +8 -6
  15. data/lib/rdoc/generator/template/aliki/css/rdoc.css +48 -36
  16. data/lib/rdoc/generator/template/aliki/js/aliki.js +8 -2
  17. data/lib/rdoc/generator/template/aliki/js/bash_highlighter.js +167 -0
  18. data/lib/rdoc/generator/template/aliki/js/c_highlighter.js +1 -1
  19. data/lib/rdoc/generator/template/aliki/js/search_controller.js +1 -1
  20. data/lib/rdoc/generator/template/darkfish/class.rhtml +2 -0
  21. data/lib/rdoc/generator/template/darkfish/css/rdoc.css +19 -0
  22. data/lib/rdoc/markdown.kpeg +22 -12
  23. data/lib/rdoc/markdown.rb +36 -26
  24. data/lib/rdoc/markup/formatter.rb +129 -106
  25. data/lib/rdoc/markup/heading.rb +101 -29
  26. data/lib/rdoc/markup/inline_parser.rb +312 -0
  27. data/lib/rdoc/markup/parser.rb +1 -1
  28. data/lib/rdoc/markup/to_ansi.rb +51 -4
  29. data/lib/rdoc/markup/to_bs.rb +22 -42
  30. data/lib/rdoc/markup/to_html.rb +178 -183
  31. data/lib/rdoc/markup/to_html_crossref.rb +58 -79
  32. data/lib/rdoc/markup/to_html_snippet.rb +62 -62
  33. data/lib/rdoc/markup/to_label.rb +29 -20
  34. data/lib/rdoc/markup/to_markdown.rb +61 -37
  35. data/lib/rdoc/markup/to_rdoc.rb +86 -26
  36. data/lib/rdoc/markup/to_test.rb +9 -1
  37. data/lib/rdoc/markup/to_tt_only.rb +10 -16
  38. data/lib/rdoc/markup/verbatim.rb +1 -1
  39. data/lib/rdoc/markup.rb +10 -32
  40. data/lib/rdoc/parser/changelog.rb +29 -0
  41. data/lib/rdoc/parser/prism_ruby.rb +44 -32
  42. data/lib/rdoc/parser/ruby.rb +1 -1
  43. data/lib/rdoc/text.rb +44 -5
  44. data/lib/rdoc/token_stream.rb +4 -8
  45. data/lib/rdoc/version.rb +1 -1
  46. data/rdoc.gemspec +2 -2
  47. metadata +7 -12
  48. data/ExampleMarkdown.md +0 -39
  49. data/ExampleRDoc.rdoc +0 -210
  50. data/lib/rdoc/markup/attr_changer.rb +0 -22
  51. data/lib/rdoc/markup/attr_span.rb +0 -35
  52. data/lib/rdoc/markup/attribute_manager.rb +0 -405
  53. data/lib/rdoc/markup/attributes.rb +0 -70
  54. data/lib/rdoc/markup/regexp_handling.rb +0 -40
@@ -10,6 +10,8 @@
10
10
  # RDoc::Markup::FormatterTestCase. If you're writing a text-output formatter
11
11
  # use RDoc::Markup::TextFormatterTestCase which provides extra test cases.
12
12
 
13
+ require 'rdoc/markup/inline_parser'
14
+
13
15
  class RDoc::Markup::Formatter
14
16
 
15
17
  ##
@@ -18,6 +20,7 @@ class RDoc::Markup::Formatter
18
20
 
19
21
  InlineTag = Struct.new(:bit, :on, :off)
20
22
 
23
+
21
24
  ##
22
25
  # Converts a target url to one that is relative to a given path
23
26
 
@@ -49,17 +52,7 @@ class RDoc::Markup::Formatter
49
52
  @options = options
50
53
 
51
54
  @markup = markup || RDoc::Markup.new
52
- @am = @markup.attribute_manager
53
- @am.add_regexp_handling(/<br>/, :HARD_BREAK)
54
-
55
- @attributes = @am.attributes
56
-
57
- @attr_tags = []
58
-
59
- @in_tt = 0
60
- @tt_bit = @attributes.bitmap_for :TT
61
55
 
62
- @hard_break = ''
63
56
  @from_path = '.'
64
57
  end
65
58
 
@@ -84,29 +77,6 @@ class RDoc::Markup::Formatter
84
77
  @markup.add_regexp_handling(/rdoc-[a-z]+:[^\s\]]+/, :RDOCLINK)
85
78
  end
86
79
 
87
- ##
88
- # Adds a regexp handling for links of the form {<text>}[<url>] and
89
- # <word>[<url>]
90
-
91
- def add_regexp_handling_TIDYLINK
92
- @markup.add_regexp_handling(/(?:
93
- \{[^{}]*\} | # multi-word label
94
- \b[^\s{}]+? # single-word label
95
- )
96
-
97
- \[\S+?\] # link target
98
- /x, :TIDYLINK)
99
- end
100
-
101
- ##
102
- # Add a new set of tags for an attribute. We allow separate start and end
103
- # tags for flexibility
104
-
105
- def add_tag(name, start, stop)
106
- attr = @attributes.bitmap_for name
107
- @attr_tags << InlineTag.new(attr, start, stop)
108
- end
109
-
110
80
  ##
111
81
  # Allows +tag+ to be decorated with additional information.
112
82
 
@@ -121,117 +91,170 @@ class RDoc::Markup::Formatter
121
91
  @markup.convert content, self
122
92
  end
123
93
 
124
- ##
125
- # Converts flow items +flow+
126
-
127
- def convert_flow(flow)
128
- res = []
129
-
130
- flow.each do |item|
131
- case item
132
- when String then
133
- res << convert_string(item)
134
- when RDoc::Markup::AttrChanger then
135
- off_tags res, item
136
- on_tags res, item
137
- when RDoc::Markup::RegexpHandling then
138
- res << convert_regexp_handling(item)
94
+ # Applies regexp handling to +text+ and returns an array of [text, converted?] pairs.
95
+
96
+ def apply_regexp_handling(text)
97
+ output = []
98
+ start = 0
99
+ loop do
100
+ pos = text.size
101
+ matched_name = matched_text = nil
102
+ @markup.regexp_handlings.each do |pattern, name|
103
+ m = text.match(pattern, start)
104
+ next unless m
105
+ idx = m[1] ? 1 : 0
106
+ if m.begin(idx) < pos
107
+ pos = m.begin(idx)
108
+ matched_text = m[idx]
109
+ matched_name = name
110
+ end
111
+ end
112
+ output << [text[start...pos], false] if pos > start
113
+ if matched_name
114
+ handled = public_send(:"handle_regexp_#{matched_name}", matched_text)
115
+ output << [handled, true]
116
+ start = pos + matched_text.size
139
117
  else
140
- raise "Unknown flow element: #{item.inspect}"
118
+ start = pos
141
119
  end
120
+ break if pos == text.size
142
121
  end
143
-
144
- res.join
122
+ output
145
123
  end
146
124
 
147
- ##
148
- # Converts added regexp handlings. See RDoc::Markup#add_regexp_handling
125
+ # Called when processing plain text while traversing inline nodes from handle_inline.
126
+ # +text+ may need proper escaping.
149
127
 
150
- def convert_regexp_handling(target)
151
- return target.text if in_tt?
128
+ def handle_PLAIN_TEXT(text)
129
+ end
152
130
 
153
- handled = false
131
+ # Called when processing regexp-handling-processed text while traversing inline nodes from handle_inline.
132
+ # +text+ may contain markup tags.
154
133
 
155
- @attributes.each_name_of target.type do |name|
156
- method_name = "handle_regexp_#{name}"
134
+ def handle_REGEXP_HANDLING_TEXT(text)
135
+ end
136
+
137
+ # Called when processing text node while traversing inline nodes from handle_inline.
138
+ # Apply regexp handling and dispatch to the appropriate handler: handle_REGEXP_HANDLING_TEXT or handle_PLAIN_TEXT.
157
139
 
158
- if respond_to? method_name then
159
- target.text = public_send method_name, target
160
- handled = true
140
+ def handle_TEXT(text)
141
+ apply_regexp_handling(text).each do |part, converted|
142
+ if converted
143
+ handle_REGEXP_HANDLING_TEXT(part)
144
+ else
145
+ handle_PLAIN_TEXT(part)
161
146
  end
162
147
  end
148
+ end
163
149
 
164
- unless handled then
165
- target_name = @attributes.as_string target.type
150
+ # Called when processing a hard break while traversing inline nodes from handle_inline.
166
151
 
167
- raise RDoc::Error, "Unhandled regexp handling #{target_name}: #{target}"
168
- end
152
+ def handle_HARD_BREAK
153
+ end
169
154
 
170
- target.text
155
+ # Called when processing bold nodes while traversing inline nodes from handle_inline.
156
+ # Traverse the children nodes and dispatch to the appropriate handlers.
157
+
158
+ def handle_BOLD(nodes)
159
+ traverse_inline_nodes(nodes)
171
160
  end
172
161
 
173
- ##
174
- # Converts a string to be fancier if desired
162
+ # Called when processing emphasis nodes while traversing inline nodes from handle_inline.
163
+ # Traverse the children nodes and dispatch to the appropriate handlers.
175
164
 
176
- def convert_string(string)
177
- string
165
+ def handle_EM(nodes)
166
+ traverse_inline_nodes(nodes)
178
167
  end
179
168
 
180
- ##
181
- # Use ignore in your subclass to ignore the content of a node.
182
- #
183
- # ##
184
- # # We don't support raw nodes in ToNoRaw
185
- #
186
- # alias accept_raw ignore
169
+ # Called when processing bold word nodes while traversing inline nodes from handle_inline.
170
+ # +word+ may need proper escaping.
187
171
 
188
- def ignore *node
172
+ def handle_BOLD_WORD(word)
173
+ handle_PLAIN_TEXT(word)
189
174
  end
190
175
 
191
- ##
192
- # Are we currently inside tt tags?
176
+ # Called when processing emphasis word nodes while traversing inline nodes from handle_inline.
177
+ # +word+ may need proper escaping.
193
178
 
194
- def in_tt?
195
- @in_tt > 0
179
+ def handle_EM_WORD(word)
180
+ handle_PLAIN_TEXT(word)
196
181
  end
197
182
 
198
- def tt_tag?(attr_mask, reverse = false)
199
- each_attr_tag(attr_mask, reverse) do |tag|
200
- return true if tt? tag
201
- end
202
- false
183
+ # Called when processing tt nodes while traversing inline nodes from handle_inline.
184
+ # +code+ may need proper escaping.
185
+
186
+ def handle_TT(code)
187
+ handle_PLAIN_TEXT(code)
203
188
  end
204
189
 
205
- ##
206
- # Turns on tags for +item+ on +res+
190
+ # Called when processing strike nodes while traversing inline nodes from handle_inline.
191
+ # Traverse the children nodes and dispatch to the appropriate handlers.
207
192
 
208
- def on_tags(res, item)
209
- each_attr_tag(item.turn_on) do |tag|
210
- res << annotate(tag.on)
211
- @in_tt += 1 if tt? tag
212
- end
193
+ def handle_STRIKE(nodes)
194
+ traverse_inline_nodes(nodes)
213
195
  end
214
196
 
215
- ##
216
- # Turns off tags for +item+ on +res+
197
+ # Called when processing tidylink nodes while traversing inline nodes from handle_inline.
198
+ # +label_part+ is an array of strings or nodes representing the link label.
199
+ # +url+ is the link URL.
200
+ # Traverse the label_part nodes and dispatch to the appropriate handlers.
217
201
 
218
- def off_tags(res, item)
219
- each_attr_tag(item.turn_off, true) do |tag|
220
- @in_tt -= 1 if tt? tag
221
- res << annotate(tag.off)
222
- end
202
+ def handle_TIDYLINK(label_part, url)
203
+ traverse_inline_nodes(label_part)
223
204
  end
224
205
 
225
- def each_attr_tag(attr_mask, reverse = false)
226
- return if attr_mask.zero?
206
+ # Parses inline +text+, traverse the resulting nodes, and calls the appropriate handler methods.
207
+
208
+ def handle_inline(text)
209
+ nodes = RDoc::Markup::InlineParser.new(text).parse
210
+ traverse_inline_nodes(nodes)
211
+ end
227
212
 
228
- @attr_tags.public_send(reverse ? :reverse_each : :each) do |tag|
229
- if attr_mask & tag.bit != 0 then
230
- yield tag
213
+ # Traverses +nodes+ and calls the appropriate handler methods
214
+ # Nodes formats are described in RDoc::Markup::InlineParser#parse
215
+
216
+ def traverse_inline_nodes(nodes)
217
+ nodes.each do |node|
218
+ next handle_TEXT(node) if String === node
219
+ case node[:type]
220
+ when :TIDYLINK
221
+ handle_TIDYLINK(node[:children], node[:url])
222
+ when :HARD_BREAK
223
+ handle_HARD_BREAK
224
+ when :BOLD
225
+ handle_BOLD(node[:children])
226
+ when :BOLD_WORD
227
+ handle_BOLD_WORD(node[:children][0] || '')
228
+ when :EM
229
+ handle_EM(node[:children])
230
+ when :EM_WORD
231
+ handle_EM_WORD(node[:children][0] || '')
232
+ when :TT
233
+ handle_TT(node[:children][0] || '')
234
+ when :STRIKE
235
+ handle_STRIKE(node[:children])
231
236
  end
232
237
  end
233
238
  end
234
239
 
240
+ ##
241
+ # Converts a string to be fancier if desired
242
+
243
+ def convert_string(string)
244
+ string
245
+ end
246
+
247
+ ##
248
+ # Use ignore in your subclass to ignore the content of a node.
249
+ #
250
+ # ##
251
+ # # We don't support raw nodes in ToNoRaw
252
+ #
253
+ # alias accept_raw ignore
254
+
255
+ def ignore *node
256
+ end
257
+
235
258
  ##
236
259
  # Extracts and a scheme, url and an anchor id from +url+ and returns them.
237
260
 
@@ -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
@@ -35,46 +57,96 @@ module RDoc
35
57
 
36
58
  to_html = Markup::ToHtml.new nil
37
59
 
38
- def to_html.handle_regexp_CROSSREF(target)
39
- target.text.sub(/^\\/, '')
60
+ def to_html.handle_regexp_CROSSREF(text)
61
+ text.sub(/^\\/, '')
40
62
  end
41
63
 
42
64
  to_html
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.