# frozen_string_literal: truemoduleYARDmoduleCLI# @since 0.6.0classStats<YardocincludeTemplates::Helpers::BaseHelper# Maintains the order in which +stats_for_+ statistics methods should be# printed.## @see #print_statisticsSTATS_ORDER=[:files,:modules,:classes,:constants,:attributes,:methods]# @return [Boolean] whether to parse and load registryattr_accessor:parse# @param [Boolean] parse whether to parse and load registry (see {#parse})definitialize(parse=true)super()@parse=parse@undoc_list=nil@compact=falseenddefdescription"Prints documentation statistics on a set of files"end# Runs the commandline utility, parsing arguments and generating# output if set.## @param [Array<String>] args the list of arguments# @return [void]defrun(*args)parse_arguments(*args)ifuse_cacheRegistry.load!elsifparseYARD.parse(files,excluded)Registry.save(use_cache)ifsave_yardocendprint_statisticsprint_undocumented_objectsabortiffail_on_warning&&log.warnedend# Prints statistics for different object types## To add statistics for a specific type, add a method +#stats_for_TYPE+# to this class that calls {#output}.defprint_statistics@total=0@undocumented=0meths=methods.map(&:to_s).grep(/^stats_for_/)STATS_ORDER.eachdo|meth|mname="stats_for_#{meth}"ifmeths.include?(mname)send(mname)meths.delete(mname)endendmeths.each{|m|send(m)}total=if@undocumented==0100elsif@total==00else(@total-@undocumented).to_f/@total.to_f*100endlog.puts("% 3.2f%% documented"%total)end# Prints list of undocumented objectsdefprint_undocumented_objectsreturnif!@undoc_list||@undoc_list.empty?log.putslog.puts"Undocumented Objects:"# array needed for sort due to unstable sortobjects=@undoc_list.sort_by{|o|[o.file.to_s,o.path]}max=objects.max{|a,b|a.path.length<=>b.path.length}.path.lengthif@compactobjects.eachdo|object|log.puts("%-#{max}s (%s)"%[object.path,[object.file||"-unknown-",object.line].compact.join(":")])endelselast_file=nilobjects.eachdo|object|ifobject.file!=last_filelog.putslog.puts"(in file: #{object.file||"-unknown-"})"endlog.putsobject.pathlast_file=object.fileendendend# @return [Array<CodeObjects::Base>] all the parsed objects in the registry,# removing any objects that are not visible (private, protected) depending# on the arguments passed to the command.defall_objects@all_objects||=run_verifierRegistry.allend# Statistics for filesdefstats_for_filesfiles=[]all_objects.each{|o|files|=[o.file]}output"Files",files.sizeend# Statistics for modulesdefstats_for_modulesoutput"Modules",*type_statistics(:module)end# Statistics for classesdefstats_for_classesoutput"Classes",*type_statistics(:class)end# Statistics for constantsdefstats_for_constantsoutput"Constants",*type_statistics(:constant)end# Statistics for attributesdefstats_for_attributesobjs=all_objects.select{|m|m.type==:method&&m.is_attribute?}objs.uniq!{|m|m.name.to_s.gsub(/=$/,'')}undoc=objs.select{|m|m.docstring.blank?}@undoc_list|=undocif@undoc_listoutput"Attributes",objs.size,undoc.sizeend# Statistics for methodsdefstats_for_methodsobjs=all_objects.select{|m|m.type==:method}objs.reject!(&:is_alias?)objs.reject!(&:is_attribute?)undoc=objs.select{|m|m.docstring.blank?}@undoc_list|=undocif@undoc_listoutput"Methods",objs.size,undoc.sizeend# Prints a statistic to standard out. This method is optimized for# getting Integer values, though it allows any data to be printed.## @param [String] name the statistic name# @param [Integer, String] data the numeric (or any) data representing# the statistic. If +data+ is an Integer, it should represent the# total objects of a type.# @param [Integer, nil] undoc number of undocumented objects for the type# @return [void]defoutput(name,data,undoc=nil)@total+=dataifdata.is_a?(Integer)&&undoc@undocumented+=undocifundoc.is_a?(Integer)data=ifundoc("%5s (% 5d undocumented)"%[data,undoc])else"%5s"%dataendlog.puts("%-12s %s"%[name+":",data])endprivatedeftype_statistics(type)objs=all_objects.select{|m|m.type==type}undoc=objs.find_all{|m|m.docstring.blank?}@undoc_list|=undocif@undoc_list[objs.size,undoc.size]end# Parses commandline options.# @param [Array<String>] args each tokenized argumentdefoptparse(*args)opts=OptionParser.newopts.banner="Usage: yard stats [options] [source_files]"opts.separator"(if a list of source files is omitted, lib/**/*.rb ext/**/*.{c,rb} is used.)"general_options(opts)output_options(opts)tag_options(opts)common_options(opts)parse_options(opts,args)parse_files(*args)unlessargs.empty?enddefgeneral_options(opts)super(opts)opts.on('--list-undoc','List all undocumented objects')do@undoc_list=[]endopts.on('--compact','Compact undocumented objects listing')do@compact=trueendopts.on('--no-public',"Don't include public methods in statistics.")dovisibilities.delete(:public)endopts.on('--protected',"Include protected methods in statistics.")dovisibilities.push(:protected)endopts.on('--private',"Include private methods in statistics.")dovisibilities.push(:private)endopts.on('--no-private',"Don't include objects with @private tag in statistics.")dooptions[:verifier].add_expressions'!object.tag(:private) &&
(object.namespace.type == :proxy || !object.namespace.tag(:private))'endopts.on('--query QUERY',"Only includes objects that match a specific query")do|query|query.taintifquery.respond_to?(:taint)options[:verifier].add_expressions(query)endendendendend