Class: RDoc::RI::Driver
Relationships & Source Files | |
Namespace Children | |
Exceptions:
| |
Inherits: | Object |
Defined in: | lib/rdoc/ri/driver.rb |
Overview
The RI driver implements the command-line ri tool.
The driver supports:
-
loading
::RDoc::RI
data from:-
Ruby’s standard library
-
RubyGems
-
~/.rdoc
-
A user-supplied directory
-
-
Paging output (uses RI_PAGER environment variable, PAGER environment variable or the less, more and pager programs)
-
Interactive mode with tab-completion
-
Abbreviated names (ri Zl shows Zlib documentation)
-
Colorized output
-
Merging output from multiple
::RDoc::RI
data sources
Class Method Summary
-
.default_options
Default options for ri.
-
.dump(data_path)
Dump
data_path
using pp. -
.new(initial_options = {}) ⇒ Driver
constructor
Creates a new driver using
initial_options
from .process_args -
.process_args(argv)
Parses
argv
and returns a Hash of options. -
.run(argv = ARGV)
Runs the ri command line executable using
argv
Instance Attribute Summary
-
#paging? ⇒ Boolean
readonly
Are we using a pager?
-
#show_all
rw
Show all method documentation following a class or module.
-
#stores
rw
An Store for each entry in the
::RDoc::RI
path. -
#use_stdout
rw
Controls the user of the pager vs $stdout.
Instance Method Summary
-
#add_also_in(out, also_in)
Adds paths for undocumented classes
also_in
toout
-
#add_class(out, name, classes)
Adds a class header to
out
for classname
which is described in #classes. -
#add_extends(out, extends)
Adds
extends
toout
-
#add_extension_modules(out, type, extensions)
Adds a list of
extensions
to this module of the giventype
toout
. -
#add_from(out, store)
Adds “(from …)” to
out
forstore
-
#add_includes(out, includes)
Adds
includes
toout
-
#add_method(out, name)
Looks up the method
name
and adds it toout
-
#add_method_documentation(out, klass)
Adds documentation for all methods in
klass
toout
-
#add_method_list(out, methods, name)
Adds a list of
methods
toout
with a heading ofname
-
#ancestors_of(klass)
Returns ancestor classes of
klass
-
#class_document(name, found, klasses, includes, extends)
Builds a
::RDoc::Markup::Document
fromfound
,klasess
andincludes
-
#classes
Hash mapping a known class or module to the stores it can be loaded from.
-
#classes_and_includes_and_extends_for(name)
Returns the stores wherein
name
is found along with the classes, extends and includes that match it. -
#complete(name)
Completes
name
based on the caches. -
#display(document)
Converts
document
to text and writes it to the pager. -
#display_class(name)
Outputs formatted
::RDoc::RI
data for classname
. -
#display_method(name)
Outputs formatted
::RDoc::RI
data for methodname
-
#display_name(name)
Outputs formatted
::RDoc::RI
data for the class or methodname
. -
#display_names(names)
Displays each name in
name
-
#display_page(name)
Outputs formatted
::RDoc::RI
data for pagename
. -
#display_page_list(store, pages = , search = nil)
Outputs a formatted
::RDoc::RI
page list for the pages instore
. -
#expand_class(klass)
Expands abbreviated klass
klass
into a fully-qualified class. -
#expand_name(name)
Expands the class portion of
name
into a fully-qualified class. -
#filter_methods(found, name)
Filters the methods in
found
trying to find a match forname
. -
#find_methods(name)
Yields items matching
name
including the store they were found in, the class being searched for, the class they were found in (an ancestor) the types of methods to look up (from #method_type), and the method name being searched for. -
#find_pager_jruby(pager)
Finds the given
pager
for jruby. -
#find_store(name)
Finds a store that matches
name
which can be the name of a gem, “ruby”, “home” or “site”. -
#formatter(io)
Creates a new
::RDoc::Markup::Formatter
. -
#in_path?(file) ⇒ Boolean
Is
file
in ENV? -
#interactive
Runs ri interactively using Readline if it is available.
-
#list_known_classes(names = [])
Lists classes known to ri starting with
names
. -
#list_methods_matching(name)
Returns an Array of methods matching
name
-
#load_method(store, cache, klass, type, name)
Loads RI data for method
name
onklass
fromstore
. -
#load_methods_matching(name)
Returns an Array of
::RDoc::RI
data for methods matchingname
-
#lookup_method(name)
Returns a filtered list of methods matching
name
-
#method_document(name, filtered)
Builds a
::RDoc::Markup::Document
fromfound
,klasses
andincludes
-
#method_type(selector)
Returns the type of method (:both,
:instance
,:class
) forselector
-
#name_regexp(name)
Returns a regular expression for
name
that will match an RDoc::AnyMethod’s name. -
#page
Paginates output through a pager program.
-
#parse_name(name)
Extracts the class, selector and method name parts from
name
likeFoo::Bar#baz
. -
#run
Looks up and displays ri data according to the options given.
-
#setup_pager
Sets up a pager program to pass output through.
-
#start_server
Starts a WEBrick server for ri.
-
#add_extension_modules_multiple(out, store, modules)
Internal use only
Renders multiple included
modules
fromstore
toout
. -
#add_extension_modules_single(out, store, include)
Internal use only
Adds a single extension module
include
fromstore
toout
- #check_did_you_mean Internal use only
-
#class_cache
Internal use only
For RubyGems backwards compatibility.
-
#class_document_comment(out, comment)
Internal use only
Adds the class
comment
toout
. -
#class_document_constants(out, klass)
Internal use only
Adds the constants from
klass
to the Documentout
. - #complete_klass(name, klass, selector, method, completions) Internal use only
- #complete_method(name, klass, selector, completions) Internal use only
-
#render_class(out, store, klass, also_in)
Internal use only
Renders the
klass
fromstore
toout
. - #render_method(out, store, method, name) Internal use only
- #render_method_arguments(out, arglists) Internal use only
- #render_method_comment(out, method, alias_for = nil) Internal use only
- #render_method_superclass(out, method) Internal use only
Constructor Details
.new(initial_options = {}) ⇒ Driver
Creates a new driver using initial_options
from .process_args
# File 'lib/rdoc/ri/driver.rb', line 396
def initialize = {} @paging = false @classes = nil = self.class. .update( ) @formatter_klass = [:formatter] require 'profile' if [:profile] @names = [:names] @list = [:list] @doc_dirs = [] @stores = [] RDoc::RI::Paths.each( [:use_system], [:use_site], [:use_home], [:use_gems], * [:extra_doc_dirs]) do |path, type| @doc_dirs << path store = RDoc::RI::Store.new path, type store.load_cache @stores << store end @list_doc_dirs = [:list_doc_dirs] @interactive = [:interactive] @server = [:server] @use_stdout = [:use_stdout] @show_all = [:show_all] @width = [:width] # pager process for jruby @jruby_pager_process = nil end
Class Method Details
.default_options
Default options for ri
# File 'lib/rdoc/ri/driver.rb', line 89
def self. = {} [:interactive] = false [:profile] = false [:show_all] = false [:use_stdout] = !$stdout.tty? [:width] = 72 # By default all standard paths are used. [:use_system] = true [:use_site] = true [:use_home] = true [:use_gems] = true [:extra_doc_dirs] = [] return end
.dump(data_path)
Dump data_path
using pp
# File 'lib/rdoc/ri/driver.rb', line 110
def self.dump data_path require 'pp' File.open data_path, 'rb' do |io| pp Marshal.load(io.read) end end
.process_args(argv)
Parses argv
and returns a Hash of options
# File 'lib/rdoc/ri/driver.rb', line 121
def self.process_args argv = opts = OptionParser.new do |opt| opt.accept File do |file,| File.readable?(file) and not File.directory?(file) and file end opt.program_name = File.basename $0 opt.version = RDoc::VERSION opt.release = nil opt.summary_indent = ' ' * 4 opt. = <<-EOT Usage: #{opt.program_name} [options] [name ...] Where name can be: 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. 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: #{opt.program_name} 'Array.[]' #{opt.program_name} compact\\! To see the default directories #{opt.program_name} will search, run: #{opt.program_name} --list-doc-dirs Specifying the --system, --site, --home, --gems, or --doc-dir options will limit ri to searching only the specified directories. 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 opt.separator "Options:" opt.separator nil opt.on("--[no-]interactive", "-i", "In interactive mode you can repeatedly", "look up methods with autocomplete.") do |interactive| [:interactive] = interactive end opt.separator nil opt.on("--[no-]all", "-a", "Show all documentation for a class or", "module.") do |show_all| [:show_all] = show_all end opt.separator nil opt.on("--[no-]list", "-l", "List classes ri knows about.") do |list| [:list] = list end opt.separator nil opt.on("--[no-]pager", "Send output to a pager,", "rather than directly to stdout.") do |use_pager| [:use_stdout] = !use_pager end opt.separator nil opt.on("-T", "Synonym for --no-pager.") do [:use_stdout] = true end opt.separator nil opt.on("--width=WIDTH", "-w", OptionParser::DecimalInteger, "Set the width of the output.") do |width| [:width] = width end opt.separator nil opt.on("--server[=PORT]", Integer, "Run RDoc server on the given port.", "The default port is 8214.") do |port| [:server] = port || 8214 end 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 end formatters -= %w[html label test] # remove useless output formats opt.on("--format=NAME", "-f", "Use the selected formatter. The default", "formatter is bs for paged output and ansi", "otherwise. Valid formatters are:", "#{formatters.join(', ')}.", formatters) do |value| [:formatter] = RDoc::Markup.const_get "To#{value.capitalize}" end opt.separator nil opt.on("--help", "-h", "Show help and exit.") do puts opts exit end opt.separator nil opt.on("--version", "-v", "Output version information and exit.") do puts "#{opts.program_name} #{opts.version}" exit end opt.separator nil opt.separator "Data source options:" opt.separator nil opt.on("--[no-]list-doc-dirs", "List the directories from which ri will", "source documentation on stdout and exit.") do |list_doc_dirs| [:list_doc_dirs] = list_doc_dirs end opt.separator nil opt.on("--doc-dir=DIRNAME", "-d", Array, "List of directories from which to source", "documentation in addition to the standard", "directories. May be repeated.") do |value| value.each do |dir| unless File.directory? dir then raise OptionParser::InvalidArgument, "#{dir} is not a directory" end [:extra_doc_dirs] << File. (dir) end end opt.separator nil opt.on("--no-standard-docs", "Do not include documentation from", "the Ruby standard library, site_lib,", "installed gems, or ~/.rdoc.", "Use with --doc-dir.") do [:use_system] = false [:use_site] = false [:use_gems] = false [:use_home] = false end opt.separator nil opt.on("--[no-]system", "Include documentation from Ruby's", "standard library. Defaults to true.") do |value| [:use_system] = value end opt.separator nil opt.on("--[no-]site", "Include documentation from libraries", "installed in site_lib.", "Defaults to true.") do |value| [:use_site] = value end opt.separator nil opt.on("--[no-]gems", "Include documentation from RubyGems.", "Defaults to true.") do |value| [:use_gems] = value end opt.separator nil opt.on("--[no-]home", "Include documentation stored in ~/.rdoc.", "Defaults to true.") do |value| [:use_home] = value end opt.separator nil opt.separator "Debug options:" opt.separator nil opt.on("--[no-]profile", "Run with the ruby profiler.") do |value| [:profile] = value end opt.separator nil opt.on("--dump=CACHE", File, "Dump data from an ri cache or data file.") do |value| [:dump_path] = value end end argv = ENV['RI'].to_s.split.concat argv opts.parse! argv [:names] = argv [:use_stdout] ||= !$stdout.tty? [:use_stdout] ||= [:interactive] [:width] ||= 72 rescue OptionParser::InvalidArgument, OptionParser::InvalidOption => e puts opts puts puts e exit 1 end
.run(argv = ARGV)
Runs the ri command line executable using argv
# File 'lib/rdoc/ri/driver.rb', line 381
def self.run argv = ARGV = process_args argv if [:dump_path] then dump [:dump_path] return end ri = new ri.run end
Instance Attribute Details
#paging? ⇒ Boolean
(readonly)
Are we using a pager?
# File 'lib/rdoc/ri/driver.rb', line 1362
def paging? @paging end
#show_all (rw)
Show all method documentation following a class or module
# File 'lib/rdoc/ri/driver.rb', line 74
attr_accessor :show_all
#stores (rw)
An Store for each entry in the ::RDoc::RI
path
# File 'lib/rdoc/ri/driver.rb', line 79
attr_accessor :stores
#use_stdout (rw)
Controls the user of the pager vs $stdout
# File 'lib/rdoc/ri/driver.rb', line 84
attr_accessor :use_stdout
Instance Method Details
#add_also_in(out, also_in)
Adds paths for undocumented classes also_in
to out
# File 'lib/rdoc/ri/driver.rb', line 437
def add_also_in out, also_in return if also_in.empty? out << RDoc::Markup::Rule.new(1) out << RDoc::Markup::Paragraph.new("Also found in:") paths = RDoc::Markup::Verbatim.new also_in.each do |store| paths.parts.push store.friendly_path, "\n" end out << paths end
#add_class(out, name, classes)
Adds a class header to out
for class name
which is described in #classes.
# File 'lib/rdoc/ri/driver.rb', line 454
def add_class out, name, classes heading = if classes.all? { |klass| klass.module? } then name else superclass = classes.map do |klass| klass.superclass unless klass.module? end.compact.shift || 'Object' superclass = superclass.full_name unless String === superclass "#{name} < #{superclass}" end out << RDoc::Markup::Heading.new(1, heading) out << RDoc::Markup::BlankLine.new end
#add_extends(out, extends)
Adds extends
to out
# File 'lib/rdoc/ri/driver.rb', line 481
def add_extends out, extends add_extension_modules out, 'Extended by', extends end
#add_extension_modules(out, type, extensions)
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.
# File 'lib/rdoc/ri/driver.rb', line 489
def add_extension_modules out, type, extensions return if extensions.empty? out << RDoc::Markup::Rule.new(1) out << RDoc::Markup::Heading.new(1, "#{type}:") extensions.each do |modules, store| if modules.length == 1 then add_extension_modules_single out, store, modules.first else add_extension_modules_multiple out, store, modules end end end
#add_extension_modules_multiple(out, store, modules)
Renders multiple included modules
from store
to out
.
# File 'lib/rdoc/ri/driver.rb', line 507
def add_extension_modules_multiple out, store, modules # :nodoc: out << RDoc::Markup::Paragraph.new("(from #{store.friendly_path})") wout, with = modules.partition { |incl| incl.comment.empty? } out << RDoc::Markup::BlankLine.new unless with.empty? with.each do |incl| out << RDoc::Markup::Paragraph.new(incl.name) out << RDoc::Markup::BlankLine.new out << incl.comment end unless wout.empty? then verb = RDoc::Markup::Verbatim.new wout.each do |incl| verb.push incl.name, "\n" end out << verb end end
#add_extension_modules_single(out, store, include)
Adds a single extension module include
from store
to out
# File 'lib/rdoc/ri/driver.rb', line 534
def add_extension_modules_single out, store, include # :nodoc: name = include.name path = store.friendly_path out << RDoc::Markup::Paragraph.new("#{name} (from #{path})") if include.comment then out << RDoc::Markup::BlankLine.new out << include.comment end end
#add_from(out, store)
Adds “(from …)” to out
for store
#add_includes(out, includes)
Adds includes
to out
# File 'lib/rdoc/ri/driver.rb', line 548
def add_includes out, includes add_extension_modules out, 'Includes', includes end
#add_method(out, name)
Looks up the method name
and adds it to out
# File 'lib/rdoc/ri/driver.rb', line 555
def add_method out, name filtered = lookup_method name method_out = method_document name, filtered out.concat method_out.parts end
#add_method_documentation(out, klass)
Adds documentation for all methods in klass
to out
# File 'lib/rdoc/ri/driver.rb', line 566
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
#add_method_list(out, methods, name)
Adds a list of methods
to out
with a heading of name
# File 'lib/rdoc/ri/driver.rb', line 579
def add_method_list out, methods, name return if methods.empty? out << RDoc::Markup::Heading.new(1, "#{name}:") out << RDoc::Markup::BlankLine.new if @use_stdout and !@interactive then out.concat methods.map { |method| RDoc::Markup::Verbatim.new method } else out << RDoc::Markup::IndentedParagraph.new(2, methods.join(', ')) end out << RDoc::Markup::BlankLine.new end
#ancestors_of(klass)
Returns ancestor classes of klass
# File 'lib/rdoc/ri/driver.rb', line 599
def ancestors_of klass ancestors = [] unexamined = [klass] seen = [] loop do break if unexamined.empty? current = unexamined.shift seen << current stores = classes[current] break unless stores and not stores.empty? klasses = stores.map do |store| store.ancestors[current] end.flatten.uniq klasses = klasses - seen ancestors.concat klasses unexamined.concat klasses end ancestors.reverse end
#check_did_you_mean
# File 'lib/rdoc/ri/driver.rb', line 934
def check_did_you_mean # :nodoc: if defined? DidYouMean::SpellChecker true else begin require 'did_you_mean' if defined? DidYouMean::SpellChecker true else false end rescue LoadError false end end end
#class_cache
For RubyGems backwards compatibility
# File 'lib/rdoc/ri/driver.rb', line 630
def class_cache # :nodoc: end
#class_document(name, found, klasses, includes, extends)
Builds a ::RDoc::Markup::Document
from found
, klasess
and includes
# File 'lib/rdoc/ri/driver.rb', line 636
def class_document name, found, klasses, includes, extends also_in = [] out = RDoc::Markup::Document.new add_class out, name, klasses add_includes out, includes add_extends out, extends found.each do |store, klass| render_class out, store, klass, also_in end add_also_in out, also_in out end
#class_document_comment(out, comment)
Adds the class comment
to out
.
# File 'lib/rdoc/ri/driver.rb', line 658
def class_document_comment out, comment # :nodoc: unless comment.empty? then out << RDoc::Markup::Rule.new(1) if comment.merged? then parts = comment.parts parts = parts.zip [RDoc::Markup::BlankLine.new] * parts.length parts.flatten! parts.pop out.concat parts else out << comment end end end
#class_document_constants(out, klass)
Adds the constants from klass
to the Document out
.
# File 'lib/rdoc/ri/driver.rb', line 678
def class_document_constants out, klass # :nodoc: return if klass.constants.empty? out << RDoc::Markup::Heading.new(1, "Constants:") out << RDoc::Markup::BlankLine.new list = RDoc::Markup::List.new :NOTE constants = klass.constants.sort_by { |constant| constant.name } 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) } out << list out << RDoc::Markup::BlankLine.new end
#classes
Hash mapping a known class or module to the stores it can be loaded from
# File 'lib/rdoc/ri/driver.rb', line 702
def classes return @classes if @classes @classes = {} @stores.each do |store| store.cache[:modules].each do |mod| # using default block causes searched-for modules to be added @classes[mod] ||= [] @classes[mod] << store end end @classes end
#classes_and_includes_and_extends_for(name)
Returns the stores wherein name
is found along with the classes, extends and includes that match it
# File 'lib/rdoc/ri/driver.rb', line 722
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 RDoc::Store::MissingFileError end end.compact extends.reject! do |modules,| modules.empty? end includes.reject! do |modules,| modules.empty? end [found, klasses, includes, extends] end
#complete(name)
Completes name
based on the caches. For Readline
# File 'lib/rdoc/ri/driver.rb', line 747
def complete name completions = [] klass, selector, method = parse_name name complete_klass name, klass, selector, method, completions complete_method name, klass, selector, completions completions.sort.uniq end
#complete_klass(name, klass, selector, method, completions)
# File 'lib/rdoc/ri/driver.rb', line 758
def complete_klass name, klass, selector, method, completions # :nodoc: klasses = classes.keys # may need to include Foo when given Foo:: klass_name = method ? name : klass if name !~ /#|\./ then completions.replace klasses.grep(/^#{Regexp.escape klass_name}[^:]*$/) completions.concat klasses.grep(/^#{Regexp.escape name}[^:]*$/) if name =~ /::$/ completions << klass if classes.key? klass # to complete a method name elsif selector then completions << klass if classes.key? klass elsif classes.key? klass_name then completions << klass_name end end
#complete_method(name, klass, selector, completions)
# File 'lib/rdoc/ri/driver.rb', line 777
def complete_method name, klass, selector, completions # :nodoc: if completions.include? klass and name =~ /#|\.|::/ then methods = list_methods_matching name if not methods.empty? then # remove Foo if given Foo:: and a method was found completions.delete klass elsif selector then # replace Foo with Foo:: as given completions.delete klass completions << "#{klass}#{selector}" end completions.concat methods end end
#display(document)
Converts document
to text and writes it to the pager
#display_class(name)
Outputs formatted ::RDoc::RI
data for class name
. Groups undocumented classes
# File 'lib/rdoc/ri/driver.rb', line 810
def display_class name return if name =~ /#|\./ found, klasses, includes, extends = classes_and_includes_and_extends_for name return if found.empty? out = class_document name, found, klasses, includes, extends display out end
#display_method(name)
Outputs formatted ::RDoc::RI
data for method name
# File 'lib/rdoc/ri/driver.rb', line 826
def display_method name out = RDoc::Markup::Document.new add_method out, name display out end
#display_name(name)
Outputs formatted ::RDoc::RI
data for the class or method name
.
Returns true if name
was found, false if it was not an alternative could be guessed, raises an error if name
couldn’t be guessed.
# File 'lib/rdoc/ri/driver.rb', line 840
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 =~ /::|#|\./ true rescue NotFoundError matches = list_methods_matching name if name =~ /::|#|\./ matches = classes.keys.grep(/^#{Regexp.escape name}/) if matches.empty? raise if matches.empty? page do |io| io.puts "#{name} not found, maybe you meant:" io.puts io.puts matches.sort.join("\n") end false end
#display_names(names)
Displays each name in name
# File 'lib/rdoc/ri/driver.rb', line 869
def display_names names names.each do |name| name = name display_name name end end
#display_page(name)
Outputs formatted ::RDoc::RI
data for page name
.
# File 'lib/rdoc/ri/driver.rb', line 880
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.zero? then return display_page_list store, pages elsif 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
#display_page_list(store, pages = , search = nil)
Outputs a formatted ::RDoc::RI
page list for the pages in store
.
# File 'lib/rdoc/ri/driver.rb', line 911
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
#expand_class(klass)
Expands abbreviated klass klass
into a fully-qualified class. “Zl::Da” will be expanded to Zlib::DataError
.
# File 'lib/rdoc/ri/driver.rb', line 955
def klass class_names = classes.keys ary = class_names.grep(Regexp.new("\\A#{klass.gsub(/(?=::|\z)/, '[^:]*')}\\z")) if ary.length != 1 && ary.first != klass if check_did_you_mean suggestions = DidYouMean::SpellChecker.new(dictionary: class_names).correct(klass) raise NotFoundError.new(klass, suggestions) else raise NotFoundError, klass end end ary.first end
#expand_name(name)
Expands the class portion of name
into a fully-qualified class. See #expand_class.
# File 'lib/rdoc/ri/driver.rb', line 973
def name klass, selector, method = parse_name name return [selector, method].join if klass.empty? case selector when ':' then [find_store(klass), selector, method] else [ (klass), selector, method] end.join end
#filter_methods(found, name)
Filters the methods in found
trying to find a match for name
.
# File 'lib/rdoc/ri/driver.rb', line 989
def filter_methods found, name regexp = name_regexp name filtered = found.find_all do |store, methods| methods.any? { |method| method.full_name =~ regexp } end return filtered unless filtered.empty? found end
#find_methods(name)
Yields items matching name
including the store they were found in, the class being searched for, the class they were found in (an ancestor) the types of methods to look up (from #method_type), and the method name being searched for
# File 'lib/rdoc/ri/driver.rb', line 1007
def find_methods name klass, selector, method = parse_name name types = method_type selector klasses = nil ambiguous = klass.empty? if ambiguous then klasses = classes.keys else klasses = ancestors_of klass klasses.unshift klass end methods = [] klasses.each do |ancestor| ancestors = classes[ancestor] next unless ancestors klass = ancestor if ambiguous ancestors.each do |store| methods << [store, klass, ancestor, types, method] end end methods = methods.sort_by do |_, k, a, _, m| [k, a, m].compact end methods.each do |item| yield(*item) # :yields: store, klass, ancestor, types, method end self end
#find_pager_jruby(pager)
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).
# File 'lib/rdoc/ri/driver.rb', line 1055
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
#find_store(name)
Finds a store that matches name
which can be the name of a gem, “ruby”, “home” or “site”.
See also Store#source
# File 'lib/rdoc/ri/driver.rb', line 1083
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
#formatter(io)
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.
#in_path?(file) ⇒ Boolean
Is file
in ENV?
# File 'lib/rdoc/ri/driver.rb', line 1147
def in_path? file return true if file =~ %r%\A/% and File.exist? file ENV['PATH'].split(File::PATH_SEPARATOR).any? do |path| File.exist? File.join(path, file) end end
#interactive
Runs ri interactively using Readline if it is available.
# File 'lib/rdoc/ri/driver.rb', line 1113
def interactive puts "\nEnter the method name you want to look up." if defined? Readline then Readline.completion_proc = method :complete puts "You can use tab to autocomplete." end puts "Enter a blank line to exit.\n\n" loop do name = if defined? Readline then Readline.readline ">> " else print ">> " $stdin.gets end return if name.nil? or name.empty? begin display_name (name.strip) rescue NotFoundError => e puts e. end end rescue Interrupt exit end
#list_known_classes(names = [])
Lists classes known to ri starting with names
. If names
is empty all known classes are shown.
# File 'lib/rdoc/ri/driver.rb', line 1159
def list_known_classes names = [] classes = [] stores.each do |store| classes << store.module_names end classes = classes.flatten.uniq.sort unless names.empty? then filter = Regexp.union names.map { |name| /^#{name}/ } classes = classes.grep filter end page do |io| if paging? or io.tty? then if names.empty? then io.puts "Classes and Modules known to ri:" else io.puts "Classes and Modules starting with #{names.join ', '}:" end io.puts end io.puts classes.join("\n") end end
#list_methods_matching(name)
Returns an Array of methods matching name
# File 'lib/rdoc/ri/driver.rb', line 1191
def list_methods_matching name found = [] find_methods name do |store, klass, ancestor, types, method| if types == :instance or types == :both then methods = store.instance_methods[ancestor] if methods then matches = methods.grep(/^#{Regexp.escape method.to_s}/) matches = matches.map do |match| "#{klass}##{match}" end found.concat matches end end if types == :class or types == :both then methods = store.class_methods[ancestor] next unless methods matches = methods.grep(/^#{Regexp.escape method.to_s}/) matches = matches.map do |match| "#{klass}::#{match}" end found.concat matches end end found.uniq end
#load_method(store, cache, klass, type, name)
Loads RI data for method name
on klass
from store
. type
and cache
indicate if it is a class or instance method.
# File 'lib/rdoc/ri/driver.rb', line 1230
def load_method store, cache, klass, type, name methods = store.send(cache)[klass] return unless methods method = methods.find do |method_name| method_name == name end return unless method store.load_method klass, "#{type}#{method}" rescue RDoc::Store::MissingFileError => e comment = RDoc::Comment.new("missing documentation at #{e.file}").parse method = RDoc::AnyMethod.new nil, name method.comment = comment method end
#load_methods_matching(name)
Returns an Array of ::RDoc::RI
data for methods matching name
# File 'lib/rdoc/ri/driver.rb', line 1253
def load_methods_matching name found = [] find_methods name do |store, klass, ancestor, types, method| methods = [] methods << load_method(store, :class_methods, ancestor, '::', method) if [:class, :both].include? types methods << load_method(store, :instance_methods, ancestor, '#', method) if [:instance, :both].include? types found << [store, methods.compact] end found.reject do |path, methods| methods.empty? end end
#lookup_method(name)
Returns a filtered list of methods matching name
# File 'lib/rdoc/ri/driver.rb', line 1274
def lookup_method name found = load_methods_matching name if found.empty? if check_did_you_mean methods = [] _, _, method_name = parse_name name find_methods name do |store, klass, ancestor, types, method| methods.push(*store.class_methods[klass]) if [:class, :both].include? types methods.push(*store.instance_methods[klass]) if [:instance, :both].include? types end methods = methods.uniq suggestions = DidYouMean::SpellChecker.new(dictionary: methods).correct(method_name) raise NotFoundError.new(name, suggestions) else raise NotFoundError, name end end filter_methods found, name end
#method_document(name, filtered)
Builds a ::RDoc::Markup::Document
from found
, klasses
and includes
# File 'lib/rdoc/ri/driver.rb', line 1299
def method_document name, filtered out = RDoc::Markup::Document.new out << RDoc::Markup::Heading.new(1, name) out << RDoc::Markup::BlankLine.new filtered.each do |store, methods| methods.each do |method| render_method out, store, method, name end end out end
#method_type(selector)
Returns the type of method (:both, :instance
, :class
) for selector
# File 'lib/rdoc/ri/driver.rb', line 1317
def method_type selector case selector when '.', nil then :both when '#' then :instance else :class end end
#name_regexp(name)
Returns a regular expression for name
that will match an RDoc::AnyMethod’s name.
# File 'lib/rdoc/ri/driver.rb', line 1329
def name_regexp name klass, type, name = parse_name name case type when '#', '::' then /^#{klass}#{type}#{Regexp.escape name}$/ else /^#{klass}(#|::)#{Regexp.escape name}$/ end end
#page
Paginates output through a pager program.
# File 'lib/rdoc/ri/driver.rb', line 1343
def page if pager = setup_pager then begin yield pager ensure pager.close @jruby_pager_process.wait_for if @jruby_pager_process end else yield $stdout end rescue Errno::EPIPE ensure @paging = false end
#parse_name(name)
Extracts the class, selector and method name parts from name
like Foo::Bar#baz
.
NOTE: Given Foo::Bar
, Bar is considered a class even though it may be a method
# File 'lib/rdoc/ri/driver.rb', line 1373
def parse_name name parts = name.split(/(::?|#|\.)/) if parts.length == 1 then if parts.first =~ /^[a-z]|^([%&*\/<>^`|~-]|\@|-@|<<|<=>?|===?|=>|=~|>>|\[\]=?|~@)$/ then type = '.' meth = parts.pop else type = nil meth = nil end elsif parts.length == 2 or parts.last =~ /::|#|\./ then type = parts.pop meth = nil elsif parts[1] == ':' then klass = parts.shift type = parts.shift meth = parts.join elsif parts[-2] != '::' or parts.last !~ /^[A-Z]/ then meth = parts.pop type = parts.pop end klass ||= parts.join [klass, type, meth] end
#render_class(out, store, klass, also_in)
Renders the klass
from store
to out
. If the klass has no documentable items the class is added to also_in
instead.
# File 'lib/rdoc/ri/driver.rb', line 1405
def render_class out, store, klass, also_in # :nodoc: comment = klass.comment # TODO the store's cache should always return an empty Array class_methods = store.class_methods[klass.full_name] || [] instance_methods = store.instance_methods[klass.full_name] || [] attributes = store.attributes[klass.full_name] || [] if comment.empty? and instance_methods.empty? and class_methods.empty? then also_in << store return end add_from out, store class_document_comment out, comment if class_methods or instance_methods or not klass.constants.empty? then out << RDoc::Markup::Rule.new(1) end class_document_constants out, klass 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
#render_method(out, store, method, name)
# File 'lib/rdoc/ri/driver.rb', line 1435
def render_method out, store, method, name # :nodoc: out << RDoc::Markup::Paragraph.new("(from #{store.friendly_path})") 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) render_method_arguments out, method.arglists render_method_superclass out, method if method.is_alias_for al = method.is_alias_for alias_for = store.load_method al.parent_name, "#{al.name_prefix}#{al.name}" render_method_comment out, method, alias_for else render_method_comment out, method end end
#render_method_arguments(out, arglists)
#render_method_comment(out, method, alias_for = nil)
# File 'lib/rdoc/ri/driver.rb', line 1464
def render_method_comment out, method, alias_for = nil# :nodoc: if alias_for unless method.comment.nil? or method.comment.empty? out << RDoc::Markup::BlankLine.new out << method.comment end out << RDoc::Markup::BlankLine.new out << RDoc::Markup::Paragraph.new("(This method is an alias for #{alias_for.full_name}.)") out << RDoc::Markup::BlankLine.new out << alias_for.comment out << RDoc::Markup::BlankLine.new else out << RDoc::Markup::BlankLine.new out << method.comment out << RDoc::Markup::BlankLine.new end end
#render_method_superclass(out, method)
# File 'lib/rdoc/ri/driver.rb', line 1482
def render_method_superclass out, method # :nodoc: return unless 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
#run
Looks up and displays ri data according to the options given.
# File 'lib/rdoc/ri/driver.rb', line 1494
def run if @list_doc_dirs then puts @doc_dirs elsif @list then list_known_classes @names elsif @server then start_server elsif @interactive or @names.empty? then interactive else display_names @names end rescue NotFoundError => e abort e. end
#setup_pager
Sets up a pager program to pass output through. Tries the RI_PAGER and PAGER environment variables followed by pager, less then more.
# File 'lib/rdoc/ri/driver.rb', line 1514
def setup_pager return if @use_stdout jruby = RUBY_ENGINE == 'jruby' pagers = [ENV['RI_PAGER'], ENV['PAGER'], 'pager', 'less', 'more'] pagers.compact.uniq.each do |pager| next unless pager pager_cmd = pager.split.first next unless in_path? pager_cmd 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 $?.pid == io.pid and $?.exited? # pager didn't work @paging = true return io end @use_stdout = true nil end
#start_server
Starts a WEBrick server for ri.
# File 'lib/rdoc/ri/driver.rb', line 1553
def start_server require 'webrick' server = WEBrick::HTTPServer.new :Port => @server extra_doc_dirs = @stores.map {|s| s.type == :extra ? s.path : nil}.compact server.mount '/', RDoc::Servlet, nil, extra_doc_dirs trap 'INT' do server.shutdown end trap 'TERM' do server.shutdown end server.start end