@@ -7,6 +7,12 @@ class RDoc::Stats
77
88 include RDoc ::Text
99
10+ TYPE_ORDER = %w[ Class Module Constant Attribute Method ] . freeze
11+ GREAT_JOB_MESSAGE = <<~MSG
12+ 100% documentation!
13+ Great Job!
14+ MSG
15+
1016 ##
1117 # Output level for the coverage report
1218
@@ -193,18 +199,6 @@ def fully_documented?
193199 @fully_documented
194200 end
195201
196- ##
197- # A report that says you did a great job!
198-
199- def great_job
200- report = RDoc ::Markup ::Document . new
201-
202- report << RDoc ::Markup ::Paragraph . new ( '100% documentation!' )
203- report << RDoc ::Markup ::Paragraph . new ( 'Great Job!' )
204-
205- report
206- end
207-
208202 ##
209203 # Calculates the percentage of items documented.
210204
@@ -230,164 +224,181 @@ def report
230224 if @coverage_level . zero? then
231225 calculate
232226
233- return great_job if @num_items == @doc_items
227+ return GREAT_JOB_MESSAGE if @num_items == @doc_items
234228 end
235229
236- ucm = @store . unique_classes_and_modules
230+ items , empty_classes = collect_undocumented_items
237231
238- report = RDoc ::Markup ::Document . new
239- report << RDoc ::Markup ::Paragraph . new ( 'The following items are not documented:' )
240- report << RDoc ::Markup ::BlankLine . new
241-
242- ucm . sort . each do |cm |
243- body = report_class_module ( cm ) {
244- [
245- report_constants ( cm ) ,
246- report_attributes ( cm ) ,
247- report_methods ( cm ) ,
248- ] . compact
249- }
232+ if @coverage_level > 0 then
233+ calculate
250234
251- report << body if body
235+ return GREAT_JOB_MESSAGE if @num_items == @doc_items
252236 end
253237
254- if @coverage_level > 0 then
255- calculate
238+ report = + ""
239+ report << "The following items are not documented: \n \n "
256240
257- return great_job if @num_items == @doc_items
241+ # Referenced-but-empty classes
242+ empty_classes . each do |cm |
243+ report << "#{ cm . full_name } is referenced but empty.\n "
244+ report << "It probably came from another project. I'm sorry I'm holding it against you.\n \n "
258245 end
259246
260- report
261- end
247+ # Group items by file, then by type
248+ by_file = items . group_by { | item | item [ :file ] }
262249
263- ##
264- # Returns a report on undocumented attributes in ClassModule +cm+
250+ by_file . sort_by { | file , _ | file } . each do | file , file_items |
251+ report << " #{ file } : \n "
265252
266- def report_attributes ( cm )
267- return if cm . attributes . empty?
253+ by_type = file_items . group_by { |item | item [ :type ] }
268254
269- report = [ ]
255+ TYPE_ORDER . each do |type |
256+ next unless by_type [ type ]
257+
258+ report << " #{ type } :\n "
259+
260+ sorted = by_type [ type ] . sort_by { |item | [ item [ :line ] || 0 , item [ :name ] ] }
261+ name_width = sorted . reduce ( 0 ) { |max , item | item [ :line ] && item [ :name ] . length > max ? item [ :name ] . length : max }
262+
263+ sorted . each do |item |
264+ if item [ :line ]
265+ report << " %-*s %s:%d\n " % [ name_width , item [ :name ] , item [ :file ] , item [ :line ] ]
266+ else
267+ report << " #{ item [ :name ] } \n "
268+ end
269+
270+ if item [ :undoc_params ]
271+ report << " Undocumented params: #{ item [ :undoc_params ] . join ( ', ' ) } \n "
272+ end
273+ end
274+ end
270275
271- cm . attributes . each do |attr |
272- next if attr . documented?
273- line = attr . line ? ":#{ attr . line } " : nil
274- report << " #{ attr . definition } :#{ attr . name } # in file #{ attr . file . full_name } #{ line } \n "
275276 report << "\n "
276277 end
277278
278279 report
279280 end
280281
281282 ##
282- # Returns a report on undocumented items in ClassModule +cm+
283+ # Collects all undocumented items across all classes and modules.
284+ # Returns [items, empty_classes] where items is an Array of Hashes
285+ # with keys :type, :name, :file, :line, and empty_classes is an
286+ # Array of ClassModule objects that are referenced but have no files.
283287
284- def report_class_module ( cm )
285- return if cm . fully_documented? and @coverage_level . zero?
286- return unless cm . display?
288+ def collect_undocumented_items
289+ empty_classes = [ ]
290+ items = [ ]
287291
288- report = RDoc ::Markup ::Document . new
292+ @store . unique_classes_and_modules . each do |class_module |
293+ next unless class_module . display?
289294
290- if cm . in_files . empty? then
291- report << RDoc ::Markup ::Paragraph . new ( "#{ cm . definition } is referenced but empty." )
292- report << RDoc ::Markup ::Paragraph . new ( "It probably came from another project. I'm sorry I'm holding it against you." )
293-
294- return report
295- elsif cm . documented? then
296- documented = true
297- klass = RDoc ::Markup ::Verbatim . new ( "#{ cm . definition } # is documented\n " )
298- else
299- report << RDoc ::Markup ::Paragraph . new ( 'In files:' )
300-
301- list = RDoc ::Markup ::List . new :BULLET
302-
303- cm . in_files . each do |file |
304- para = RDoc ::Markup ::Paragraph . new file . full_name
305- list << RDoc ::Markup ::ListItem . new ( nil , para )
295+ if class_module . in_files . empty?
296+ empty_classes << class_module
297+ next
306298 end
307299
308- report << list
309- report << RDoc ::Markup ::BlankLine . new
300+ unless class_module . documented? || class_module . full_name == 'Object'
301+ collect_undocumented_class_module ( class_module , items )
302+ end
310303
311- klass = RDoc ::Markup ::Verbatim . new ( "#{ cm . definition } \n " )
304+ collect_undocumented_constants ( class_module , items )
305+ collect_undocumented_attributes ( class_module , items )
306+ collect_undocumented_methods ( class_module , items )
312307 end
313308
314- klass << "\n "
315-
316- body = yield . flatten # HACK remove #flatten
317-
318- if body . empty? then
319- return if documented
309+ [ items , empty_classes ]
310+ end
320311
321- klass . parts . pop
322- else
323- klass . parts . concat body
312+ ##
313+ # Collects undocumented classes or modules from +class_module+ into +items+.
314+ # Reopened classes/modules are reported in every file they appear in.
315+
316+ def collect_undocumented_class_module ( class_module , items )
317+ class_module . in_files . map ( &:full_name ) . uniq . each do |file |
318+ items << {
319+ type : class_module . type . capitalize ,
320+ name : class_module . full_name ,
321+ file : file ,
322+ line : nil ,
323+ }
324324 end
325+ end
326+
327+ ##
328+ # Collects undocumented constants from +class_module+ into +items+.
325329
326- klass << "end\n "
330+ def collect_undocumented_constants ( class_module , items )
331+ class_module . constants . each do |constant |
332+ next if constant . documented? || constant . is_alias_for
327333
328- report << klass
334+ file = constant . file &.full_name
335+ next unless file
329336
330- report
337+ items << {
338+ type : "Constant" ,
339+ name : constant . full_name ,
340+ file : file ,
341+ line : constant . line ,
342+ }
343+ end
331344 end
332345
333346 ##
334- # Returns a report on undocumented constants in ClassModule +cm+
335-
336- def report_constants ( cm )
337- return if cm . constants . empty?
347+ # Collects undocumented attributes from +class_module+ into +items+.
338348
339- report = [ ]
349+ def collect_undocumented_attributes ( class_module , items )
350+ class_module . attributes . each do |attr |
351+ next if attr . documented?
340352
341- cm . constants . each do |constant |
342- # TODO constant aliases are listed in the summary but not reported
343- # figure out what to do here
344- next if constant . documented? || constant . is_alias_for
353+ file = attr . file &.full_name
354+ next unless file
345355
346- line = constant . line ? ":#{ constant . line } " : line
347- report << " # in file #{ constant . file . full_name } #{ line } \n "
348- report << " #{ constant . name } = nil\n "
349- report << "\n "
356+ scope = attr . singleton ? "." : "#"
357+ items << {
358+ type : "Attribute" ,
359+ name : "#{ class_module . full_name } #{ scope } #{ attr . name } " ,
360+ file : file ,
361+ line : attr . line ,
362+ }
350363 end
351-
352- report
353364 end
354365
355366 ##
356- # Returns a report on undocumented methods in ClassModule +cm+
357-
358- def report_methods ( cm )
359- return if cm . method_list . empty?
367+ # Collects undocumented methods from +class_module+ into +items+.
368+ # At coverage level > 0, also counts undocumented parameters.
360369
361- report = [ ]
370+ def collect_undocumented_methods ( class_module , items )
371+ class_module . each_method do |method |
372+ next if method . documented? && @coverage_level . zero?
362373
363- cm . each_method do |method |
364- next if method . documented? and @coverage_level . zero?
374+ undoc_param_names = nil
365375
366- if @coverage_level > 0 then
376+ if @coverage_level > 0
367377 params , undoc = undoc_params method
368-
369378 @num_params += params
370379
371- unless undoc . empty? then
380+ unless undoc . empty?
372381 @undoc_params += undoc . length
373-
374- undoc = undoc . map do |param | "+#{ param } +" end
375- param_report = " # #{ undoc . join ', ' } is not documented\n "
382+ undoc_param_names = undoc
376383 end
377384 end
378385
379- next if method . documented? and not param_report
386+ next if method . documented? && ! undoc_param_names
380387
381- line = method . line ? ": #{ method . line } " : nil
382- scope = method . singleton ? 'self.' : nil
388+ file = method . file &. full_name
389+ next unless file
383390
384- report << " # in file #{ method . file . full_name } #{ line } \n "
385- report << param_report if param_report
386- report << " def #{ scope } #{ method . name } #{ method . params } ; end\n "
387- report << "\n "
388- end
391+ scope = method . singleton ? "." : "#"
392+ item = {
393+ type : "Method" ,
394+ name : "#{ class_module . full_name } #{ scope } #{ method . name } " ,
395+ file : file ,
396+ line : method . line ,
397+ }
398+ item [ :undoc_params ] = undoc_param_names if undoc_param_names
389399
390- report
400+ items << item
401+ end
391402 end
392403
393404 ##
@@ -407,12 +418,10 @@ def summary
407418 @undoc_params ,
408419 ] . max . to_s . length
409420
410- report = RDoc :: Markup :: Verbatim . new
421+ report = + ""
411422
412423 report << "Files: %*d\n " % [ num_width , @num_files ]
413-
414424 report << "\n "
415-
416425 report << "Classes: %*d (%*d undocumented)\n " % [
417426 num_width , @num_classes , undoc_width , @undoc_classes ]
418427 report << "Modules: %*d (%*d undocumented)\n " % [
@@ -426,17 +435,14 @@ def summary
426435 report << "Parameters: %*d (%*d undocumented)\n " % [
427436 num_width , @num_params , undoc_width , @undoc_params ] if
428437 @coverage_level > 0
429-
430438 report << "\n "
431-
432439 report << "Total: %*d (%*d undocumented)\n " % [
433440 num_width , @num_items , undoc_width , @undoc_items ]
434-
435441 report << "%6.2f%% documented\n " % percent_doc
436442 report << "\n "
437443 report << "Elapsed: %0.1fs\n " % ( Time . now - @start )
438444
439- RDoc :: Markup :: Document . new report
445+ report
440446 end
441447
442448 ##
0 commit comments