summaryrefslogtreecommitdiff
path: root/lib/rdoc/ri
diff options
context:
space:
mode:
authordrbrain <drbrain@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2012-11-27 04:28:14 +0000
committerdrbrain <drbrain@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2012-11-27 04:28:14 +0000
commit1c279a7d2753949c725754e1302f791b76358114 (patch)
tree36aa3bdde250e564445eba5f2e25fcb96bcb6cef /lib/rdoc/ri
parentc72f0daa877808e4fa5018b3191ca09d4b97c03d (diff)
* lib/rdoc*: Updated to RDoc 4.0 (pre-release)
* bin/rdoc: ditto * test/rdoc: ditto * NEWS: Updated with RDoc 4.0 information git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37889 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib/rdoc/ri')
-rw-r--r--lib/rdoc/ri/driver.rb404
-rw-r--r--lib/rdoc/ri/paths.rb121
-rw-r--r--lib/rdoc/ri/store.rb356
3 files changed, 414 insertions, 467 deletions
diff --git a/lib/rdoc/ri/driver.rb b/lib/rdoc/ri/driver.rb
index 26304dca96..4beda55881 100644
--- a/lib/rdoc/ri/driver.rb
+++ b/lib/rdoc/ri/driver.rb
@@ -11,11 +11,7 @@ begin
rescue LoadError
end
-require 'rdoc/ri'
-require 'rdoc/ri/paths'
-require 'rdoc/markup'
-require 'rdoc/markup/formatter'
-require 'rdoc/text'
+require 'rdoc'
##
# For RubyGems backwards compatibility
@@ -61,6 +57,11 @@ class RDoc::RI::Driver
end
##
+ # Show all method documentation following a class or module
+
+ attr_accessor :show_all
+
+ ##
# An RDoc::RI::Store for each entry in the RI path
attr_accessor :stores
@@ -75,17 +76,18 @@ class RDoc::RI::Driver
def self.default_options
options = {}
- options[:use_stdout] = !$stdout.tty?
- options[:width] = 72
options[:interactive] = false
- options[:use_cache] = true
- options[:profile] = false
+ options[:profile] = false
+ options[:show_all] = false
+ options[:use_cache] = true
+ options[:use_stdout] = !$stdout.tty?
+ options[:width] = 72
# By default all standard paths are used.
- options[:use_system] = true
- options[:use_site] = true
- options[:use_home] = true
- options[:use_gems] = true
+ options[:use_system] = true
+ options[:use_site] = true
+ options[:use_home] = true
+ options[:use_gems] = true
options[:extra_doc_dirs] = []
return options
@@ -123,7 +125,11 @@ Usage: #{opt.program_name} [options] [names...]
Where name can be:
- Class | Class::method | Class#method | Class.method | method
+ Class | Module | Module::Class
+
+ Class::method | Class#method | Class.method | method
+
+ gem_name: | gem_name:README | gem_name:History
All class names may be abbreviated to their minimum unambiguous form. If a name
is ambiguous, all valid options will be listed.
@@ -131,12 +137,18 @@ is ambiguous, all valid options will be listed.
A '.' matches either class or instance methods, while #method
matches only instance and ::method matches only class methods.
+README and other files may be displayed by prefixing them with the gem name
+they're contained in. If the gem name is followed by a ':' all files in the
+gem will be shown. The file name extension may be omitted where it is
+unambiguous.
+
For example:
#{opt.program_name} Fil
#{opt.program_name} File
#{opt.program_name} File.new
#{opt.program_name} zip
+ #{opt.program_name} rdoc:README
Note that shell quoting or escaping may be required for method names containing
punctuation:
@@ -151,7 +163,10 @@ To see the default directories ri will search, run:
Specifying the --system, --site, --home, --gems or --doc-dir options will
limit ri to searching only the specified directories.
-Options may also be set in the 'RI' environment variable.
+ri options may be set in the 'RI' environment variable.
+
+The ri pager can be set with the 'RI_PAGER' environment variable or the
+'PAGER' environment variable.
EOT
opt.separator nil
@@ -159,64 +174,74 @@ Options may also be set in the 'RI' environment variable.
opt.separator nil
- formatters = RDoc::Markup.constants.grep(/^To[A-Z][a-z]+$/).sort
- formatters = formatters.sort.map do |formatter|
- formatter.to_s.sub('To', '').downcase
+ opt.on("--[no-]interactive", "-i",
+ "In interactive mode you can repeatedly",
+ "look up methods with autocomplete.") do |interactive|
+ options[:interactive] = interactive
end
- opt.on("--format=NAME", "-f",
- "Uses the selected formatter. The default",
- "formatter is bs for paged output and ansi",
- "otherwise. Valid formatters are:",
- formatters.join(' '), formatters) do |value|
- options[:formatter] = RDoc::Markup.const_get "To#{value.capitalize}"
+ opt.separator nil
+
+ opt.on("--[no-]all", "-a",
+ "Show all documentation for a class or",
+ "module.") do |show_all|
+ options[:show_all] = show_all
end
opt.separator nil
- opt.on("--no-pager", "-T",
- "Send output directly to stdout,",
- "rather than to a pager.") do
- options[:use_stdout] = true
+ opt.on("--[no-]list", "-l",
+ "List classes ri knows about.") do |list|
+ options[:list] = list
end
opt.separator nil
- opt.on("--width=WIDTH", "-w", OptionParser::DecimalInteger,
- "Set the width of the output.") do |value|
- options[:width] = value
+ opt.on("--[no-]pager", "-T",
+ "Send output directly to stdout,",
+ "rather than to a pager.") do |use_pager|
+ options[:use_stdout] = !use_pager
end
opt.separator nil
- opt.on("--interactive", "-i",
- "In interactive mode you can repeatedly",
- "look up methods with autocomplete.") do
- options[:interactive] = true
+ opt.on("--width=WIDTH", "-w", OptionParser::DecimalInteger,
+ "Set the width of the output.") do |width|
+ options[:width] = width
end
opt.separator nil
- opt.on("--list", "-l",
- "List classes ri knows about.") do
- options[:list] = true
+ opt.on("--server [PORT]", Integer,
+ "Run RDoc server on the given port.",
+ "The default port is 8214.") do |port|
+ options[:server] = port || 8214
end
opt.separator nil
- opt.on("--[no-]profile",
- "Run with the ruby profiler") do |value|
- options[:profile] = value
+ formatters = RDoc::Markup.constants.grep(/^To[A-Z][a-z]+$/).sort
+ formatters = formatters.sort.map do |formatter|
+ formatter.to_s.sub('To', '').downcase
+ end
+ formatters -= %w[html label test] # remove useless output formats
+
+ opt.on("--format=NAME", "-f",
+ "Uses the selected formatter. The default",
+ "formatter is bs for paged output and ansi",
+ "otherwise. Valid formatters are:",
+ formatters.join(' '), formatters) do |value|
+ options[:formatter] = RDoc::Markup.const_get "To#{value.capitalize}"
end
opt.separator nil
opt.separator "Data source options:"
opt.separator nil
- opt.on("--list-doc-dirs",
+ opt.on("--[no-]list-doc-dirs",
"List the directories from which ri will",
- "source documentation on stdout and exit.") do
- options[:list_doc_dirs] = true
+ "source documentation on stdout and exit.") do |list_doc_dirs|
+ options[:list_doc_dirs] = list_doc_dirs
end
opt.separator nil
@@ -284,6 +309,13 @@ Options may also be set in the 'RI' environment variable.
opt.separator "Debug options:"
opt.separator nil
+ opt.on("--[no-]profile",
+ "Run with the ruby profiler") do |value|
+ options[:profile] = value
+ end
+
+ opt.separator nil
+
opt.on("--dump=CACHE", File,
"Dumps data from an ri cache or data file") do |value|
options[:dump_path] = value
@@ -356,7 +388,12 @@ Options may also be set in the 'RI' environment variable.
@list_doc_dirs = options[:list_doc_dirs]
@interactive = options[:interactive]
+ @server = options[:server]
@use_stdout = options[:use_stdout]
+ @show_all = options[:show_all]
+
+ # pager process for jruby
+ @jruby_pager_process = nil
end
##
@@ -404,15 +441,23 @@ Options may also be set in the 'RI' environment variable.
end
##
- # Adds +includes+ to +out+
+ # Adds +extends+ to +out+
- def add_includes out, includes
- return if includes.empty?
+ def add_extends out, extends
+ add_extension_modules out, 'Extended by', extends
+ end
+
+ ##
+ # Adds a list of +extensions+ to this module of the given +type+ to +out+.
+ # add_includes and add_extends call this, so you should use those directly.
+
+ def add_extension_modules out, type, extensions
+ return if extensions.empty?
out << RDoc::Markup::Rule.new(1)
- out << RDoc::Markup::Heading.new(1, "Includes:")
+ out << RDoc::Markup::Heading.new(1, "#{type}:")
- includes.each do |modules, store|
+ extensions.each do |modules, store|
if modules.length == 1 then
include = modules.first
name = include.name
@@ -450,6 +495,37 @@ Options may also be set in the 'RI' environment variable.
end
##
+ # Adds +includes+ to +out+
+
+ def add_includes out, includes
+ add_extension_modules out, 'Includes', includes
+ end
+
+ ##
+ # Looks up the method +name+ and adds it to +out+
+
+ def add_method out, name
+ filtered = lookup_method name
+
+ method_out = method_document name, filtered
+
+ out.concat method_out.parts
+ end
+
+ ##
+ # Adds documentation for all methods in +klass+ to +out+
+
+ def add_method_documentation out, klass
+ klass.method_list.each do |method|
+ begin
+ add_method out, method.full_name
+ rescue NotFoundError
+ next
+ end
+ end
+ end
+
+ ##
# Adds a list of +methods+ to +out+ with a heading of +name+
def add_method_list out, methods, name
@@ -458,10 +534,10 @@ Options may also be set in the 'RI' environment variable.
out << RDoc::Markup::Heading.new(1, "#{name}:")
out << RDoc::Markup::BlankLine.new
- if @use_stdout and !@interactive
- out.push(*methods.map do |method|
+ if @use_stdout and !@interactive then
+ out.concat methods.map { |method|
RDoc::Markup::Verbatim.new method
- end)
+ }
else
out << RDoc::Markup::IndentedParagraph.new(2, methods.join(', '))
end
@@ -493,8 +569,8 @@ Options may also be set in the 'RI' environment variable.
klasses = klasses - seen
- ancestors.push(*klasses)
- unexamined.push(*klasses)
+ ancestors.concat klasses
+ unexamined.concat klasses
end
ancestors.reverse
@@ -509,7 +585,7 @@ Options may also be set in the 'RI' environment variable.
##
# Builds a RDoc::Markup::Document from +found+, +klasess+ and +includes+
- def class_document name, found, klasses, includes
+ def class_document name, found, klasses, includes, extends
also_in = []
out = RDoc::Markup::Document.new
@@ -517,6 +593,7 @@ Options may also be set in the 'RI' environment variable.
add_class out, name, klasses
add_includes out, includes
+ add_extends out, extends
found.each do |store, klass|
comment = klass.comment
@@ -542,7 +619,7 @@ Options may also be set in the 'RI' environment variable.
parts.flatten!
parts.pop
- out.push(*parts)
+ out.concat parts
else
out << comment
end
@@ -559,13 +636,13 @@ Options may also be set in the 'RI' environment variable.
constants = klass.constants.sort_by { |constant| constant.name }
- list.push(*constants.map do |constant|
+ list.items.concat constants.map { |constant|
parts = constant.comment.parts if constant.comment
parts << RDoc::Markup::Paragraph.new('[not documented]') if
parts.empty?
RDoc::Markup::ListItem.new(constant.name, *parts)
- end)
+ }
out << list
out << RDoc::Markup::BlankLine.new
@@ -574,6 +651,8 @@ Options may also be set in the 'RI' environment variable.
add_method_list out, class_methods, 'Class methods'
add_method_list out, instance_methods, 'Instance methods'
add_method_list out, attributes, 'Attributes'
+
+ add_method_documentation out, klass if @show_all
end
add_also_in out, also_in
@@ -601,26 +680,29 @@ Options may also be set in the 'RI' environment variable.
end
##
- # Returns the stores wherin +name+ is found along with the classes and
- # includes that match it
+ # Returns the stores wherein +name+ is found along with the classes,
+ # extends and includes that match it
- def classes_and_includes_for name
+ def classes_and_includes_and_extends_for name
klasses = []
+ extends = []
includes = []
found = @stores.map do |store|
begin
klass = store.load_class name
klasses << klass
+ extends << [klass.extends, store] if klass.extends
includes << [klass.includes, store] if klass.includes
[store, klass]
- rescue Errno::ENOENT
+ rescue RDoc::Store::MissingFileError
end
end.compact
+ extends.reject! do |modules,| modules.empty? end
includes.reject! do |modules,| modules.empty? end
- [found, klasses, includes]
+ [found, klasses, includes, extends]
end
##
@@ -659,7 +741,7 @@ Options may also be set in the 'RI' environment variable.
completions << "#{klass}#{selector}"
end
- completions.push(*methods)
+ completions.concat methods
end
completions.sort.uniq
@@ -682,11 +764,12 @@ Options may also be set in the 'RI' environment variable.
def display_class name
return if name =~ /#|\./
- found, klasses, includes = classes_and_includes_for name
+ found, klasses, includes, extends =
+ classes_and_includes_and_extends_for name
return if found.empty?
- out = class_document name, found, klasses, includes
+ out = class_document name, found, klasses, includes, extends
display out
end
@@ -695,13 +778,9 @@ Options may also be set in the 'RI' environment variable.
# Outputs formatted RI data for method +name+
def display_method name
- found = load_methods_matching name
-
- raise NotFoundError, name if found.empty?
-
- filtered = filter_methods found, name
+ out = RDoc::Markup::Document.new
- out = method_document name, filtered
+ add_method out, name
display out
end
@@ -713,6 +792,11 @@ Options may also be set in the 'RI' environment variable.
# be guessed, raises an error if +name+ couldn't be guessed.
def display_name name
+ if name =~ /\w:(\w|$)/ then
+ display_page name
+ return true
+ end
+
return true if display_class name
display_method name if name =~ /::|#|\./
@@ -727,7 +811,7 @@ Options may also be set in the 'RI' environment variable.
page do |io|
io.puts "#{name} not found, maybe you meant:"
io.puts
- io.puts matches.join("\n")
+ io.puts matches.sort.join("\n")
end
false
@@ -745,6 +829,61 @@ Options may also be set in the 'RI' environment variable.
end
##
+ # Outputs formatted RI data for page +name+.
+
+ def display_page name
+ store_name, page_name = name.split ':', 2
+
+ store = @stores.find { |s| s.source == store_name }
+
+ return display_page_list store if page_name.empty?
+
+ pages = store.cache[:pages]
+
+ unless pages.include? page_name then
+ found_names = pages.select do |n|
+ n =~ /^#{Regexp.escape page_name}\.[^.]+$/
+ end
+
+ if found_names.length > 1 then
+ return display_page_list store, found_names, page_name
+ end
+
+ page_name = found_names.first
+ end
+
+ page = store.load_page page_name
+
+ display page.comment
+ end
+
+ ##
+ # Outputs a formatted RI page list for the pages in +store+.
+
+ def display_page_list store, pages = store.cache[:pages], search = nil
+ out = RDoc::Markup::Document.new
+
+ title = if search then
+ "#{search} pages"
+ else
+ 'Pages'
+ end
+
+ out << RDoc::Markup::Heading.new(1, "#{title} in #{store.friendly_path}")
+ out << RDoc::Markup::BlankLine.new
+
+ list = RDoc::Markup::List.new(:BULLET)
+
+ pages.each do |page|
+ list << RDoc::Markup::Paragraph.new(page)
+ end
+
+ out << list
+
+ display out
+ end
+
+ ##
# Expands abbreviated klass +klass+ into a fully-qualified class. "Zl::Da"
# will be expanded to Zlib::DataError.
@@ -776,7 +915,12 @@ Options may also be set in the 'RI' environment variable.
return [selector, method].join if klass.empty?
- "#{expand_class klass}#{selector}#{method}"
+ case selector
+ when ':' then
+ [find_store(klass), selector, method]
+ else
+ [expand_class(klass), selector, method]
+ end.join
end
##
@@ -841,6 +985,55 @@ Options may also be set in the 'RI' environment variable.
end
##
+ # Finds the given +pager+ for jruby. Returns an IO if +pager+ was found.
+ #
+ # Returns false if +pager+ does not exist.
+ #
+ # Returns nil if the jruby JVM doesn't support ProcessBuilder redirection
+ # (1.6 and older).
+
+ def find_pager_jruby pager
+ require 'java'
+ require 'shellwords'
+
+ return nil unless java.lang.ProcessBuilder.constants.include? :Redirect
+
+ pager = Shellwords.split pager
+
+ pb = java.lang.ProcessBuilder.new(*pager)
+ pb = pb.redirect_output java.lang.ProcessBuilder::Redirect::INHERIT
+
+ @jruby_pager_process = pb.start
+
+ input = @jruby_pager_process.output_stream
+
+ io = input.to_io
+ io.sync = true
+ io
+ rescue java.io.IOException
+ false
+ end
+
+ ##
+ # Finds a store that matches +name+ which can be the name of a gem, "ruby",
+ # "home" or "site".
+ #
+ # See also RDoc::Store#source
+
+ def find_store name
+ @stores.each do |store|
+ source = store.source
+
+ return source if source == name
+
+ return source if
+ store.type == :gem and source =~ /^#{Regexp.escape name}-\d/
+ end
+
+ raise RDoc::RI::Driver::NotFoundError, name
+ end
+
+ ##
# Creates a new RDoc::Markup::Formatter. If a formatter is given with -f,
# use it. If we're outputting to a pager, use bs, otherwise ansi.
@@ -909,7 +1102,7 @@ Options may also be set in the 'RI' environment variable.
classes = []
stores.each do |store|
- classes << store.modules
+ classes << store.module_names
end
classes = classes.flatten.uniq.sort
@@ -951,7 +1144,7 @@ Options may also be set in the 'RI' environment variable.
"#{klass}##{match}"
end
- found.push(*matches)
+ found.concat matches
end
end
@@ -965,7 +1158,7 @@ Options may also be set in the 'RI' environment variable.
"#{klass}::#{match}"
end
- found.push(*matches)
+ found.concat matches
end
end
@@ -1012,7 +1205,18 @@ Options may also be set in the 'RI' environment variable.
end
##
- # Builds a RDoc::Markup::Document from +found+, +klasess+ and +includes+
+ # Returns a filtered list of methods matching +name+
+
+ def lookup_method name
+ found = load_methods_matching name
+
+ raise NotFoundError, name if found.empty?
+
+ filter_methods found, name
+ end
+
+ ##
+ # Builds a RDoc::Markup::Document from +found+, +klasses+ and +includes+
def method_document name, filtered
out = RDoc::Markup::Document.new
@@ -1027,6 +1231,7 @@ Options may also be set in the 'RI' environment variable.
unless name =~ /^#{Regexp.escape method.parent_name}/ then
out << RDoc::Markup::Heading.new(3, "Implementation from #{method.parent_name}")
end
+
out << RDoc::Markup::Rule.new(1)
if method.arglists then
@@ -1036,6 +1241,12 @@ Options may also be set in the 'RI' environment variable.
out << RDoc::Markup::Rule.new(1)
end
+ if method.respond_to?(:superclass_method) and method.superclass_method
+ out << RDoc::Markup::BlankLine.new
+ out << RDoc::Markup::Heading.new(4, "(Uses superclass method #{method.superclass_method})")
+ out << RDoc::Markup::Rule.new(1)
+ end
+
out << RDoc::Markup::BlankLine.new
out << method.comment
out << RDoc::Markup::BlankLine.new
@@ -1080,6 +1291,7 @@ Options may also be set in the 'RI' environment variable.
yield pager
ensure
pager.close
+ @jruby_pager_process.wait_for if @jruby_pager_process
end
else
yield $stdout
@@ -1101,10 +1313,10 @@ Options may also be set in the 'RI' environment variable.
# Foo::Bar#baz.
#
# NOTE: Given Foo::Bar, Bar is considered a class even though it may be a
- # method
+ # method
def parse_name name
- parts = name.split(/(::|#|\.)/)
+ parts = name.split(/(::?|#|\.)/)
if parts.length == 1 then
if parts.first =~ /^[a-z]|^([%&*+\/<>^`|~-]|\+@|-@|<<|<=>?|===?|=>|=~|>>|\[\]=?|~@)$/ then
@@ -1135,6 +1347,8 @@ Options may also be set in the 'RI' environment variable.
puts @doc_dirs
elsif @list then
list_known_classes @names
+ elsif @server then
+ start_server
elsif @interactive or @names.empty? then
interactive
else
@@ -1151,6 +1365,8 @@ Options may also be set in the 'RI' environment variable.
def setup_pager
return if @use_stdout
+ jruby = Object.const_defined?(:RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
+
pagers = [ENV['RI_PAGER'], ENV['PAGER'], 'pager', 'less', 'more']
pagers.compact.uniq.each do |pager|
@@ -1160,9 +1376,17 @@ Options may also be set in the 'RI' environment variable.
next unless in_path? pager_cmd
- io = IO.popen(pager, 'w') rescue next
+ if jruby then
+ case io = find_pager_jruby(pager)
+ when nil then break
+ when false then next
+ else io
+ end
+ else
+ io = IO.popen(pager, 'w') rescue next
+ end
- next if $? and $?.exited? # pager didn't work
+ next if $? and $?.pid == io.pid and $?.exited? # pager didn't work
@paging = true
@@ -1174,5 +1398,21 @@ Options may also be set in the 'RI' environment variable.
nil
end
+ ##
+ # Starts a WEBrick server for ri.
+
+ def start_server
+ require 'webrick'
+
+ server = WEBrick::HTTPServer.new :Port => @server
+
+ server.mount '/', RDoc::Servlet
+
+ trap 'INT' do server.shutdown end
+ trap 'TERM' do server.shutdown end
+
+ server.start
+ end
+
end
diff --git a/lib/rdoc/ri/paths.rb b/lib/rdoc/ri/paths.rb
index a3c65bf928..d7ea285eaa 100644
--- a/lib/rdoc/ri/paths.rb
+++ b/lib/rdoc/ri/paths.rb
@@ -1,7 +1,8 @@
require 'rdoc/ri'
##
-# The directories where ri data lives.
+# The directories where ri data lives. Paths can be enumerated via ::each, or
+# queried individually via ::system_dir, ::site_dir, ::home_dir and ::gem_dir.
module RDoc::RI::Paths
@@ -10,15 +11,12 @@ module RDoc::RI::Paths
version = RbConfig::CONFIG['ruby_version']
- base = if RbConfig::CONFIG.key? 'ridir' then
+ BASE = if RbConfig::CONFIG.key? 'ridir' then
File.join RbConfig::CONFIG['ridir'], version
else
File.join RbConfig::CONFIG['datadir'], 'ri', version
end
- SYSDIR = File.join base, "system"
- SITEDIR = File.join base, "site"
-
homedir = begin
File.expand_path('~')
rescue ArgumentError
@@ -32,8 +30,6 @@ module RDoc::RI::Paths
end
#:startdoc:
- @gemdirs = nil
-
##
# Iterates over each selected path yielding the directory and type.
#
@@ -47,16 +43,19 @@ module RDoc::RI::Paths
# :extra:: ri data directory from the command line. Yielded for each
# entry in +extra_dirs+
- def self.each system, site, home, gems, *extra_dirs # :yields: directory, type
+ def self.each system = true, site = true, home = true, gems = :latest, *extra_dirs # :yields: directory, type
+ return enum_for __method__, system, site, home, gems, *extra_dirs unless
+ block_given?
+
extra_dirs.each do |dir|
yield dir, :extra
end
- yield SYSDIR, :system if system
- yield SITEDIR, :site if site
- yield HOMEDIR, :home if home and HOMEDIR
+ yield system_dir, :system if system
+ yield site_dir, :site if site
+ yield home_dir, :home if home and HOMEDIR
- gemdirs.each do |dir|
+ gemdirs(gems).each do |dir|
yield dir, :gem
end if gems
@@ -64,36 +63,72 @@ module RDoc::RI::Paths
end
##
- # The latest installed gems' ri directories
+ # The ri directory for the gem with +gem_name+.
- def self.gemdirs
- return @gemdirs if @gemdirs
+ def self.gem_dir name, version
+ req = Gem::Requirement.new "= #{version}"
- require 'rubygems' unless defined?(Gem)
+ spec = Gem::Specification.find_by_name name, req
- # HACK dup'd from Gem.latest_partials and friends
- all_paths = []
+ File.join spec.doc_dir, 'ri'
+ end
- all_paths = Gem.path.map do |dir|
- Dir[File.join(dir, 'doc', '*', 'ri')]
- end.flatten
+ ##
+ # The latest installed gems' ri directories. +filter+ can be :all or
+ # :latest.
+ #
+ # A +filter+ :all includes all versions of gems and includes gems without
+ # ri documentation.
+
+ def self.gemdirs filter = :latest
+ require 'rubygems' unless defined?(Gem)
ri_paths = {}
- all_paths.each do |dir|
- base = File.basename File.dirname(dir)
- if base =~ /(.*)-((\d+\.)*\d+)/ then
- name, version = $1, $2
- ver = Gem::Version.new version
- if ri_paths[name].nil? or ver > ri_paths[name][0] then
- ri_paths[name] = [ver, dir]
+ all = Gem::Specification.map do |spec|
+ [File.join(spec.doc_dir, 'ri'), spec.name, spec.version]
+ end
+
+ if filter == :all then
+ gemdirs = []
+
+ all.group_by do |_, name, _|
+ name
+ end.sort_by do |group, _|
+ group
+ end.map do |group, items|
+ items.sort_by do |_, _, version|
+ version
+ end.reverse_each do |dir,|
+ gemdirs << dir
end
end
+
+ return gemdirs
+ end
+
+ all.each do |dir, name, ver|
+ next unless File.exist? dir
+
+ if ri_paths[name].nil? or ver > ri_paths[name].first then
+ ri_paths[name] = [ver, name, dir]
+ end
end
- @gemdirs = ri_paths.map { |k,v| v.last }.sort
+ ri_paths.sort_by { |_, (_, name, _)| name }.map { |k, v| v.last }
rescue LoadError
- @gemdirs = []
+ []
+ end
+
+ ##
+ # The location of the rdoc data in the user's home directory.
+ #
+ # Like ::system, ri data in the user's home directory is rare and predates
+ # libraries distributed via RubyGems. ri data is rarely generated into this
+ # directory.
+
+ def self.home_dir
+ HOMEDIR
end
##
@@ -102,7 +137,7 @@ module RDoc::RI::Paths
#
# See also ::each
- def self.path(system, site, home, gems, *extra_dirs)
+ def self.path(system = true, site = true, home = true, gems = :latest, *extra_dirs)
path = raw_path system, site, home, gems, *extra_dirs
path.select { |directory| File.directory? directory }
@@ -124,5 +159,29 @@ module RDoc::RI::Paths
path.compact
end
+ ##
+ # The location of ri data installed into the site dir.
+ #
+ # Historically this was available for documentation installed by ruby
+ # libraries predating RubyGems. It is unlikely to contain any content for
+ # modern ruby installations.
+
+ def self.site_dir
+ File.join BASE, 'site'
+ end
+
+ ##
+ # The location of the built-in ri data.
+ #
+ # This data is built automatically when `make` is run when ruby is
+ # installed. If you did not install ruby by hand you may need to install
+ # the documentation yourself. Please consult the documentation for your
+ # package manager or ruby installer for details. You can also use the
+ # rdoc-data gem to install system ri data for common versions of ruby.
+
+ def self.system_dir
+ File.join BASE, 'system'
+ end
+
end
diff --git a/lib/rdoc/ri/store.rb b/lib/rdoc/ri/store.rb
index fe4ccc244d..9fa9bbb03c 100644
--- a/lib/rdoc/ri/store.rb
+++ b/lib/rdoc/ri/store.rb
@@ -1,358 +1,6 @@
-require 'rdoc/code_objects'
-require 'fileutils'
+module RDoc::RI
-##
-# A set of ri data.
-#
-# The store manages reading and writing ri data for a project (gem, path,
-# etc.) and maintains a cache of methods, classes and ancestors in the
-# store.
-#
-# The store maintains a #cache of its contents for faster lookup. After
-# adding items to the store it must be flushed using #save_cache. The cache
-# contains the following structures:
-#
-# @cache = {
-# :class_methods => {}, # class name => class methods
-# :instance_methods => {}, # class name => instance methods
-# :attributes => {}, # class name => attributes
-# :modules => [], # classes and modules in this store
-# :ancestors => {}, # class name => ancestor names
-# }
-#--
-# TODO need to store the list of files and prune classes
-
-class RDoc::RI::Store
-
- ##
- # If true this Store will not write any files
-
- attr_accessor :dry_run
-
- ##
- # Path this store reads or writes
-
- attr_accessor :path
-
- ##
- # Type of ri datastore this was loaded from. See RDoc::RI::Driver,
- # RDoc::RI::Paths.
-
- attr_accessor :type
-
- ##
- # The contents of the Store
-
- attr_reader :cache
-
- ##
- # The encoding of the contents in the Store
-
- attr_accessor :encoding
-
- ##
- # Creates a new Store of +type+ that will load or save to +path+
-
- def initialize path, type = nil
- @dry_run = false
- @type = type
- @path = path
- @encoding = nil
-
- @cache = {
- :ancestors => {},
- :attributes => {},
- :class_methods => {},
- :encoding => @encoding,
- :instance_methods => {},
- :modules => [],
- }
- end
-
- ##
- # Ancestors cache accessor. Maps a klass name to an Array of its ancestors
- # in this store. If Foo in this store inherits from Object, Kernel won't be
- # listed (it will be included from ruby's ri store).
-
- def ancestors
- @cache[:ancestors]
- end
-
- ##
- # Attributes cache accessor. Maps a class to an Array of its attributes.
-
- def attributes
- @cache[:attributes]
- end
-
- ##
- # Path to the cache file
-
- def cache_path
- File.join @path, 'cache.ri'
- end
-
- ##
- # Path to the ri data for +klass_name+
-
- def class_file klass_name
- name = klass_name.split('::').last
- File.join class_path(klass_name), "cdesc-#{name}.ri"
- end
-
- ##
- # Class methods cache accessor. Maps a class to an Array of its class
- # methods (not full name).
-
- def class_methods
- @cache[:class_methods]
- end
-
- ##
- # Path where data for +klass_name+ will be stored (methods or class data)
-
- def class_path klass_name
- File.join @path, *klass_name.split('::')
- end
-
- ##
- # Removes empty items and ensures item in each collection are unique and
- # sorted
-
- def clean_cache_collection collection # :nodoc:
- collection.each do |name, item|
- if item.empty? then
- collection.delete name
- else
- # HACK mongrel-1.1.5 documents its files twice
- item.uniq!
- item.sort!
- end
- end
- end
-
- ##
- # Friendly rendition of #path
-
- def friendly_path
- case type
- when :gem then
- sep = Regexp.union(*['/', File::ALT_SEPARATOR].compact)
- @path =~ /#{sep}doc#{sep}(.*?)#{sep}ri$/
- "gem #{$1}"
- when :home then '~/.ri'
- when :site then 'ruby site'
- when :system then 'ruby core'
- else @path
- end
- end
-
- def inspect # :nodoc:
- "#<%s:0x%x %s %p>" % [self.class, object_id, @path, modules.sort]
- end
-
- ##
- # Instance methods cache accessor. Maps a class to an Array of its
- # instance methods (not full name).
-
- def instance_methods
- @cache[:instance_methods]
- end
-
- ##
- # Loads cache file for this store
-
- def load_cache
- #orig_enc = @encoding
-
- open cache_path, 'rb' do |io|
- @cache = Marshal.load io.read
- end
-
- load_enc = @cache[:encoding]
-
- # TODO this feature will be time-consuming to add:
- # a) Encodings may be incompatible but transcodeable
- # b) Need to warn in the appropriate spots, wherever they may be
- # c) Need to handle cross-cache differences in encodings
- # d) Need to warn when generating into a cache with diffent encodings
- #
- #if orig_enc and load_enc != orig_enc then
- # warn "Cached encoding #{load_enc} is incompatible with #{orig_enc}\n" \
- # "from #{path}/cache.ri" unless
- # Encoding.compatible? orig_enc, load_enc
- #end
-
- @encoding = load_enc unless @encoding
-
- @cache
- rescue Errno::ENOENT
- end
-
- ##
- # Loads ri data for +klass_name+
-
- def load_class klass_name
- open class_file(klass_name), 'rb' do |io|
- Marshal.load io.read
- end
- end
-
- ##
- # Loads ri data for +method_name+ in +klass_name+
-
- def load_method klass_name, method_name
- open method_file(klass_name, method_name), 'rb' do |io|
- Marshal.load io.read
- end
- end
-
- ##
- # Path to the ri data for +method_name+ in +klass_name+
-
- def method_file klass_name, method_name
- method_name = method_name.split('::').last
- method_name =~ /#(.*)/
- method_type = $1 ? 'i' : 'c'
- method_name = $1 if $1
-
- method_name = if ''.respond_to? :ord then
- method_name.gsub(/\W/) { "%%%02x" % $&[0].ord }
- else
- method_name.gsub(/\W/) { "%%%02x" % $&[0] }
- end
-
- File.join class_path(klass_name), "#{method_name}-#{method_type}.ri"
- end
-
- ##
- # Modules cache accessor. An Array of all the modules (and classes) in the
- # store.
-
- def modules
- @cache[:modules]
- end
-
- ##
- # Writes the cache file for this store
-
- def save_cache
- clean_cache_collection @cache[:ancestors]
- clean_cache_collection @cache[:attributes]
- clean_cache_collection @cache[:class_methods]
- clean_cache_collection @cache[:instance_methods]
-
- @cache[:modules].uniq!
- @cache[:modules].sort!
- @cache[:encoding] = @encoding # this gets set twice due to assert_cache
-
- return if @dry_run
-
- marshal = Marshal.dump @cache
-
- open cache_path, 'wb' do |io|
- io.write marshal
- end
- end
-
- ##
- # Writes the ri data for +klass+
-
- def save_class klass
- full_name = klass.full_name
-
- FileUtils.mkdir_p class_path(full_name) unless @dry_run
-
- @cache[:modules] << full_name
-
- path = class_file full_name
-
- begin
- disk_klass = load_class full_name
-
- klass = disk_klass.merge klass
- rescue Errno::ENOENT
- end
-
- # BasicObject has no ancestors
- ancestors = klass.ancestors.compact.map do |ancestor|
- # HACK for classes we don't know about (class X < RuntimeError)
- String === ancestor ? ancestor : ancestor.full_name
- end
-
- @cache[:ancestors][full_name] ||= []
- @cache[:ancestors][full_name].push(*ancestors)
-
- attributes = klass.attributes.map do |attribute|
- "#{attribute.definition} #{attribute.name}"
- end
-
- unless attributes.empty? then
- @cache[:attributes][full_name] ||= []
- @cache[:attributes][full_name].push(*attributes)
- end
-
- to_delete = []
-
- unless klass.method_list.empty? then
- @cache[:class_methods][full_name] ||= []
- @cache[:instance_methods][full_name] ||= []
-
- class_methods, instance_methods =
- klass.method_list.partition { |meth| meth.singleton }
-
- class_methods = class_methods. map { |method| method.name }
- instance_methods = instance_methods.map { |method| method.name }
-
- old = @cache[:class_methods][full_name] - class_methods
- to_delete.concat old.map { |method|
- method_file full_name, "#{full_name}::#{method}"
- }
-
- old = @cache[:instance_methods][full_name] - instance_methods
- to_delete.concat old.map { |method|
- method_file full_name, "#{full_name}##{method}"
- }
-
- @cache[:class_methods][full_name] = class_methods
- @cache[:instance_methods][full_name] = instance_methods
- end
-
- return if @dry_run
-
- FileUtils.rm_f to_delete
-
- marshal = Marshal.dump klass
-
- open path, 'wb' do |io|
- io.write marshal
- end
- end
-
- ##
- # Writes the ri data for +method+ on +klass+
-
- def save_method klass, method
- full_name = klass.full_name
-
- FileUtils.mkdir_p class_path(full_name) unless @dry_run
-
- cache = if method.singleton then
- @cache[:class_methods]
- else
- @cache[:instance_methods]
- end
- cache[full_name] ||= []
- cache[full_name] << method.name
-
- return if @dry_run
-
- marshal = Marshal.dump method
-
- open method_file(full_name, method.full_name), 'wb' do |io|
- io.write marshal
- end
- end
+ Store = RDoc::Store # :nodoc:
end