Class: RDoc::RDoc
| Relationships & Source Files | |
| Inherits: | Object |
| Defined in: | lib/rdoc/rdoc.rb |
Overview
This is the driver for generating RDoc output. It handles file parsing and
generation of output.
To use this class to generate RDoc output via the API, the recommended way
is:
rdoc = RDoc::RDoc.new
= RDoc::Options. # returns an RDoc::Options instance
# set extra options
rdoc.document
You can also generate output like the rdoc executable:
rdoc = RDoc::RDoc.new
rdoc.document argv
Where argv is an array of strings, each corresponding to an argument you'd
give rdoc on the command line. See rdoc --help for details.
Constant Summary
-
GENERATORS =
# File 'lib/rdoc/rdoc.rb', line 37
This is the list of supported output generators
{} -
TEST_SUITE_DIRECTORY_NAMES =
# File 'lib/rdoc/rdoc.rb', line 47
List of directory names skipped if test suites should be skipped
%w[spec test].freeze
-
UNCONDITIONALLY_SKIPPED_DIRECTORIES =
# File 'lib/rdoc/rdoc.rb', line 42
List of directory names always skipped
%w[CVS .svn .git].freeze
Class Attribute Summary
-
.current
rw
Active
RDocinstance. -
.current=(rdoc)
rw
Sets the active
RDocinstance.
Class Method Summary
-
.add_generator(klass)
Add
klassthat can generate output after parsing. -
.new ⇒ RDoc
constructor
Creates a new
RDocinstance. -
.rbs_discovery_extension?(extension) ⇒ Boolean
Internal use only
Returns true when
extensionis the RBS gem'sRDocdiscovery hook.
Instance Attribute Summary
-
#auto_discovered_rbs_signatures_changed? ⇒ Boolean
readonly
Returns true if any auto-discovered RBS signature file has changed since the last run.
-
#generator
rw
Generatorinstance used for creating output. -
#last_modified
readonly
Hash of files and their last modified times.
-
#options
rw
RDocoptions. -
#stats
readonly
Accessor for statistics.
-
#store
rw
The current documentation store.
Instance Method Summary
-
#auto_discovered_rbs_signature_files
Returns RBS files that
RDocauto-discovers for signature loading. -
#document(options)
Generates documentation or a coverage report depending upon the settings in #options.
-
#error(msg)
Report an error message and exit.
-
#gather_files(files)
Gathers a set of parseable files from the files and directories listed in
files. -
#generate
Generates documentation for
file_info(from #parse_files) into the output dir using the generator selected by theRDocoptions. -
#handle_pipe
Turns RDoc from stdin into HTML.
-
#install_siginfo_handler
Installs a siginfo handler that prints the current filename.
-
#list_files_in_directory(dir)
Return a list of the files to be processed in a directory.
-
#load_auto_discovered_rbs_signatures
Loads RBS type signatures from the project's
sigdirectory and RBS stdlib, then merges them into the store's code objects. -
#normalized_file_list(relative_files, force_doc = false, exclude_pattern = nil)
Given a list of files and directories, create a list of all the Ruby files they contain.
-
#output_flag_file(op_dir)
Return the path name of the flag file in an output directory.
-
#parse_dot_doc_file(in_dir, filename)
The #document file contains a list of file and directory name patterns, representing candidates for documentation.
-
#parse_file(filename)
Parses
filenameand returns anTopLevel -
#parse_files(files)
Parse each file on the command line, recursively entering directories.
-
#record_auto_discovered_rbs_signature_mtimes
Records auto-discovered RBS signature file mtimes so normal generation freshness checks and the live server watcher can see signature-only edits.
-
#relative_path_for(filename)
Returns the relative path for
filenameagainstoptions.root(andoptions.page_dirwhen set). -
#remove_siginfo_handler
Removes a siginfo handler and replaces the previous.
-
#remove_unparseable(files)
Removes file extensions known to be unparseable from
filesand TAGS files for emacs and vim. -
#setup_output_dir(dir, force)
Create an output dir if it doesn't exist.
-
#start_server
Starts a live-reloading HTTP server for previewing documentation.
- #syntax_check_command_for(filename, parser_class = RDoc::Parser.can_parse_by_name(filename))
-
#update_output_dir(op_dir, time, last = {})
Update the flag file in an output directory.
-
#watch_files
Files watched by the live preview server.
-
#auto_discovered_rbs_signature_file?(file) ⇒ Boolean
Internal use only
Returns true for project RBS files that are auto-discovered for signature loading.
-
#auto_discovered_rbs_signature_mtimes
Internal use only
Returns mtimes for auto-discovered RBS signature files.
Constructor Details
.new ⇒ RDoc
Creates a new RDoc instance. Call #document to parse files and
generate documentation.
# File 'lib/rdoc/rdoc.rb', line 101
def initialize @current = nil @generator = nil @last_modified = {} @old_siginfo = nil @options = nil @stats = nil @store = nil end
Class Attribute Details
.current (rw)
Active RDoc instance
# File 'lib/rdoc/rdoc.rb', line 86
def self.current @current end
.current=(rdoc) (rw)
Sets the active RDoc instance
# File 'lib/rdoc/rdoc.rb', line 93
def self.current=(rdoc) @current = rdoc end
Class Method Details
.add_generator(klass)
Add klass that can generate output after parsing
# File 'lib/rdoc/rdoc.rb', line 78
def self.add_generator(klass) name = klass.name.sub(/^RDoc::Generator::/, '').downcase GENERATORS[name] = klass end
.rbs_discovery_extension?(extension) ⇒ Boolean
Returns true when extension is the RBS gem's RDoc discovery hook.
Released RBS gems install their plugin through this hook, so skip it to
avoid replacing the built-in parser during discovery.
# File 'lib/rdoc/rdoc.rb', line 688
def self.rbs_discovery_extension?(extension) # :nodoc: extension = File.(extension) Gem::Specification.find_all_by_name('rbs').any? do |spec| File.('lib/rdoc/discover.rb', spec.full_gem_path) == extension end end
Instance Attribute Details
#auto_discovered_rbs_signatures_changed? ⇒ Boolean (readonly)
Returns true if any auto-discovered RBS signature file has changed since the last run.
# File 'lib/rdoc/rdoc.rb', line 608
def auto_discovered_rbs_signatures_changed? current = auto_discovered_rbs_signature_mtimes previous = @last_modified.select { |file, _| auto_discovered_rbs_signature_file?(file) } return true unless (previous.keys - current.keys).empty? current.any? do |file, mtime| last_modified = @last_modified[file] last_modified.nil? || mtime.to_i > last_modified.to_i end end
#generator (rw)
Generator instance used for creating output
# File 'lib/rdoc/rdoc.rb', line 53
attr_accessor :generator
#last_modified (readonly)
Hash of files and their last modified times.
# File 'lib/rdoc/rdoc.rb', line 58
attr_reader :last_modified
#options (rw)
RDoc options
# File 'lib/rdoc/rdoc.rb', line 63
attr_accessor :
#stats (readonly)
Accessor for statistics. Available after each call to parse_files
# File 'lib/rdoc/rdoc.rb', line 68
attr_reader :stats
#store (rw)
The current documentation store
# File 'lib/rdoc/rdoc.rb', line 73
attr_accessor :store
Instance Method Details
#auto_discovered_rbs_signature_file?(file) ⇒ Boolean
Returns true for project RBS files that are auto-discovered for signature
loading. RDoc parses any selected .rbs file as documentation input, but
only sig/**/*.rbs files are loaded through RBS::EnvironmentLoader for
type signature merging and live-reload bookkeeping.
# File 'lib/rdoc/rdoc.rb', line 642
def auto_discovered_rbs_signature_file?(file) # :nodoc: return false unless File.extname(file) == '.rbs' root = Pathname(@options.root.to_s). relative_path = Pathname(file)..relative_path_from root relative_path.each_filename.first == 'sig' rescue ArgumentError # file and root may be on different drives on Windows false end
#auto_discovered_rbs_signature_files
Returns RBS files that RDoc auto-discovers for signature loading.
# File 'lib/rdoc/rdoc.rb', line 600
def auto_discovered_rbs_signature_files Dir[File.join(@options.root.to_s, 'sig', '**', '*.rbs')].sort end
#auto_discovered_rbs_signature_mtimes
Returns mtimes for auto-discovered RBS signature files.
# File 'lib/rdoc/rdoc.rb', line 656
def auto_discovered_rbs_signature_mtimes # :nodoc: auto_discovered_rbs_signature_files.each_with_object({}) do |file, mtimes| mtime = RDoc.safe_mtime(file) mtimes[file] = mtime if mtime end end
#document(options)
Generates documentation or a coverage report depending upon the settings in #options.
#options can be either an Options instance or an array of strings
equivalent to the strings that would be passed on the command line like
%w[-q -o doc -t My\ Doc\ Title]. #document will automatically
call Options#finish if an options instance was given.
For a list of options, see either Options or rdoc --help.
By default, output will be stored in a directory called "doc" below the current directory, so make sure you're somewhere writable before invoking.
# File 'lib/rdoc/rdoc.rb', line 475
def document() if RDoc::Options === then @options = else @options = RDoc::Options. @options.parse end @options.finish @store = RDoc::Store.new(@options) if @options.pipe then handle_pipe exit end if @options.server_port @store.load_cache parse_files @options.files record_auto_discovered_rbs_signature_mtimes @options.default_title = "RDoc Documentation" load_auto_discovered_rbs_signatures @store.complete @options.visibility start_server exit end unless @options.coverage_report then @last_modified = setup_output_dir @options.op_dir, @options.force_update end @start_time = Time.now @store.load_cache auto_discovered_rbs_signatures_changed = auto_discovered_rbs_signatures_changed? # When only auto-discovered RBS signatures changed, no Ruby file would be # reparsed under normal mtime checks. The store cache holds class metadata # but not live RDoc::Context objects, so the generator would have nothing # to iterate. Force a full reparse so updated signatures show up in the # rendered output. @last_modified.clear if auto_discovered_rbs_signatures_changed file_info = parse_files @options.files record_auto_discovered_rbs_signature_mtimes @options.default_title = "RDoc Documentation" load_auto_discovered_rbs_signatures @store.complete @options.visibility @stats.coverage_level = @options.coverage_report if @options.coverage_report then puts puts @stats.report elsif file_info.empty? && !auto_discovered_rbs_signatures_changed then $stderr.puts "\nNo newer files." unless @options.quiet else gen_klass = @options.generator @generator = gen_klass.new @store, @options generate end if @stats and (@options.coverage_report or not @options.quiet) then puts puts @stats.summary end exit @stats.fully_documented? if @options.coverage_report end
#error(msg)
Report an error message and exit
# File 'lib/rdoc/rdoc.rb', line 114
def error(msg) raise RDoc::Error, msg end
#gather_files(files)
Gathers a set of parseable files from the files and directories listed in
files.
# File 'lib/rdoc/rdoc.rb', line 122
def gather_files(files) files = [@options.root.to_s] if files.empty? file_list = normalized_file_list files, true, @options.exclude file_list = remove_unparseable(file_list) if file_list.count {|name, mtime| file_list[name] = @last_modified[name] unless mtime mtime } > 0 @last_modified.replace file_list file_list.keys.sort else [] end end
#generate
Generates documentation for file_info (from #parse_files) into the
output dir using the generator selected
by the RDoc options
# File 'lib/rdoc/rdoc.rb', line 560
def generate if @options.dry_run then # do nothing @generator.generate else Dir.chdir @options.op_dir do unless @options.quiet then $stderr.puts "\nGenerating #{@generator.class.name.sub(/^.*::/, '')} format into #{Dir.pwd}..." uri = "file://#{Dir.pwd}/index.html" ref = $stderr.tty? ? "\e]8;;#{uri}\e\\#{uri}\e]8;;\e\\" : uri $stderr.puts "\nYou can visit the home page at: #{ref}" end @generator.generate update_output_dir '.', @start_time, @last_modified end end end
#handle_pipe
Turns RDoc from stdin into HTML
#install_siginfo_handler
Installs a siginfo handler that prints the current filename.
# File 'lib/rdoc/rdoc.rb', line 158
def install_siginfo_handler return unless Signal.list.include? 'INFO' @old_siginfo = trap 'INFO' do puts @current if @current end end
#list_files_in_directory(dir)
# File 'lib/rdoc/rdoc.rb', line 315
def list_files_in_directory(dir) files = Dir.glob File.join(dir, "*") normalized_file_list files, false, @options.exclude end
#load_auto_discovered_rbs_signatures
Loads RBS type signatures from the project's sig directory and RBS
stdlib, then merges them into the store's code objects.
# File 'lib/rdoc/rdoc.rb', line 583
def load_auto_discovered_rbs_signatures sig_dirs = [] sig_dir = File.join(@options.root.to_s, 'sig') sig_dirs << sig_dir if File.directory?(sig_dir) signatures = RDoc::RbsHelper.load_signatures(*sig_dirs) @store.merge_rbs_signatures(signatures) rescue RBS::BaseError, Errno::ENOENT, LoadError => e # In server mode, a previous successful load may have populated the store; # drop those signatures so a now-broken sig file doesn't keep showing # stale types alongside the warning. @store.clear_rbs_signatures @options.warn "Failed to load RBS type signatures: #{e.}" end
#normalized_file_list(relative_files, force_doc = false, exclude_pattern = nil)
Given a list of files and directories, create a list of all the Ruby files they contain.
If force_doc is true we always add the given files, if false, only
add files that we guarantee we can parse. It is true when looking at
files given on the command line, false when recursing through
subdirectories.
The effect of this is that if you want a file with a non-standard extension parsed, you must name it explicitly.
# File 'lib/rdoc/rdoc.rb', line 267
def normalized_file_list(relative_files, force_doc = false, exclude_pattern = nil) file_list = {} relative_files.each do |rel_file_name| rel_file_name = rel_file_name.sub(/^\.\//, '') next if rel_file_name.end_with? 'created.rid' next if exclude_pattern && exclude_pattern =~ rel_file_name stat = File.stat rel_file_name rescue next case type = stat.ftype when "file" then mtime = (stat.mtime unless (last_modified = @last_modified[rel_file_name] and stat.mtime.to_i <= last_modified.to_i)) if force_doc or RDoc::Parser.can_parse(rel_file_name) then file_list[rel_file_name] = mtime end when "directory" then next if UNCONDITIONALLY_SKIPPED_DIRECTORIES.include?(rel_file_name) basename = File.basename(rel_file_name) next if .skip_tests && TEST_SUITE_DIRECTORY_NAMES.include?(basename) created_rid = File.join rel_file_name, "created.rid" next if File.file? created_rid dot_doc = File.join rel_file_name, RDoc::DOT_DOC_FILENAME if File.file? dot_doc then file_list.update(parse_dot_doc_file(rel_file_name, dot_doc)) else file_list.update(list_files_in_directory(rel_file_name)) end else warn "rdoc can't parse the #{type} #{rel_file_name}" end end file_list end
#output_flag_file(op_dir)
Return the path name of the flag file in an output directory.
# File 'lib/rdoc/rdoc.rb', line 232
def output_flag_file(op_dir) File.join op_dir, "created.rid" end
#parse_dot_doc_file(in_dir, filename)
The #document file contains a list of file and directory name patterns, representing candidates for documentation. It may also contain comments (starting with '#')
# File 'lib/rdoc/rdoc.rb', line 241
def parse_dot_doc_file(in_dir, filename) # read and strip comments patterns = File.read(filename).gsub(/#.*/, '') result = {} patterns.split(' ').each do |patt| candidates = Dir.glob(File.join(in_dir, patt)) result.update normalized_file_list(candidates, false, @options.exclude) end result end
#parse_file(filename)
Parses filename and returns an TopLevel
# File 'lib/rdoc/rdoc.rb', line 324
def parse_file(filename) encoding = @options.encoding filename = filename.encode encoding @stats.add_file filename return if RDoc::Parser.binary? filename content = RDoc::Encoding.read_file filename, encoding return unless content top_level = @store.add_file filename, relative_name: relative_path_for(filename) parser = RDoc::Parser.for top_level, content, @options, @stats return unless parser parser.scan # restart documentation for the classes & modules found top_level.classes_or_modules.each do |cm| cm.done_documenting = false end top_level rescue Errno::EACCES => e $stderr.puts <<-EOF Unable to read #{filename}, #{e.} Please check the permissions for this file. Perhaps you do not have access to it or perhaps the original author's permissions are to restrictive. If the this is not your library please report a bug to the author. EOF rescue => e syntax_check_command = syntax_check_command_for filename, parser&.class = if syntax_check_command <<~MESSAGE Before reporting this, could you check that the file you're documenting has proper syntax: #{syntax_check_command} MESSAGE else <<~MESSAGE Before reporting this, could you check that the file you're documenting has proper syntax for its language? MESSAGE end $stderr.puts <<-EOF #{} RDoc's parsers are not full language parsers and may fail when fed invalid source files. The internal error was: \t(#{e.class}) #{e.} EOF $stderr.puts e.backtrace.join("\n\t") if $DEBUG_RDOC raise e end
#parse_files(files)
Parse each file on the command line, recursively entering directories.
# File 'lib/rdoc/rdoc.rb', line 426
def parse_files(files) file_list = gather_files files @stats = RDoc::Stats.new @store, file_list.length, @options.verbosity return [] if file_list.empty? # This workaround can be removed after the :main: directive is removed = @options.dup @stats.begin_adding file_info = file_list.map do |filename| @current = filename parse_file filename end.compact @store.resolve_c_superclasses @stats.done_adding @options = file_info end
#record_auto_discovered_rbs_signature_mtimes
Records auto-discovered RBS signature file mtimes so normal generation freshness checks and the live server watcher can see signature-only edits.
# File 'lib/rdoc/rdoc.rb', line 624
def record_auto_discovered_rbs_signature_mtimes @last_modified.reject! { |file, _| auto_discovered_rbs_signature_file?(file) } @last_modified.merge! auto_discovered_rbs_signature_mtimes end
#relative_path_for(filename)
Returns the relative path for filename against options.root (and
options.page_dir when set). This is the key used by Store to
identify files.
# File 'lib/rdoc/rdoc.rb', line 406
def relative_path_for(filename) filename_path = Pathname(filename). begin relative_path = filename_path.relative_path_from @options.root rescue ArgumentError relative_path = filename_path end if @options.page_dir && relative_path.to_s.start_with?(@options.page_dir.to_s) relative_path = relative_path.relative_path_from @options.page_dir end relative_path.to_s end
#remove_siginfo_handler
Removes a siginfo handler and replaces the previous
# File 'lib/rdoc/rdoc.rb', line 675
def remove_siginfo_handler return unless Signal.list.key? 'INFO' handler = @old_siginfo || 'DEFAULT' trap 'INFO', handler end
#remove_unparseable(files)
Removes file extensions known to be unparseable from files and TAGS
files for emacs and vim.
# File 'lib/rdoc/rdoc.rb', line 453
def remove_unparseable(files) files.reject do |file, *| file =~ /\.(?:class|eps|erb|scpt\.txt|svg|ttf|yml)\z/i or (file =~ /tags\z/i and /\A(\f\n[^,],\d$|!_TAG_)/.match?(File.binread(file, 100))) end end
#setup_output_dir(dir, force)
Create an output dir if it doesn't exist. If it does exist, but doesn't contain the flag file created.rid then we refuse to use it, as we may clobber some manually generated documentation
# File 'lib/rdoc/rdoc.rb', line 171
def setup_output_dir(dir, force) flag_file = output_flag_file dir last = {} if @options.dry_run then # do nothing elsif File.exist? dir then error "#{dir} exists and is not a directory" unless File.directory? dir begin File.open flag_file do |io| unless force then Time.parse io.gets io.each do |line| file, time = line.split "\t", 2 time = Time.parse(time) rescue next last[file] = time end end end rescue SystemCallError, TypeError error <<-ERROR Directory #{dir} already exists, but it looks like it isn't an RDoc directory. Because RDoc doesn't want to risk destroying any of your existing files, you'll need to specify a different output directory name (using the --op <dir> option) ERROR end unless @options.force_output else FileUtils.mkdir_p dir FileUtils.touch flag_file end last end
#start_server
Starts a live-reloading HTTP server for previewing documentation. Called from #document when --server is given.
#syntax_check_command_for(filename, parser_class = RDoc::Parser.can_parse_by_name(filename))
[ GitHub ]# File 'lib/rdoc/rdoc.rb', line 391
def syntax_check_command_for(filename, parser_class = RDoc::Parser.can_parse_by_name(filename)) if parser_class == RDoc::Parser::Ruby "#{Gem.ruby} -c #{filename}" elsif parser_class == RDoc::Parser::C cc = ENV['CC'] cc = 'cc' if cc.nil? || cc.empty? "#{cc} -fsyntax-only #{filename}" end end
#update_output_dir(op_dir, time, last = {})
Update the flag file in an output directory.
# File 'lib/rdoc/rdoc.rb', line 215
def update_output_dir(op_dir, time, last = {}) return if @options.dry_run or not @options.update_output_dir unless ENV['SOURCE_DATE_EPOCH'].nil? time = Time.at(ENV['SOURCE_DATE_EPOCH'].to_i).gmtime end File.open output_flag_file(op_dir), "w" do |f| f.puts time.rfc2822 last.each do |n, t| f.puts "#{n}\t#{t.rfc2822}" end end end
#watch_files
Files watched by the live preview server.
# File 'lib/rdoc/rdoc.rb', line 632
def watch_files (@last_modified.keys + auto_discovered_rbs_signature_files).uniq end