1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/bin/ri

317 lines
8.5 KiB
Text
Raw Normal View History

#!/usr/bin/env ruby
# usage:
#
# ri name...
#
# where name can be
#
# Class | Class::method | Class#method | Class.method | method
#
# All names may be abbreviated to their minimum unbiguous form. If a name
# _is_ ambiguous, all valid options will be listed.
#
# The form '.' method matches either class or instance methods, while
# #method matches only instance and ::method matches only class methods.
require 'rdoc/ri/ri_paths'
require 'rdoc/ri/ri_cache'
require 'rdoc/ri/ri_util'
require 'rdoc/ri/ri_reader'
require 'rdoc/ri/ri_formatter'
require 'rdoc/ri/ri_options'
######################################################################
class RiDisplay
def initialize
@options = RI::Options.instance
@options.parse
paths = @options.paths || RI::Paths::PATH
if paths.empty?
$stderr.puts "No ri documentation found in:"
[ RI::Paths::SYSDIR, RI::Paths::SITEDIR, RI::Paths::HOMEDIR].each do |d|
$stderr.puts " #{d}"
end
$stderr.puts "\nWas rdoc run to create documentation?"
exit 1
end
@ri_reader = RI::RiReader.new(RI::RiCache.new(paths))
@formatter = @options.formatter.new(@options, " ")
end
######################################################################
def display_usage
setup_pager
RI::Options::OptionList.usage(short_form=true)
page_output
end
######################################################################
def setup_pager
unless @options.use_stdout
require 'tempfile'
@save_stdout = STDOUT.clone
STDOUT.reopen(Tempfile.new("ri_"))
end
end
######################################################################
def page_output
unless @options.use_stdout
path = STDOUT.path
STDOUT.reopen(@save_stdout)
@save_stdout = nil
paged = false
for pager in [ ENV['PAGER'], "less", "more <", 'pager' ].compact.uniq
if system("#{pager} #{path}")
paged = true
break
end
end
if !paged
@options.use_stdout = true
puts File.read(path)
end
end
end
######################################################################
def display_params(method)
params = method.params
if params[0,1] == "("
if method.is_singleton
params = method.full_name + params
else
params = method.name + params
end
end
params.split(/\n/).each {|p| @formatter.wrap(p) }
end
######################################################################
def display_flow(flow)
if !flow || flow.empty?
@formatter.wrap("(no description...)")
else
@formatter.display_flow(flow)
end
end
######################################################################
def display_method_info(method_entry)
method = @ri_reader.get_method(method_entry)
@formatter.draw_line(method.full_name)
display_params(method)
@formatter.draw_line
display_flow(method.comment)
if method.aliases && !method.aliases.empty?
@formatter.blankline
aka = "(also known as "
aka << method.aliases.map {|a| a.name }.join(", ")
aka << ")"
@formatter.wrap(aka)
end
end
######################################################################
def display_class_info(class_entry)
klass = @ri_reader.get_class(class_entry)
superclass = klass.superclass_string
if superclass
superclass = " < " + superclass
else
superclass = ""
end
@formatter.draw_line(klass.display_name + ": " +
klass.full_name + superclass)
display_flow(klass.comment)
@formatter.draw_line
unless klass.includes.empty?
@formatter.blankline
@formatter.display_heading("Includes:", 2, "")
incs = []
klass.includes.each do |inc|
inc_desc = @ri_reader.find_class_by_name(inc.name)
if inc_desc
str = inc.name + "("
str << inc_desc.instance_methods.map{|m| m.name}.join(", ")
str << ")"
incs << str
else
incs << inc.name
end
end
@formatter.wrap(incs.sort.join(', '))
end
unless klass.constants.empty?
@formatter.blankline
@formatter.display_heading("Constants:", 2, "")
len = 0
klass.constants.each { |c| len = c.name.length if c.name.length > len }
len += 2
klass.constants.each do |c|
@formatter.wrap(c.value,
@formatter.indent+((c.name+":").ljust(len)))
end
end
unless klass.class_methods.empty?
@formatter.blankline
@formatter.display_heading("Class methods:", 2, "")
@formatter.wrap(klass.class_methods.map{|m| m.name}.sort.join(', '))
end
unless klass.instance_methods.empty?
@formatter.blankline
@formatter.display_heading("Instance methods:", 2, "")
@formatter.wrap(klass.instance_methods.map{|m| m.name}.sort.join(', '))
end
unless klass.attributes.empty?
@formatter.blankline
@formatter.wrap("Attributes:", "")
@formatter.wrap(klass.attributes.map{|a| a.name}.sort.join(', '))
end
end
######################################################################
# If the list of matching methods contains exactly one entry, or
# if it contains an entry that exactly matches the requested method,
# then display that entry, otherwise display the list of
# matching method names
def report_method_stuff(requested_method_name, methods)
if methods.size == 1
display_method_info(methods[0])
else
entries = methods.find_all {|m| m.name == requested_method_name}
if entries.size == 1
display_method_info(entries[0])
else
puts "More than one method matched your request. You can refine"
puts "your search by asking for information on one of:\n\n"
@formatter.wrap(methods.map {|m| m.full_name} .join(", "))
end
end
end
######################################################################
def report_class_stuff(requested_class_name, namespaces)
if namespaces.size == 1
display_class_info(namespaces[0])
else
entries = namespaces.find_all {|m| m.full_name == requested_class_name}
if entries.size == 1
display_class_info(entries[0])
else
puts "More than one class or module matched your request. You can refine"
puts "your search by asking for information on one of:\n\n"
@formatter.wrap(namespaces.map {|m| m.full_name}.join(", "))
end
end
end
######################################################################
def display_info_for(arg)
desc = NameDescriptor.new(arg)
namespaces = @ri_reader.top_level_namespace
for class_name in desc.class_names
namespaces = @ri_reader.lookup_namespace_in(class_name, namespaces)
if namespaces.empty?
raise RiError.new("Nothing known about #{arg}")
end
end
setup_pager
begin
if desc.method_name.nil?
report_class_stuff(desc.class_names.join('::'), namespaces)
else
methods = @ri_reader.find_methods(desc.method_name,
desc.is_class_method,
namespaces)
if methods.empty?
raise RiError.new("Nothing known about #{arg}")
else
report_method_stuff(desc.method_name, methods)
end
end
page_output
ensure
STDOUT.reopen(@save_stdout) if @save_stdout
end
end
######################################################################
def process_args
if @options.list_classes
display_class_list
else
if ARGV.size.zero?
display_usage
else
begin
ARGV.each do |arg|
display_info_for(arg)
end
rescue RiError => e
$stderr.puts(e.message)
exit(1)
end
end
end
end
######################################################################
def display_class_list
classes = @ri_reader.class_names
if classes.empty?
puts "Before using ri, you need to generate documentation"
puts "using 'rdoc' with the --ri option"
else
setup_pager
@formatter.draw_line("Known classes and modules")
@formatter.blankline
@formatter.wrap(@ri_reader.class_names.sort.join(", "))
page_output
end
end
end # class RiDisplay
######################################################################
ri = RiDisplay.new
ri.process_args