rdoc 7.2.0 → 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 (101) hide show
  1. checksums.yaml +4 -4
  2. data/CONTRIBUTING.md +3 -4
  3. data/LICENSE.rdoc +4 -0
  4. data/README.md +43 -2
  5. data/doc/markup_reference/markdown.md +104 -3
  6. data/lib/rdoc/code_object/alias.rb +2 -8
  7. data/lib/rdoc/code_object/any_method.rb +11 -6
  8. data/lib/rdoc/code_object/attr.rb +11 -6
  9. data/lib/rdoc/code_object/class_module.rb +62 -32
  10. data/lib/rdoc/code_object/constant.rb +29 -3
  11. data/lib/rdoc/code_object/context/section.rb +4 -35
  12. data/lib/rdoc/code_object/context.rb +39 -34
  13. data/lib/rdoc/code_object/method_attr.rb +9 -15
  14. data/lib/rdoc/code_object/mixin.rb +2 -2
  15. data/lib/rdoc/code_object/top_level.rb +9 -3
  16. data/lib/rdoc/code_object.rb +2 -4
  17. data/lib/rdoc/comment.rb +0 -65
  18. data/lib/rdoc/cross_reference.rb +7 -27
  19. data/lib/rdoc/encoding.rb +3 -3
  20. data/lib/rdoc/generator/aliki.rb +17 -0
  21. data/lib/rdoc/generator/darkfish.rb +12 -6
  22. data/lib/rdoc/generator/json_index.rb +2 -2
  23. data/lib/rdoc/generator/markup.rb +56 -31
  24. data/lib/rdoc/generator/template/aliki/DESIGN.md +536 -0
  25. data/lib/rdoc/generator/template/aliki/_aside_toc.rhtml +1 -1
  26. data/lib/rdoc/generator/template/aliki/_head.rhtml +1 -1
  27. data/lib/rdoc/generator/template/aliki/_sidebar_extends.rhtml +8 -6
  28. data/lib/rdoc/generator/template/aliki/_sidebar_includes.rhtml +8 -6
  29. data/lib/rdoc/generator/template/aliki/_sidebar_installed.rhtml +1 -1
  30. data/lib/rdoc/generator/template/aliki/_sidebar_pages.rhtml +2 -2
  31. data/lib/rdoc/generator/template/aliki/_sidebar_sections.rhtml +1 -1
  32. data/lib/rdoc/generator/template/aliki/_sidebar_toggle.rhtml +1 -1
  33. data/lib/rdoc/generator/template/aliki/class.rhtml +56 -46
  34. data/lib/rdoc/generator/template/aliki/css/rdoc.css +337 -111
  35. data/lib/rdoc/generator/template/aliki/index.rhtml +1 -1
  36. data/lib/rdoc/generator/template/aliki/js/aliki.js +20 -18
  37. data/lib/rdoc/generator/template/aliki/page.rhtml +1 -1
  38. data/lib/rdoc/generator/template/aliki/servlet_not_found.rhtml +1 -1
  39. data/lib/rdoc/generator/template/aliki/servlet_root.rhtml +2 -2
  40. data/lib/rdoc/generator/template/darkfish/_sidebar_extends.rhtml +8 -6
  41. data/lib/rdoc/generator/template/darkfish/_sidebar_includes.rhtml +8 -6
  42. data/lib/rdoc/generator/template/darkfish/_sidebar_installed.rhtml +1 -1
  43. data/lib/rdoc/generator/template/darkfish/_sidebar_pages.rhtml +1 -1
  44. data/lib/rdoc/generator/template/darkfish/_sidebar_sections.rhtml +1 -1
  45. data/lib/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml +5 -5
  46. data/lib/rdoc/generator/template/darkfish/class.rhtml +18 -21
  47. data/lib/rdoc/generator/template/darkfish/css/rdoc.css +0 -1
  48. data/lib/rdoc/generator/template/darkfish/table_of_contents.rhtml +3 -3
  49. data/lib/rdoc/i18n/text.rb +3 -3
  50. data/lib/rdoc/markdown.kpeg +15 -10
  51. data/lib/rdoc/markdown.rb +289 -104
  52. data/lib/rdoc/markup/document.rb +2 -2
  53. data/lib/rdoc/markup/formatter.rb +24 -34
  54. data/lib/rdoc/markup/heading.rb +1 -4
  55. data/lib/rdoc/markup/indented_paragraph.rb +1 -1
  56. data/lib/rdoc/markup/list.rb +2 -2
  57. data/lib/rdoc/markup/list_item.rb +2 -2
  58. data/lib/rdoc/markup/pre_process.rb +0 -25
  59. data/lib/rdoc/markup/to_ansi.rb +1 -1
  60. data/lib/rdoc/markup/to_bs.rb +1 -1
  61. data/lib/rdoc/markup/to_html.rb +131 -53
  62. data/lib/rdoc/markup/to_html_crossref.rb +97 -71
  63. data/lib/rdoc/markup/to_html_snippet.rb +5 -5
  64. data/lib/rdoc/markup/to_joined_paragraph.rb +0 -5
  65. data/lib/rdoc/markup/to_label.rb +2 -2
  66. data/lib/rdoc/markup/to_markdown.rb +1 -1
  67. data/lib/rdoc/markup/to_rdoc.rb +2 -2
  68. data/lib/rdoc/markup/to_table_of_contents.rb +1 -1
  69. data/lib/rdoc/markup/to_tt_only.rb +0 -7
  70. data/lib/rdoc/markup/verbatim.rb +1 -1
  71. data/lib/rdoc/options.rb +36 -51
  72. data/lib/rdoc/parser/c.rb +7 -6
  73. data/lib/rdoc/parser/rbs.rb +275 -0
  74. data/lib/rdoc/parser/ruby.rb +954 -2066
  75. data/lib/rdoc/parser/ruby_colorizer.rb +253 -0
  76. data/lib/rdoc/parser.rb +3 -2
  77. data/lib/rdoc/rbs_helper.rb +186 -0
  78. data/lib/rdoc/rdoc.rb +196 -24
  79. data/lib/rdoc/ri/driver.rb +8 -2
  80. data/lib/rdoc/ri/paths.rb +1 -1
  81. data/lib/rdoc/{servlet.rb → ri/servlet.rb} +5 -5
  82. data/lib/rdoc/ri.rb +4 -3
  83. data/lib/rdoc/rubygems_hook.rb +11 -11
  84. data/lib/rdoc/server.rb +460 -0
  85. data/lib/rdoc/stats.rb +147 -124
  86. data/lib/rdoc/store.rb +212 -4
  87. data/lib/rdoc/task.rb +16 -15
  88. data/lib/rdoc/text.rb +1 -118
  89. data/lib/rdoc/token_stream.rb +11 -33
  90. data/lib/rdoc/version.rb +1 -1
  91. data/lib/rdoc.rb +35 -7
  92. data/lib/rubygems_plugin.rb +2 -11
  93. data/rdoc-logo.svg +43 -0
  94. data/rdoc.gemspec +6 -4
  95. metadata +35 -18
  96. data/lib/rdoc/code_object/anon_class.rb +0 -10
  97. data/lib/rdoc/code_object/ghost_method.rb +0 -6
  98. data/lib/rdoc/code_object/meta_method.rb +0 -6
  99. data/lib/rdoc/parser/prism_ruby.rb +0 -1112
  100. data/lib/rdoc/parser/ripper_state_lex.rb +0 -302
  101. data/lib/rdoc/parser/ruby_tools.rb +0 -163
data/lib/rdoc/stats.rb CHANGED
@@ -7,6 +7,19 @@ class RDoc::Stats
7
7
 
8
8
  include RDoc::Text
9
9
 
10
+ ##
11
+ # Display order for item types in the coverage report
12
+
13
+ TYPE_ORDER = %w[Class Module Constant Attribute Method].freeze
14
+
15
+ ##
16
+ # Message displayed when all items are documented
17
+
18
+ GREAT_JOB_MESSAGE = <<~MSG
19
+ 100% documentation!
20
+ Great Job!
21
+ MSG
22
+
10
23
  ##
11
24
  # Output level for the coverage report
12
25
 
@@ -39,10 +52,17 @@ class RDoc::Stats
39
52
  @start = Time.now
40
53
  @undoc_params = 0
41
54
 
55
+ self.verbosity = verbosity
56
+ end
57
+
58
+ ##
59
+ # Sets the verbosity level, rebuilding the display outputter.
60
+
61
+ def verbosity=(verbosity)
42
62
  @display = case verbosity
43
- when 0 then Quiet.new num_files
44
- when 1 then Normal.new num_files
45
- else Verbose.new num_files
63
+ when 0 then Quiet.new @num_files
64
+ when 1 then Normal.new @num_files
65
+ else Verbose.new @num_files
46
66
  end
47
67
  end
48
68
 
@@ -186,18 +206,6 @@ class RDoc::Stats
186
206
  @fully_documented
187
207
  end
188
208
 
189
- ##
190
- # A report that says you did a great job!
191
-
192
- def great_job
193
- report = RDoc::Markup::Document.new
194
-
195
- report << RDoc::Markup::Paragraph.new('100% documentation!')
196
- report << RDoc::Markup::Paragraph.new('Great Job!')
197
-
198
- report
199
- end
200
-
201
209
  ##
202
210
  # Calculates the percentage of items documented.
203
211
 
@@ -223,48 +231,55 @@ class RDoc::Stats
223
231
  if @coverage_level.zero? then
224
232
  calculate
225
233
 
226
- return great_job if @num_items == @doc_items
234
+ return GREAT_JOB_MESSAGE if @num_items == @doc_items
227
235
  end
228
236
 
229
- ucm = @store.unique_classes_and_modules
237
+ items, empty_classes = collect_undocumented_items
230
238
 
231
- report = RDoc::Markup::Document.new
232
- report << RDoc::Markup::Paragraph.new('The following items are not documented:')
233
- report << RDoc::Markup::BlankLine.new
234
-
235
- ucm.sort.each do |cm|
236
- body = report_class_module(cm) {
237
- [
238
- report_constants(cm),
239
- report_attributes(cm),
240
- report_methods(cm),
241
- ].compact
242
- }
239
+ if @coverage_level > 0 then
240
+ calculate
243
241
 
244
- report << body if body
242
+ return GREAT_JOB_MESSAGE if @num_items == @doc_items
245
243
  end
246
244
 
247
- if @coverage_level > 0 then
248
- calculate
245
+ report = +""
246
+ report << "The following items are not documented:\n\n"
249
247
 
250
- return great_job if @num_items == @doc_items
248
+ # Referenced-but-empty classes
249
+ empty_classes.each do |cm|
250
+ report << "#{cm.full_name} is referenced but empty.\n"
251
+ report << "It probably came from another project. I'm sorry I'm holding it against you.\n\n"
251
252
  end
252
253
 
253
- report
254
- end
254
+ # Group items by file, then by type
255
+ by_file = items.group_by { |item| item[:file] }
255
256
 
256
- ##
257
- # Returns a report on undocumented attributes in ClassModule +cm+
257
+ by_file.sort_by { |file, _| file }.each do |file, file_items|
258
+ report << "#{file}:\n"
258
259
 
259
- def report_attributes(cm)
260
- return if cm.attributes.empty?
260
+ by_type = file_items.group_by { |item| item[:type] }
261
261
 
262
- report = []
262
+ TYPE_ORDER.each do |type|
263
+ next unless by_type[type]
264
+
265
+ report << " #{type}:\n"
266
+
267
+ sorted = by_type[type].sort_by { |item| [item[:line] || 0, item[:name]] }
268
+ name_width = sorted.reduce(0) { |max, item| item[:line] && item[:name].length > max ? item[:name].length : max }
269
+
270
+ sorted.each do |item|
271
+ if item[:line]
272
+ report << " %-*s %s:%d\n" % [name_width, item[:name], item[:file], item[:line]]
273
+ else
274
+ report << " #{item[:name]}\n"
275
+ end
276
+
277
+ if item[:undoc_params]
278
+ report << " Undocumented params: #{item[:undoc_params].join(', ')}\n"
279
+ end
280
+ end
281
+ end
263
282
 
264
- cm.attributes.each do |attr|
265
- next if attr.documented?
266
- line = attr.line ? ":#{attr.line}" : nil
267
- report << " #{attr.definition} :#{attr.name} # in file #{attr.file.full_name}#{line}\n"
268
283
  report << "\n"
269
284
  end
270
285
 
@@ -272,115 +287,128 @@ class RDoc::Stats
272
287
  end
273
288
 
274
289
  ##
275
- # Returns a report on undocumented items in ClassModule +cm+
276
-
277
- def report_class_module(cm)
278
- return if cm.fully_documented? and @coverage_level.zero?
279
- return unless cm.display?
280
-
281
- report = RDoc::Markup::Document.new
290
+ # Collects all undocumented items across all classes and modules.
291
+ # Returns [items, empty_classes] where items is an Array of Hashes
292
+ # with keys :type, :name, :file, :line, and empty_classes is an
293
+ # Array of ClassModule objects that are referenced but have no files.
282
294
 
283
- if cm.in_files.empty? then
284
- report << RDoc::Markup::Paragraph.new("#{cm.definition} is referenced but empty.")
285
- report << RDoc::Markup::Paragraph.new("It probably came from another project. I'm sorry I'm holding it against you.")
295
+ def collect_undocumented_items
296
+ empty_classes = []
297
+ items = []
286
298
 
287
- return report
288
- elsif cm.documented? then
289
- documented = true
290
- klass = RDoc::Markup::Verbatim.new("#{cm.definition} # is documented\n")
291
- else
292
- report << RDoc::Markup::Paragraph.new('In files:')
299
+ @store.unique_classes_and_modules.each do |class_module|
300
+ next unless class_module.display?
293
301
 
294
- list = RDoc::Markup::List.new :BULLET
295
-
296
- cm.in_files.each do |file|
297
- para = RDoc::Markup::Paragraph.new file.full_name
298
- list << RDoc::Markup::ListItem.new(nil, para)
302
+ if class_module.in_files.empty?
303
+ empty_classes << class_module
304
+ next
299
305
  end
300
306
 
301
- report << list
302
- report << RDoc::Markup::BlankLine.new
307
+ unless class_module.documented? || class_module.full_name == 'Object'
308
+ collect_undocumented_class_module(class_module, items)
309
+ end
303
310
 
304
- klass = RDoc::Markup::Verbatim.new("#{cm.definition}\n")
311
+ collect_undocumented_constants(class_module, items)
312
+ collect_undocumented_attributes(class_module, items)
313
+ collect_undocumented_methods(class_module, items)
305
314
  end
306
315
 
307
- klass << "\n"
308
-
309
- body = yield.flatten # HACK remove #flatten
310
-
311
- if body.empty? then
312
- return if documented
316
+ [items, empty_classes]
317
+ end
313
318
 
314
- klass.parts.pop
315
- else
316
- klass.parts.concat body
319
+ ##
320
+ # Collects undocumented classes or modules from +class_module+ into +items+.
321
+ # Reopened classes/modules are reported in every file they appear in.
322
+
323
+ def collect_undocumented_class_module(class_module, items)
324
+ class_module.in_files.map(&:full_name).uniq.each do |file|
325
+ items << {
326
+ type: class_module.type.capitalize,
327
+ name: class_module.full_name,
328
+ file: file,
329
+ line: nil,
330
+ }
317
331
  end
332
+ end
333
+
334
+ ##
335
+ # Collects undocumented constants from +class_module+ into +items+.
318
336
 
319
- klass << "end\n"
337
+ def collect_undocumented_constants(class_module, items)
338
+ class_module.constants.each do |constant|
339
+ next unless constant.display?
340
+ next if constant.documented? || constant.is_alias_for
320
341
 
321
- report << klass
342
+ file = constant.file&.full_name
343
+ next unless file
322
344
 
323
- report
345
+ items << {
346
+ type: "Constant",
347
+ name: constant.full_name,
348
+ file: file,
349
+ line: constant.line,
350
+ }
351
+ end
324
352
  end
325
353
 
326
354
  ##
327
- # Returns a report on undocumented constants in ClassModule +cm+
355
+ # Collects undocumented attributes from +class_module+ into +items+.
328
356
 
329
- def report_constants(cm)
330
- return if cm.constants.empty?
331
-
332
- report = []
357
+ def collect_undocumented_attributes(class_module, items)
358
+ class_module.attributes.each do |attr|
359
+ next unless attr.display?
360
+ next if attr.documented?
333
361
 
334
- cm.constants.each do |constant|
335
- # TODO constant aliases are listed in the summary but not reported
336
- # figure out what to do here
337
- next if constant.documented? || constant.is_alias_for
362
+ file = attr.file&.full_name
363
+ next unless file
338
364
 
339
- line = constant.line ? ":#{constant.line}" : line
340
- report << " # in file #{constant.file.full_name}#{line}\n"
341
- report << " #{constant.name} = nil\n"
342
- report << "\n"
365
+ scope = attr.singleton ? "." : "#"
366
+ items << {
367
+ type: "Attribute",
368
+ name: "#{class_module.full_name}#{scope}#{attr.name}",
369
+ file: file,
370
+ line: attr.line,
371
+ }
343
372
  end
344
-
345
- report
346
373
  end
347
374
 
348
375
  ##
349
- # Returns a report on undocumented methods in ClassModule +cm+
376
+ # Collects undocumented methods from +class_module+ into +items+.
377
+ # At coverage level > 0, also counts undocumented parameters.
350
378
 
351
- def report_methods(cm)
352
- return if cm.method_list.empty?
379
+ def collect_undocumented_methods(class_module, items)
380
+ class_module.each_method do |method|
381
+ next unless method.display?
382
+ next if method.documented? && @coverage_level.zero?
353
383
 
354
- report = []
384
+ undoc_param_names = nil
355
385
 
356
- cm.each_method do |method|
357
- next if method.documented? and @coverage_level.zero?
358
-
359
- if @coverage_level > 0 then
386
+ if @coverage_level > 0
360
387
  params, undoc = undoc_params method
361
-
362
388
  @num_params += params
363
389
 
364
- unless undoc.empty? then
390
+ unless undoc.empty?
365
391
  @undoc_params += undoc.length
366
-
367
- undoc = undoc.map do |param| "+#{param}+" end
368
- param_report = " # #{undoc.join ', '} is not documented\n"
392
+ undoc_param_names = undoc
369
393
  end
370
394
  end
371
395
 
372
- next if method.documented? and not param_report
396
+ next if method.documented? && !undoc_param_names
373
397
 
374
- line = method.line ? ":#{method.line}" : nil
375
- scope = method.singleton ? 'self.' : nil
398
+ file = method.file&.full_name
399
+ next unless file
376
400
 
377
- report << " # in file #{method.file.full_name}#{line}\n"
378
- report << param_report if param_report
379
- report << " def #{scope}#{method.name}#{method.params}; end\n"
380
- report << "\n"
381
- end
401
+ scope = method.singleton ? "." : "#"
402
+ item = {
403
+ type: "Method",
404
+ name: "#{class_module.full_name}#{scope}#{method.name}",
405
+ file: file,
406
+ line: method.line,
407
+ }
408
+ item[:undoc_params] = undoc_param_names if undoc_param_names
382
409
 
383
- report
410
+ items << item
411
+ end
384
412
  end
385
413
 
386
414
  ##
@@ -400,12 +428,10 @@ class RDoc::Stats
400
428
  @undoc_params,
401
429
  ].max.to_s.length
402
430
 
403
- report = RDoc::Markup::Verbatim.new
431
+ report = +""
404
432
 
405
433
  report << "Files: %*d\n" % [num_width, @num_files]
406
-
407
434
  report << "\n"
408
-
409
435
  report << "Classes: %*d (%*d undocumented)\n" % [
410
436
  num_width, @num_classes, undoc_width, @undoc_classes]
411
437
  report << "Modules: %*d (%*d undocumented)\n" % [
@@ -419,17 +445,14 @@ class RDoc::Stats
419
445
  report << "Parameters: %*d (%*d undocumented)\n" % [
420
446
  num_width, @num_params, undoc_width, @undoc_params] if
421
447
  @coverage_level > 0
422
-
423
448
  report << "\n"
424
-
425
449
  report << "Total: %*d (%*d undocumented)\n" % [
426
450
  num_width, @num_items, undoc_width, @undoc_items]
427
-
428
451
  report << "%6.2f%% documented\n" % percent_doc
429
452
  report << "\n"
430
453
  report << "Elapsed: %0.1fs\n" % (Time.now - @start)
431
454
 
432
- RDoc::Markup::Document.new report
455
+ report
433
456
  end
434
457
 
435
458
  ##
data/lib/rdoc/store.rb CHANGED
@@ -193,6 +193,121 @@ class RDoc::Store
193
193
  top_level
194
194
  end
195
195
 
196
+ ##
197
+ # Removes a file and its classes/modules from the store. Used by the
198
+ # live-reloading server when a source file is deleted.
199
+ #
200
+ # Note: this does not handle reopened classes correctly. If a class is
201
+ # defined across multiple files (e.g. +Foo+ in both +a.rb+ and +b.rb+),
202
+ # deleting one file removes the entire class from the store — including
203
+ # the parts contributed by the other file. Saving the remaining file
204
+ # triggers a re-parse that restores it.
205
+
206
+ def remove_file(relative_name)
207
+ top_level = @files_hash.delete(relative_name)
208
+ @text_files_hash.delete(relative_name)
209
+ @c_class_variables.delete(relative_name)
210
+ @c_singleton_class_variables.delete(relative_name)
211
+ return unless top_level
212
+
213
+ remove_classes_and_modules(top_level.classes_or_modules)
214
+ end
215
+
216
+ ##
217
+ # Removes a file's contributions (methods, constants, comments, etc.)
218
+ # from its classes and modules. If no other files contribute to a
219
+ # class or module, it is removed from the store entirely. This
220
+ # prevents duplication when the file is re-parsed while preserving
221
+ # shared namespaces like +RDoc+ that span many files.
222
+
223
+ def clear_file_contributions(relative_name, keep_position: false)
224
+ top_level = @files_hash[relative_name]
225
+ return unless top_level
226
+
227
+ top_level.classes_or_modules.each do |cm|
228
+ # Remove methods and attributes contributed by this file
229
+ cm.method_list.reject! { |m| m.file == top_level }
230
+ cm.attributes.reject! { |a| a.file == top_level }
231
+
232
+ # Rebuild methods_hash from remaining methods and attributes
233
+ cm.methods_hash.clear
234
+ cm.method_list.each { |m| cm.methods_hash[m.pretty_name] = m }
235
+ cm.attributes.each { |a| cm.methods_hash[a.pretty_name] = a }
236
+
237
+ # Remove constants contributed by this file
238
+ cm.constants.reject! { |c| c.file == top_level }
239
+ cm.constants_hash.clear
240
+ cm.constants.each { |c| cm.constants_hash[c.name] = c }
241
+
242
+ # Remove includes, extends, and aliases from this file
243
+ cm.includes.reject! { |i| i.file == top_level }
244
+ cm.extends.reject! { |e| e.file == top_level }
245
+ cm.aliases.reject! { |a| a.file == top_level }
246
+ cm.external_aliases.reject! { |a| a.file == top_level }
247
+
248
+ # Clear or remove comment entries from this file
249
+ if cm.is_a?(RDoc::ClassModule)
250
+ if keep_position
251
+ cm.comment_location[top_level] = [] if cm.comment_location.key?(top_level)
252
+ else
253
+ cm.comment_location.delete(top_level)
254
+ end
255
+ cm.rebuild_comment_from_location
256
+ end
257
+
258
+ unless keep_position
259
+ # Remove this file from the class/module's file list
260
+ cm.in_files.delete(top_level)
261
+
262
+ # If no files contribute to this class/module anymore, remove it
263
+ # from the store entirely. This handles file deletion correctly
264
+ # for classes that are only defined in the deleted file, while
265
+ # preserving classes that span multiple files.
266
+ if cm.in_files.empty?
267
+ if cm.is_a?(RDoc::NormalModule)
268
+ @modules_hash.delete(cm.full_name)
269
+ else
270
+ @classes_hash.delete(cm.full_name)
271
+ end
272
+ cm.parent&.classes_hash&.delete(cm.name)
273
+ cm.parent&.modules_hash&.delete(cm.name)
274
+ end
275
+ end
276
+ end
277
+
278
+ # Clear the TopLevel's class/module list to prevent duplicates
279
+ top_level.classes_or_modules.clear
280
+ end
281
+
282
+ ##
283
+ # Removes stale empty placeholders left by +clear_file_contributions+ with
284
+ # <tt>keep_position: true</tt>. After re-parsing, a file may no longer
285
+ # define a class it previously contributed to, leaving an empty entry in
286
+ # +comment_location+ and a stale +in_files+ reference. Call this after
287
+ # all re-parsing is complete.
288
+
289
+ def cleanup_stale_contributions
290
+ all_classes_and_modules.each do |cm|
291
+ cm.comment_location.delete_if { |_, comments| comments.empty? }
292
+ cm.rebuild_comment_from_location
293
+
294
+ cm.in_files.select! { |tl| cm.comment_location.key?(tl) ||
295
+ cm.method_list.any? { |m| m.file == tl } ||
296
+ cm.attributes.any? { |a| a.file == tl } ||
297
+ cm.constants.any? { |c| c.file == tl } }
298
+
299
+ if cm.in_files.empty?
300
+ if cm.is_a?(RDoc::NormalModule)
301
+ @modules_hash.delete(cm.full_name)
302
+ else
303
+ @classes_hash.delete(cm.full_name)
304
+ end
305
+ cm.parent&.classes_hash&.delete(cm.name)
306
+ cm.parent&.modules_hash&.delete(cm.name)
307
+ end
308
+ end
309
+ end
310
+
196
311
  ##
197
312
  # Make sure any references to C variable names are resolved to the corresponding class.
198
313
  #
@@ -206,11 +321,11 @@ class RDoc::Store
206
321
  end
207
322
 
208
323
  ##
209
- # Sets the parser of +absolute_name+, unless it from a source code file.
324
+ # Caches +relative_name+ in the text files hash, if it is a text file.
210
325
 
211
- def update_parser_of_file(absolute_name, parser)
212
- if top_level = @files_hash[absolute_name] then
213
- @text_files_hash[absolute_name] = top_level if top_level.text?
326
+ def cache_text_file(relative_name)
327
+ if top_level = @files_hash[relative_name]
328
+ @text_files_hash[relative_name] = top_level if top_level.text?
214
329
  end
215
330
  end
216
331
 
@@ -228,6 +343,86 @@ class RDoc::Store
228
343
  @classes_hash.values + @modules_hash.values
229
344
  end
230
345
 
346
+ ##
347
+ # Returns a hash mapping class/module names to their paths, for use
348
+ # by type signature linking. Maps both qualified names (Foo::Bar) and
349
+ # unambiguous unqualified names (Bar). Ambiguous unqualified names
350
+ # (where multiple classes share the same name) are excluded to avoid
351
+ # wrong links. Cached after first call.
352
+
353
+ ##
354
+ # Invalidates the cached type name lookup. Server mode calls this after
355
+ # re-parsing changes the set of classes and modules.
356
+
357
+ def invalidate_type_name_lookup # :nodoc:
358
+ @type_name_lookup = nil
359
+ end
360
+
361
+ def type_name_lookup
362
+ @type_name_lookup ||= begin
363
+ lookup = {}
364
+ unqualified_names = {}
365
+ ambiguous_names = {}
366
+ all_classes_and_modules.each do |cm|
367
+ lookup[cm.full_name] = cm.path
368
+ unqualified_name = cm.name
369
+
370
+ if ambiguous_names[unqualified_name]
371
+ # already known ambiguous, skip
372
+ elsif unqualified_names.key?(unqualified_name)
373
+ unqualified_names.delete(unqualified_name)
374
+ ambiguous_names[unqualified_name] = true
375
+ else
376
+ unqualified_names[unqualified_name] = cm.path
377
+ end
378
+ end
379
+ lookup.merge!(unqualified_names)
380
+ end
381
+ end
382
+
383
+ ##
384
+ # Stores RBS type signatures loaded from sidecar .rbs files, keyed by
385
+ # "ClassName#method" or "ClassName.method". Replaces any previously
386
+ # stored set, so passing +{}+ clears it. Inline +#:+ annotations on
387
+ # method objects are NOT touched — those are owned by the source file.
388
+
389
+ def merge_rbs_signatures(signatures)
390
+ @rbs_signatures = signatures
391
+ end
392
+
393
+ ##
394
+ # Returns the RBS type signature lines for +method_attr+ from loaded
395
+ # sidecar +.rbs+ files, or +nil+ if none. Falls through to the
396
+ # canonical method for aliases, and handles +initialize+ -> +.new+
397
+ # singleton mapping.
398
+
399
+ def rbs_signature_for(method_attr)
400
+ return nil unless @rbs_signatures
401
+ cm = method_attr.parent
402
+ return nil unless cm.respond_to?(:full_name)
403
+
404
+ key = method_attr.singleton ? "#{cm.full_name}.#{method_attr.name}" : "#{cm.full_name}##{method_attr.name}"
405
+ sig = @rbs_signatures[key]
406
+
407
+ # RBS keys constructors as #initialize, but RDoc renames them to .new
408
+ if !sig && method_attr.name == 'new' && method_attr.singleton
409
+ sig = @rbs_signatures["#{cm.full_name}#initialize"]
410
+ end
411
+
412
+ # For aliases, fall through to the canonical method (its inline #:
413
+ # takes precedence over any sidecar signature on the alias's name).
414
+ if !sig && method_attr.is_alias_for
415
+ canonical = method_attr.is_alias_for
416
+ sig = canonical.type_signature_lines || rbs_signature_for(canonical)
417
+ end
418
+
419
+ sig
420
+ end
421
+
422
+ def clear_rbs_signatures # :nodoc:
423
+ @rbs_signatures = nil
424
+ end
425
+
231
426
  ##
232
427
  # All TopLevels known to RDoc
233
428
 
@@ -978,6 +1173,19 @@ class RDoc::Store
978
1173
  end
979
1174
 
980
1175
  private
1176
+
1177
+ def remove_classes_and_modules(cms)
1178
+ cms.each do |cm|
1179
+ remove_classes_and_modules(cm.classes_and_modules)
1180
+
1181
+ if cm.is_a?(RDoc::NormalModule)
1182
+ @modules_hash.delete(cm.full_name)
1183
+ else
1184
+ @classes_hash.delete(cm.full_name)
1185
+ end
1186
+ end
1187
+ end
1188
+
981
1189
  def marshal_load(file)
982
1190
  File.open(file, 'rb') {|io| Marshal.load(io, MarshalFilter)}
983
1191
  end
data/lib/rdoc/task.rb CHANGED
@@ -202,21 +202,6 @@ class RDoc::Task < Rake::TaskLib
202
202
  @options = []
203
203
  end
204
204
 
205
- ##
206
- # All source is inline now. This method is deprecated
207
-
208
- def inline_source # :nodoc:
209
- warn "RDoc::Task#inline_source is deprecated"
210
- true
211
- end
212
-
213
- ##
214
- # All source is inline now. This method is deprecated
215
-
216
- def inline_source=(value) # :nodoc:
217
- warn "RDoc::Task#inline_source is deprecated"
218
- end
219
-
220
205
  ##
221
206
  # Create the tasks defined by this task lib.
222
207
 
@@ -260,6 +245,15 @@ class RDoc::Task < Rake::TaskLib
260
245
  $stderr.puts "rdoc #{args.join ' '}" if Rake.application.options.trace
261
246
  RDoc::RDoc.new.document args
262
247
  end
248
+
249
+ desc server_task_description
250
+ task "server" do
251
+ @before_running_rdoc.call if @before_running_rdoc
252
+ args = option_list + ["--server"] + @rdoc_files
253
+
254
+ $stderr.puts "rdoc #{args.join ' '}" if Rake.application.options.trace
255
+ RDoc::RDoc.new.document args
256
+ end
263
257
  end
264
258
 
265
259
  self
@@ -309,6 +303,13 @@ class RDoc::Task < Rake::TaskLib
309
303
  "Print RDoc coverage report"
310
304
  end
311
305
 
306
+ ##
307
+ # Task description for the server task
308
+
309
+ def server_task_description
310
+ "Start a live-reloading documentation server"
311
+ end
312
+
312
313
  private
313
314
 
314
315
  def rdoc_target