diff options
Diffstat (limited to 'trunk/lib/rdoc/ri/driver.rb')
-rw-r--r-- | trunk/lib/rdoc/ri/driver.rb | 551 |
1 files changed, 0 insertions, 551 deletions
diff --git a/trunk/lib/rdoc/ri/driver.rb b/trunk/lib/rdoc/ri/driver.rb deleted file mode 100644 index dfc5f2f98a..0000000000 --- a/trunk/lib/rdoc/ri/driver.rb +++ /dev/null @@ -1,551 +0,0 @@ -require 'optparse' -require 'yaml' - -require 'rdoc/ri' -require 'rdoc/ri/paths' -require 'rdoc/ri/formatter' -require 'rdoc/ri/display' -require 'fileutils' -require 'rdoc/markup' -require 'rdoc/markup/to_flow' - -class RDoc::RI::Driver - - class Hash < ::Hash - def self.convert(hash) - hash = new.update hash - - hash.each do |key, value| - hash[key] = case value - when ::Hash then - convert value - when Array then - value = value.map do |v| - ::Hash === v ? convert(v) : v - end - value - else - value - end - end - - hash - end - - def method_missing method, *args - self[method.to_s] - end - - def merge_enums(other) - other.each do |k, v| - if self[k] then - case v - when Array then - # HACK dunno - if String === self[k] and self[k].empty? then - self[k] = v - else - self[k] += v - end - when Hash then - self[k].update v - else - # do nothing - end - else - self[k] = v - end - end - end - end - - class Error < RDoc::RI::Error; end - - class NotFoundError < Error - def message - "Nothing known about #{super}" - end - end - - attr_accessor :homepath # :nodoc: - - def self.process_args(argv) - options = {} - options[:use_stdout] = !$stdout.tty? - options[:width] = 72 - options[:formatter] = RDoc::RI::Formatter.for 'plain' - options[:list_classes] = false - options[:list_names] = false - - # By default all paths are used. If any of these are true, only those - # directories are used. - use_system = false - use_site = false - use_home = false - use_gems = false - doc_dirs = [] - - opts = OptionParser.new do |opt| - opt.program_name = File.basename $0 - opt.version = RDoc::VERSION - opt.summary_indent = ' ' * 4 - - directories = [ - RDoc::RI::Paths::SYSDIR, - RDoc::RI::Paths::SITEDIR, - RDoc::RI::Paths::HOMEDIR - ] - - if RDoc::RI::Paths::GEMDIRS then - Gem.path.each do |dir| - directories << "#{dir}/doc/*/ri" - end - end - - opt.banner = <<-EOT -Usage: #{opt.program_name} [options] [names...] - -Where name can be: - - Class | Class::method | Class#method | Class.method | method - -All class names may be abbreviated to their minimum unambiguous 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. - -For example: - - #{opt.program_name} Fil - #{opt.program_name} File - #{opt.program_name} File.new - #{opt.program_name} zip - -Note that shell quoting may be required for method names containing -punctuation: - - #{opt.program_name} 'Array.[]' - #{opt.program_name} compact\\! - -By default ri searches for documentation in the following directories: - - #{directories.join "\n "} - -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. - EOT - - opt.separator nil - opt.separator "Options:" - opt.separator nil - - opt.on("--classes", "-c", - "Display the names of classes and modules we", - "know about.") do |value| - options[:list_classes] = value - end - - opt.separator nil - - opt.on("--doc-dir=DIRNAME", "-d", Array, - "List of directories to search for", - "documentation. If not specified, we search", - "the standard rdoc/ri directories. May be", - "repeated.") do |value| - value.each do |dir| - unless File.directory? dir then - raise OptionParser::InvalidArgument, "#{dir} is not a directory" - end - end - - doc_dirs.concat value - end - - opt.separator nil - - opt.on("--fmt=FORMAT", "--format=FORMAT", "-f", - RDoc::RI::Formatter::FORMATTERS.keys, - "Format to use when displaying output:", - " #{RDoc::RI::Formatter.list}", - "Use 'bs' (backspace) with most pager", - "programs. To use ANSI, either disable the", - "pager or tell the pager to allow control", - "characters.") do |value| - options[:formatter] = RDoc::RI::Formatter.for value - end - - opt.separator nil - - unless RDoc::RI::Paths::GEMDIRS.empty? then - opt.on("--[no-]gems", - "Include documentation from RubyGems.") do |value| - use_gems = value - end - end - - opt.separator nil - - opt.on("--[no-]home", - "Include documentation stored in ~/.rdoc.") do |value| - use_home = value - end - - opt.separator nil - - opt.on("--[no-]list-names", "-l", - "List all the names known to RDoc, one per", - "line.") do |value| - options[:list_names] = value - end - - opt.separator nil - - opt.on("--no-pager", "-T", - "Send output directly to stdout.") do |value| - options[:use_stdout] = !value - end - - opt.separator nil - - opt.on("--[no-]site", - "Include documentation from libraries", - "installed in site_lib.") do |value| - use_site = value - end - - opt.separator nil - - opt.on("--[no-]system", - "Include documentation from Ruby's standard", - "library.") do |value| - use_system = value - end - - opt.separator nil - - opt.on("--width=WIDTH", "-w", OptionParser::DecimalInteger, - "Set the width of the output.") do |value| - options[:width] = value - end - end - - argv = ENV['RI'].to_s.split.concat argv - - opts.parse! argv - - options[:names] = argv - - options[:path] = RDoc::RI::Paths.path(use_system, use_site, use_home, - use_gems, *doc_dirs) - options[:raw_path] = RDoc::RI::Paths.raw_path(use_system, use_site, - use_home, use_gems, *doc_dirs) - - options - - rescue OptionParser::InvalidArgument, OptionParser::InvalidOption => e - puts opts - puts - puts e - exit 1 - end - - def self.run(argv = ARGV) - options = process_args argv - ri = new options - ri.run - end - - def initialize(options={}) - options[:formatter] ||= RDoc::RI::Formatter.for('plain') - options[:use_stdout] ||= !$stdout.tty? - options[:width] ||= 72 - @names = options[:names] - - @class_cache_name = 'classes' - @all_dirs = RDoc::RI::Paths.path(true, true, true, true) - @homepath = RDoc::RI::Paths.raw_path(false, false, true, false).first - @homepath = @homepath.sub(/\.rdoc/, '.ri') - @sys_dirs = RDoc::RI::Paths.raw_path(true, false, false, false) - - FileUtils.mkdir_p cache_file_path unless File.directory? cache_file_path - - @class_cache = nil - - @display = RDoc::RI::DefaultDisplay.new(options[:formatter], - options[:width], - options[:use_stdout]) - end - - def class_cache - return @class_cache if @class_cache - - newest = map_dirs('created.rid', :all) do |f| - File.mtime f if test ?f, f - end.max - - up_to_date = (File.exist?(class_cache_file_path) and - newest and newest < File.mtime(class_cache_file_path)) - - @class_cache = if up_to_date then - load_cache_for @class_cache_name - else - class_cache = RDoc::RI::Driver::Hash.new - - classes = map_dirs('**/cdesc*.yaml', :sys) { |f| Dir[f] } - populate_class_cache class_cache, classes - - classes = map_dirs('**/cdesc*.yaml') { |f| Dir[f] } - warn "Updating class cache with #{classes.size} classes..." - - populate_class_cache class_cache, classes, true - write_cache class_cache, class_cache_file_path - end - - @class_cache = RDoc::RI::Driver::Hash.convert @class_cache - @class_cache - end - - def class_cache_file_path - File.join cache_file_path, @class_cache_name - end - - def cache_file_for(klassname) - File.join cache_file_path, klassname.gsub(/:+/, "-") - end - - def cache_file_path - File.join @homepath, 'cache' - end - - def display_class(name) - klass = class_cache[name] - klass = RDoc::RI::Driver::Hash.convert klass - @display.display_class_info klass, class_cache - end - - def get_info_for(arg) - @names = [arg] - run - end - - def load_cache_for(klassname) - path = cache_file_for klassname - - cache = nil - - if File.exist? path and - File.mtime(path) >= File.mtime(class_cache_file_path) then - open path, 'rb' do |fp| - cache = Marshal.load fp.read - end - else - class_cache = nil - - open class_cache_file_path, 'rb' do |fp| - class_cache = Marshal.load fp.read - end - - klass = class_cache[klassname] - return nil unless klass - - method_files = klass["sources"] - cache = RDoc::RI::Driver::Hash.new - - sys_dir = @sys_dirs.first - method_files.each do |f| - system_file = f.index(sys_dir) == 0 - Dir[File.join(File.dirname(f), "*")].each do |yaml| - next unless yaml =~ /yaml$/ - next if yaml =~ /cdesc-[^\/]+yaml$/ - method = read_yaml yaml - name = method["full_name"] - ext_path = f - ext_path = "gem #{$1}" if f =~ %r%gems/[\d.]+/doc/([^/]+)% - method["source_path"] = ext_path unless system_file - cache[name] = RDoc::RI::Driver::Hash.convert method - end - end - - write_cache cache, path - end - - RDoc::RI::Driver::Hash.convert cache - end - - ## - # Finds the next ancestor of +orig_klass+ after +klass+. - - def lookup_ancestor(klass, orig_klass) - cache = class_cache[orig_klass] - - return nil unless cache - - ancestors = [orig_klass] - ancestors.push(*cache.includes.map { |inc| inc['name'] }) - ancestors << cache.superclass - - ancestor = ancestors[ancestors.index(klass) + 1] - - return ancestor if ancestor - - lookup_ancestor klass, cache.superclass - end - - ## - # Finds the method - - def lookup_method(name, klass) - cache = load_cache_for klass - return nil unless cache - - method = cache[name.gsub('.', '#')] - method = cache[name.gsub('.', '::')] unless method - method - end - - def map_dirs(file_name, system=false) - dirs = if system == :all then - @all_dirs - else - if system then - @sys_dirs - else - @all_dirs - @sys_dirs - end - end - - dirs.map { |dir| yield File.join(dir, file_name) }.flatten.compact - end - - ## - # Extract the class and method name parts from +name+ like Foo::Bar#baz - - def parse_name(name) - parts = name.split(/(::|\#|\.)/) - - if parts[-2] != '::' or parts.last !~ /^[A-Z]/ then - meth = parts.pop - parts.pop - end - - klass = parts.join - - [klass, meth] - end - - def populate_class_cache(class_cache, classes, extension = false) - classes.each do |cdesc| - desc = read_yaml cdesc - klassname = desc["full_name"] - - unless class_cache.has_key? klassname then - desc["display_name"] = "Class" - desc["sources"] = [cdesc] - desc["instance_method_extensions"] = [] - desc["class_method_extensions"] = [] - class_cache[klassname] = desc - else - klass = class_cache[klassname] - - if extension then - desc["instance_method_extensions"] = desc.delete "instance_methods" - desc["class_method_extensions"] = desc.delete "class_methods" - end - - klass = RDoc::RI::Driver::Hash.convert klass - - klass.merge_enums desc - klass["sources"] << cdesc - end - end - end - - def read_yaml(path) - data = File.read path - data = data.gsub(/ \!ruby\/(object|struct):(RDoc::RI|RI).*/, '') - data = data.gsub(/ \!ruby\/(object|struct):SM::(\S+)/, - ' !ruby/\1:RDoc::Markup::\2') - YAML.load data - end - - def run - if @names.empty? then - @display.list_known_classes class_cache.keys.sort - else - @names.each do |name| - case name - when /::|\#|\./ then - if class_cache.key? name then - display_class name - else - klass, = parse_name name - - orig_klass = klass - orig_name = name - - until klass == 'Kernel' do - method = lookup_method name, klass - - break method if method - - ancestor = lookup_ancestor klass, orig_klass - - break unless ancestor - - name = name.sub klass, ancestor - klass = ancestor - end - - raise NotFoundError, orig_name unless method - - @display.display_method_info method - end - else - if class_cache.key? name then - display_class name - else - methods = select_methods(/^#{name}/) - - if methods.size == 0 - raise NotFoundError, name - elsif methods.size == 1 - @display.display_method_info methods.first - else - @display.display_method_list methods - end - end - end - end - end - rescue NotFoundError => e - abort e.message - end - - def select_methods(pattern) - methods = [] - class_cache.keys.sort.each do |klass| - class_cache[klass]["instance_methods"].map{|h|h["name"]}.grep(pattern) do |name| - method = load_cache_for(klass)[klass+'#'+name] - methods << method if method - end - class_cache[klass]["class_methods"].map{|h|h["name"]}.grep(pattern) do |name| - method = load_cache_for(klass)[klass+'::'+name] - methods << method if method - end - end - methods - end - - def write_cache(cache, path) - File.open path, "wb" do |cache_file| - Marshal.dump cache, cache_file - end - - cache - end - -end - |