diff options
author | Robin Houston <robin@lenny.robin> | 2011-08-24 18:09:33 +0100 |
---|---|---|
committer | Robin Houston <robin@lenny.robin> | 2011-08-24 18:09:33 +0100 |
commit | 95ed2bc24312699cfce9bb6f9bc1418b15718bb1 (patch) | |
tree | 6639e8b140b0d74a8f93c2ab00d7a7faba41d6d7 /vendor/gems/rdoc-2.4.3/lib/rdoc/ri | |
parent | 68a5989094f63a7e32fd6745b1c5739e038339f3 (diff) |
Add rdoc 2.4.3 to vendor/gems
Diffstat (limited to 'vendor/gems/rdoc-2.4.3/lib/rdoc/ri')
-rw-r--r-- | vendor/gems/rdoc-2.4.3/lib/rdoc/ri/cache.rb | 187 | ||||
-rw-r--r-- | vendor/gems/rdoc-2.4.3/lib/rdoc/ri/descriptions.rb | 156 | ||||
-rw-r--r-- | vendor/gems/rdoc-2.4.3/lib/rdoc/ri/display.rb | 340 | ||||
-rw-r--r-- | vendor/gems/rdoc-2.4.3/lib/rdoc/ri/driver.rb | 828 | ||||
-rw-r--r-- | vendor/gems/rdoc-2.4.3/lib/rdoc/ri/formatter.rb | 654 | ||||
-rw-r--r-- | vendor/gems/rdoc-2.4.3/lib/rdoc/ri/paths.rb | 93 | ||||
-rw-r--r-- | vendor/gems/rdoc-2.4.3/lib/rdoc/ri/reader.rb | 106 | ||||
-rw-r--r-- | vendor/gems/rdoc-2.4.3/lib/rdoc/ri/util.rb | 79 | ||||
-rw-r--r-- | vendor/gems/rdoc-2.4.3/lib/rdoc/ri/writer.rb | 68 |
9 files changed, 2511 insertions, 0 deletions
diff --git a/vendor/gems/rdoc-2.4.3/lib/rdoc/ri/cache.rb b/vendor/gems/rdoc-2.4.3/lib/rdoc/ri/cache.rb new file mode 100644 index 000000000..15935abb1 --- /dev/null +++ b/vendor/gems/rdoc-2.4.3/lib/rdoc/ri/cache.rb @@ -0,0 +1,187 @@ +require 'rdoc/ri' + +class RDoc::RI::ClassEntry + + attr_reader :name + attr_reader :path_names + + def initialize(path_name, name, in_class) + @path_names = [ path_name ] + @name = name + @in_class = in_class + @class_methods = [] + @instance_methods = [] + @inferior_classes = [] + end + + # We found this class in more than one place, so add + # in the name from there. + def add_path(path) + @path_names << path + end + + ## + # read in our methods and any classes and modules in our namespace. Methods + # are stored in files called name-c|i.yaml, where the 'name' portion is the + # external form of the method name and the c|i is a class|instance flag + + def load_from(dir) + return unless File.exist? dir + + Dir.foreach(dir) do |name| + next if name =~ /^\./ + + # convert from external to internal form, and + # extract the instance/class flag + + if name =~ /^(.*?)-(c|i).yaml$/ + external_name = $1 + is_class_method = $2 == "c" + internal_name = RDoc::RI::Writer.external_to_internal(external_name) + list = is_class_method ? @class_methods : @instance_methods + path = File.join(dir, name) + list << RDoc::RI::MethodEntry.new(path, internal_name, is_class_method, self) + else + full_name = File.join(dir, name) + if File.directory?(full_name) + inf_class = @inferior_classes.find {|c| c.name == name } + if inf_class + inf_class.add_path(full_name) + else + inf_class = RDoc::RI::ClassEntry.new(full_name, name, self) + @inferior_classes << inf_class + end + inf_class.load_from(full_name) + end + end + end + end + + # Return a list of any classes or modules that we contain + # that match a given string + + def contained_modules_matching(name) + @inferior_classes.find_all {|c| c.name[name]} + end + + def classes_and_modules + @inferior_classes + end + + # Return an exact match to a particular name + def contained_class_named(name) + @inferior_classes.find {|c| c.name == name} + end + + # return the list of local methods matching name + # We're split into two because we need distinct behavior + # when called from the _toplevel_ + def methods_matching(name, is_class_method) + local_methods_matching(name, is_class_method) + end + + # Find methods matching 'name' in ourselves and in + # any classes we contain + def recursively_find_methods_matching(name, is_class_method) + res = local_methods_matching(name, is_class_method) + @inferior_classes.each do |c| + res.concat(c.recursively_find_methods_matching(name, is_class_method)) + end + res + end + + + # Return our full name + def full_name + res = @in_class.full_name + res << "::" unless res.empty? + res << @name + end + + # Return a list of all out method names + def all_method_names + res = @class_methods.map {|m| m.full_name } + @instance_methods.each {|m| res << m.full_name} + res + end + + private + + # Return a list of all our methods matching a given string. + # Is +is_class_methods+ if 'nil', we don't care if the method + # is a class method or not, otherwise we only return + # those methods that match + def local_methods_matching(name, is_class_method) + + list = case is_class_method + when nil then @class_methods + @instance_methods + when true then @class_methods + when false then @instance_methods + else fail "Unknown is_class_method: #{is_class_method.inspect}" + end + + list.find_all {|m| m.name; m.name[name]} + end +end + +## +# A TopLevelEntry is like a class entry, but when asked to search for methods +# searches all classes, not just itself + +class RDoc::RI::TopLevelEntry < RDoc::RI::ClassEntry + def methods_matching(name, is_class_method) + res = recursively_find_methods_matching(name, is_class_method) + end + + def full_name + "" + end + + def module_named(name) + + end + +end + +class RDoc::RI::MethodEntry + attr_reader :name + attr_reader :path_name + + def initialize(path_name, name, is_class_method, in_class) + @path_name = path_name + @name = name + @is_class_method = is_class_method + @in_class = in_class + end + + def full_name + res = @in_class.full_name + unless res.empty? + if @is_class_method + res << "::" + else + res << "#" + end + end + res << @name + end +end + +## +# We represent everything known about all 'ri' files accessible to this program + +class RDoc::RI::Cache + + attr_reader :toplevel + + def initialize(dirs) + # At the top level we have a dummy module holding the + # overall namespace + @toplevel = RDoc::RI::TopLevelEntry.new('', '::', nil) + + dirs.each do |dir| + @toplevel.load_from(dir) + end + end + +end diff --git a/vendor/gems/rdoc-2.4.3/lib/rdoc/ri/descriptions.rb b/vendor/gems/rdoc-2.4.3/lib/rdoc/ri/descriptions.rb new file mode 100644 index 000000000..99a7cb11f --- /dev/null +++ b/vendor/gems/rdoc-2.4.3/lib/rdoc/ri/descriptions.rb @@ -0,0 +1,156 @@ +require 'yaml' +require 'rdoc/markup/fragments' +require 'rdoc/ri' + +## +# Descriptions are created by RDoc (in ri_generator) and written out in +# serialized form into the documentation tree. ri then reads these to generate +# the documentation + +class RDoc::RI::NamedThing + attr_reader :name + def initialize(name) + @name = name + end + + def <=>(other) + @name <=> other.name + end + + def hash + @name.hash + end + + def eql?(other) + @name.eql?(other) + end +end + +class RDoc::RI::AliasName < RDoc::RI::NamedThing; end + +class RDoc::RI::Attribute < RDoc::RI::NamedThing + attr_reader :rw, :comment + + def initialize(name, rw, comment) + super(name) + @rw = rw + @comment = comment + end +end + +class RDoc::RI::Constant < RDoc::RI::NamedThing + attr_reader :value, :comment + + def initialize(name, value, comment) + super(name) + @value = value + @comment = comment + end +end + +class RDoc::RI::IncludedModule < RDoc::RI::NamedThing; end + +class RDoc::RI::MethodSummary < RDoc::RI::NamedThing + def initialize(name="") + super + end +end + +class RDoc::RI::Description + attr_accessor :name + attr_accessor :full_name + attr_accessor :comment + + def serialize + self.to_yaml + end + + def self.deserialize(from) + YAML.load(from) + end + + def <=>(other) + @name <=> other.name + end +end + +class RDoc::RI::ModuleDescription < RDoc::RI::Description + + attr_accessor :class_methods + attr_accessor :class_method_extensions + attr_accessor :instance_methods + attr_accessor :instance_method_extensions + attr_accessor :attributes + attr_accessor :constants + attr_accessor :includes + + # merge in another class description into this one + def merge_in(old) + merge(@class_methods, old.class_methods) + merge(@instance_methods, old.instance_methods) + merge(@attributes, old.attributes) + merge(@constants, old.constants) + merge(@includes, old.includes) + if @comment.nil? || @comment.empty? + @comment = old.comment + else + unless old.comment.nil? or old.comment.empty? then + if @comment.nil? or @comment.empty? then + @comment = old.comment + else + @comment << RDoc::Markup::Flow::RULE.new + @comment.concat old.comment + end + end + end + end + + def display_name + "Module" + end + + # the 'ClassDescription' subclass overrides this + # to format up the name of a parent + def superclass_string + nil + end + + private + + def merge(into, from) + names = {} + into.each {|i| names[i.name] = i } + from.each {|i| names[i.name] = i } + into.replace(names.keys.sort.map {|n| names[n]}) + end +end + +class RDoc::RI::ClassDescription < RDoc::RI::ModuleDescription + attr_accessor :superclass + + def display_name + "Class" + end + + def superclass_string + if @superclass && @superclass != "Object" + @superclass + else + nil + end + end +end + +class RDoc::RI::MethodDescription < RDoc::RI::Description + + attr_accessor :is_class_method + attr_accessor :visibility + attr_accessor :block_params + attr_accessor :is_singleton + attr_accessor :aliases + attr_accessor :is_alias_for + attr_accessor :params + attr_accessor :source_path + +end + diff --git a/vendor/gems/rdoc-2.4.3/lib/rdoc/ri/display.rb b/vendor/gems/rdoc-2.4.3/lib/rdoc/ri/display.rb new file mode 100644 index 000000000..173995943 --- /dev/null +++ b/vendor/gems/rdoc-2.4.3/lib/rdoc/ri/display.rb @@ -0,0 +1,340 @@ +require 'rdoc/ri' + +## +# This is a kind of 'flag' module. If you want to write your own 'ri' display +# module (perhaps because you're writing an IDE), you write a class which +# implements the various 'display' methods in RDoc::RI::DefaultDisplay, and +# include the RDoc::RI::Display module in that class. +# +# To access your class from the command line, you can do +# +# ruby -r <your source file> ../ri .... + +module RDoc::RI::Display + + @@display_class = nil + + def self.append_features(display_class) + @@display_class = display_class + end + + def self.new(*args) + @@display_class.new(*args) + end + +end + +## +# A paging display module. Uses the RDoc::RI::Formatter class to do the actual +# presentation. + +class RDoc::RI::DefaultDisplay + + include RDoc::RI::Display + + attr_reader :formatter + + def initialize(formatter, width, use_stdout, output = $stdout) + @use_stdout = use_stdout + @formatter = formatter.new output, width, " " + end + + ## + # Display information about +klass+. Fetches additional information from + # +ri_reader+ as necessary. + + def display_class_info(klass) + page do + superclass = klass.superclass + + 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| + incs << inc.name + end + + @formatter.wrap(incs.sort.join(', ')) + end + + unless klass.constants.empty? + @formatter.blankline + @formatter.display_heading("Constants:", 2, "") + + constants = klass.constants.sort_by { |constant| constant.name } + + constants.each do |constant| + @formatter.wrap "#{constant.name} = #{constant.value}" + if constant.comment then + @formatter.indent do + @formatter.display_flow constant.comment + end + else + @formatter.break_to_newline + end + end + end + + unless klass.attributes.empty? then + @formatter.blankline + @formatter.display_heading 'Attributes:', 2, '' + + attributes = klass.attributes.sort_by { |attribute| attribute.name } + + attributes.each do |attribute| + if attribute.comment then + @formatter.wrap "#{attribute.name} (#{attribute.rw}):" + @formatter.indent do + @formatter.display_flow attribute.comment + end + else + @formatter.wrap "#{attribute.name} (#{attribute.rw})" + @formatter.break_to_newline + end + end + end + + return display_class_method_list(klass) + end + end + + ## + # Given a Hash mapping a class' methods to method types (returned by + # display_class_method_list), this method allows the user to choose one of + # the methods. + + def get_class_method_choice(method_map) + end + + ## + # Display methods on +klass+. Returns a hash mapping method name to method + # contents + + def display_class_method_list(klass) + method_map = {} + + class_data = [ + :class_methods, + :class_method_extensions, + :instance_methods, + :instance_method_extensions, + ] + + class_data.each do |data_type| + data = klass.send data_type + + unless data.nil? or data.empty? then + @formatter.blankline + + heading = data_type.to_s.split('_').join(' ').capitalize << ':' + @formatter.display_heading heading, 2, '' + + method_names = [] + data.each do |item| + method_names << item.name + + if(data_type == :class_methods || + data_type == :class_method_extensions) then + method_map["::#{item.name}"] = :class + method_map[item.name] = :class + else + # + # Since we iterate over instance methods after class methods, + # an instance method always will overwrite the unqualified + # class method entry for a class method of the same name. + # + method_map["##{item.name}"] = :instance + method_map[item.name] = :instance + end + end + method_names.sort! + + @formatter.wrap method_names.join(', ') + end + end + + method_map + end + private :display_class_method_list + + ## + # Display an Array of RDoc::Markup::Flow objects, +flow+. + + def display_flow(flow) + if flow and not flow.empty? then + @formatter.display_flow flow + else + @formatter.wrap '[no description]' + end + end + + ## + # Display information about +method+. + + def display_method_info(method) + page do + @formatter.draw_line(method.full_name) + display_params(method) + + @formatter.draw_line + display_flow(method.comment) + + if method.aliases and not method.aliases.empty? then + @formatter.blankline + aka = "(also known as #{method.aliases.map { |a| a.name }.join(', ')})" + @formatter.wrap aka + end + end + end + + ## + # Display the list of +methods+. + + def display_method_list(methods) + page do + @formatter.wrap "More than one method matched your request. You can refine your search by asking for information on one of:" + @formatter.blankline + + methods.each do |method| + @formatter.raw_print_line "#{method.full_name} [#{method.source_path}]\n" + end + end + end + + ## + # Display a list of +methods+ and allow the user to select one of them. + + def display_method_list_choice(methods) + page do + @formatter.wrap "More than one method matched your request. Please choose one of the possible matches." + @formatter.blankline + + methods.each_with_index do |method, index| + @formatter.raw_print_line "%3d %s [%s]\n" % [index + 1, method.full_name, method.source_path] + end + + @formatter.raw_print_line ">> " + + choice = $stdin.gets.strip! + + if(choice == '') + return + end + + choice = choice.to_i + + if ((choice == 0) || (choice > methods.size)) then + @formatter.raw_print_line "Invalid choice!\n" + else + method = methods[choice - 1] + display_method_info(method) + end + end + end + + ## + # Display the params for +method+. + + def display_params(method) + params = method.params + + if params[0,1] == "(" then + if method.is_singleton + params = method.full_name + params + else + params = method.name + params + end + end + + params.split(/\n/).each do |param| + @formatter.wrap param + @formatter.break_to_newline + end + + @formatter.blankline + @formatter.wrap("From #{method.source_path}") + end + + ## + # List the classes in +classes+. + + def list_known_classes(classes) + if classes.empty? then + warn_no_database + else + page do + @formatter.draw_line "Known classes and modules" + @formatter.blankline + + classes.sort.each do |klass| + @formatter.wrap klass + end + end + end + end + + ## + # Paginates output through a pager program. + + def page + if pager = setup_pager then + begin + orig_output = @formatter.output + @formatter.output = pager + yield + ensure + @formatter.output = orig_output + pager.close + end + else + yield + end + rescue Errno::EPIPE + end + + ## + # Sets up a pager program to pass output through. + + def setup_pager + unless @use_stdout then + for pager in [ ENV['PAGER'], "less", "more", 'pager' ].compact.uniq + return IO.popen(pager, "w") rescue nil + end + @use_stdout = true + nil + end + end + + ## + # Displays a message that describes how to build RI data. + + def warn_no_database + output = @formatter.output + + output.puts "No ri data found" + output.puts + output.puts "If you've installed Ruby yourself, you need to generate documentation using:" + output.puts + output.puts " make install-doc" + output.puts + output.puts "from the same place you ran `make` to build ruby." + output.puts + output.puts "If you installed Ruby from a packaging system, then you may need to" + output.puts "install an additional package, or ask the packager to enable ri generation." + end + +end diff --git a/vendor/gems/rdoc-2.4.3/lib/rdoc/ri/driver.rb b/vendor/gems/rdoc-2.4.3/lib/rdoc/ri/driver.rb new file mode 100644 index 000000000..310ff8487 --- /dev/null +++ b/vendor/gems/rdoc-2.4.3/lib/rdoc/ri/driver.rb @@ -0,0 +1,828 @@ +require 'abbrev' +require 'optparse' +require 'yaml' + +begin + require 'readline' +rescue LoadError +end + +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 + + ## + # This class offers both Hash and OpenStruct functionality. We convert from + # the Core Hash to this before calling any of the display methods, in order + # to give the display methods a cleaner API for accessing the data. + + class OpenStructHash < Hash + + ## + # This method converts from a Hash to an OpenStructHash. + + def self.convert(object) + case object + when Hash then + new_hash = new # Convert Hash -> OpenStructHash + + object.each do |key, value| + new_hash[key] = convert value + end + + new_hash + when Array then + object.map do |element| + convert element + end + else + object + end + 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 + + def method_missing method, *args + self[method.to_s] + end + end + + class Error < RDoc::RI::Error; end + + class NotFoundError < Error + def message + "Nothing known about #{super}" + end + end + + attr_accessor :homepath # :nodoc: + + ## + # Default options for ri + + def self.default_options + options = {} + options[:use_stdout] = !$stdout.tty? + options[:width] = 72 + options[:formatter] = RDoc::RI::Formatter.for 'plain' + options[:interactive] = false + options[:use_cache] = true + + # By default all standard paths are used. + options[:use_system] = true + options[:use_site] = true + options[:use_home] = true + options[:use_gems] = true + options[:extra_doc_dirs] = [] + + return options + end + + ## + # Parses +argv+ and returns a Hash of options + + def self.process_args(argv) + options = default_options + + opts = OptionParser.new do |opt| + opt.program_name = File.basename $0 + opt.version = RDoc::VERSION + opt.release = nil + 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("--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 + + 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 + + options[:extra_doc_dirs] << File.expand_path(dir) + end + end + + opt.separator nil + + opt.on("--[no-]use-cache", + "Whether or not to use ri's cache.", + "True by default.") do |value| + options[:use_cache] = value + end + + opt.separator nil + + opt.on("--no-standard-docs", + "Do not include documentation from", + "the Ruby standard library, site_lib,", + "installed gems, or ~/.rdoc.", + "Equivalent to specifying", + "the options --no-system, --no-site, --no-gems,", + "and --no-home") do + options[:use_system] = false + options[:use_site] = false + options[:use_gems] = false + options[:use_home] = false + end + + opt.separator nil + + opt.on("--[no-]system", + "Include documentation from Ruby's standard", + "library. Defaults to true.") do |value| + options[:use_system] = value + end + + opt.separator nil + + opt.on("--[no-]site", + "Include documentation from libraries", + "installed in site_lib.", + "Defaults to true.") do |value| + options[:use_site] = value + end + + opt.separator nil + + opt.on("--[no-]gems", + "Include documentation from RubyGems.", + "Defaults to true.") do |value| + options[:use_gems] = value + end + + opt.separator nil + + opt.on("--[no-]home", + "Include documentation stored in ~/.rdoc.", + "Defaults to true.") do |value| + options[:use_home] = value + end + + opt.separator nil + + opt.on("--list-doc-dirs", + "List the directories from which ri will", + "source documentation on stdout and exit.") do + options[:list_doc_dirs] = true + end + + opt.separator nil + + opt.on("--no-pager", "-T", + "Send output directly to stdout,", + "rather than to a pager.") do + options[:use_stdout] = true + end + + opt.on("--interactive", "-i", + "This makes ri go into interactive mode.", + "When ri is in interactive mode it will", + "allow the user to disambiguate lists of", + "methods in case multiple methods match", + "against a method search string. It also", + "will allow the user to enter in a method", + "name (with auto-completion, if readline", + "is supported) when viewing a class.") do + options[:interactive] = true + 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[:formatter] ||= RDoc::RI::Formatter.for('plain') + options[:use_stdout] ||= !$stdout.tty? + options[:use_stdout] ||= options[:interactive] + options[:width] ||= 72 + + options + + rescue OptionParser::InvalidArgument, OptionParser::InvalidOption => e + puts opts + puts + puts e + exit 1 + end + + ## + # Runs the ri command line executable using +argv+ + + def self.run(argv = ARGV) + options = process_args argv + ri = new options + ri.run + end + + def initialize(initial_options={}) + options = self.class.default_options.update(initial_options) + + @names = options[:names] + @class_cache_name = 'classes' + + @doc_dirs = RDoc::RI::Paths.path(options[:use_system], + options[:use_site], + options[:use_home], + options[:use_gems], + options[:extra_doc_dirs]) + + @homepath = RDoc::RI::Paths.raw_path(false, false, true, false).first + @homepath = @homepath.sub(/\.rdoc/, '.ri') + @sys_dir = RDoc::RI::Paths.raw_path(true, false, false, false).first + @list_doc_dirs = options[:list_doc_dirs] + + FileUtils.mkdir_p cache_file_path unless File.directory? cache_file_path + @cache_doc_dirs_path = File.join cache_file_path, ".doc_dirs" + + @use_cache = options[:use_cache] + @class_cache = nil + + @interactive = options[:interactive] + @display = RDoc::RI::DefaultDisplay.new(options[:formatter], + options[:width], + options[:use_stdout]) + end + + ## + # Cache of classes ri knows about + + def class_cache + return @class_cache if @class_cache + + # Get the documentation directories used to make the cache in order to see + # whether the cache is valid for the current ri instantiation. + if(File.readable?(@cache_doc_dirs_path)) + cache_doc_dirs = IO.read(@cache_doc_dirs_path).split("\n") + else + cache_doc_dirs = [] + end + + newest = map_dirs('created.rid') do |f| + File.mtime f if test ?f, f + end.max + + # An up to date cache file must have been created more recently than + # the last modification of any of the documentation directories. It also + # must have been created with the same documentation directories + # as those from which ri currently is sourcing documentation. + up_to_date = (File.exist?(class_cache_file_path) and + newest and newest < File.mtime(class_cache_file_path) and + (cache_doc_dirs == @doc_dirs)) + + if up_to_date and @use_cache then + open class_cache_file_path, 'rb' do |fp| + begin + @class_cache = Marshal.load fp.read + rescue + # + # This shouldn't be necessary, since the up_to_date logic above + # should force the cache to be recreated when a new version of + # rdoc is installed. This seems like a worthwhile enhancement + # to ri's robustness, however. + # + $stderr.puts "Error reading the class cache; recreating the class cache!" + @class_cache = create_class_cache + end + end + else + @class_cache = create_class_cache + end + + @class_cache + end + + ## + # Creates the class cache if it is empty + + def create_class_cache + class_cache = OpenStructHash.new + + if(@use_cache) + # Dump the documentation directories to a file in the cache, so that + # we only will use the cache for future instantiations with identical + # documentation directories. + File.open @cache_doc_dirs_path, "wb" do |fp| + fp << @doc_dirs.join("\n") + end + end + + classes = map_dirs('**/cdesc*.yaml') { |f| Dir[f] } + warn "Updating ri class cache with #{classes.size} classes..." + populate_class_cache class_cache, classes + + write_cache class_cache, class_cache_file_path + + class_cache + end + + ## + # Populates +class_cache+ with +classes+, adding +extension+ data for found + # methods when asked + + 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.merge_enums desc + klass["sources"] << cdesc + end + end + end + + ## + # Path to the class_cache + + def class_cache_file_path + File.join cache_file_path, @class_cache_name + end + + ## + # Path to the cache file for +klassname+ + + def cache_file_for(klassname) + File.join cache_file_path, klassname.gsub(/:+/, "-") + end + + ## + # Directory where cache files live + + def cache_file_path + File.join @homepath, 'cache' + end + + ## + # Displays the module, class or method +name+. For methods, locates the + # method in the ancestors list if it isn't in the named module. + + def display_name(name) + if class_cache.key? name then + method_map = display_class name + elsif name =~ /::|\#|\./ then + method = nil + klass, = parse_name name + + klass = expand_klass klass unless class_cache.key? klass + + orig_klass = klass + orig_name = name + + loop do + method = lookup_method name, klass + + break 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_method method + else + methods = select_methods(/#{name}/) + + if methods.size == 0 + raise NotFoundError, name + elsif methods.size == 1 + display_method methods[0] + else + @display.display_method_list methods + end + end + end + + ## + # Displays info for class or module +name+ + + def display_class(name) + klass = class_cache[name] + @display.display_class_info klass + end + + ## + # Displays info for method +method+ + + def display_method(method) + @display.display_method_info method + end + + ## + # Runs ri interactively using Readline if it is available. + + def interactive + formatter = @display.formatter + + if defined? Readline then + # prepare abbreviations for tab completion + klasses = class_cache.keys + + Readline.completion_proc = proc do |name| + case name + when /(#|\.|::)([^A-Z]|$)/ then + methods = [] + method_type = $1 == '.' ? '#|::' : $1 + + klass, method = if $2.empty? then + [$`, ''] + else + parse_name name + end + + cache = load_cache_for klass + + methods += cache.keys.select do |method_name| + method_name =~ /^#{klass}#{method_type}#{method}/ + end + + # TODO ancestor lookup + + if method_type == '::' and method.empty? then + methods += klasses.grep(/^#{klass}::/) + end + + methods + when /^[A-Z]\w*/ then + klasses.grep(/^#{name}/) + else + [] + end + end + end + + formatter.raw_print_line "\nEnter the method name you want to look up.\n" + + if defined? Readline then + formatter.raw_print_line "You can use tab to autocomplete.\n" + end + + formatter.raw_print_line "Enter a blank line to exit.\n\n" + + loop do + name = if defined? Readline then + Readline.readline ">> " + else + formatter.raw_print_line ">> " + $stdin.gets + end + + return if name.nil? or name.empty? + + name = name.strip + + begin + display_name name + rescue NotFoundError => e + formatter.raw_print_line "#{e.message}\n" + end + end + + rescue Interrupt + exit + end + + ## + # Expands abbreviated klass +klass+ into a fully-qualified klass. "Zl::Da" + # will be expanded to Zlib::DataError. + + def expand_klass(klass) + klass.split('::').inject '' do |expanded, klass_part| + expanded << '::' unless expanded.empty? + short = expanded << klass_part + + subset = class_cache.keys.select do |klass_name| + klass =~ /^#{expanded}[^:]*$/ + end + + abbrevs = Abbrev.abbrev subset + + expanded = abbrevs[short] + + raise NotFoundError, short unless expanded + + expanded.dup + end + end + + ## + # Loads the cache for +klassname+ + + 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) and + @use_cache then + open path, 'rb' do |fp| + begin + cache = Marshal.load fp.read + rescue + # + # The cache somehow is bad. Recreate the cache. + # + $stderr.puts "Error reading the cache for #{klassname}; recreating the cache!" + cache = create_cache_for klassname, path + end + end + else + cache = create_cache_for klassname, path + end + + cache + end + + ## + # Writes a cache file for +klassname+ to +path+ + + def create_cache_for(klassname, path) + klass = class_cache[klassname] + return nil unless klass + + method_files = klass["sources"] + cache = OpenStructHash.new + + 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 + + if system_file then + method["source_path"] = "Ruby #{RDoc::RI::Paths::VERSION}" + else + gem = Gem.path.any? do |gem_path| + pattern = File.join Regexp.escape(gem_path), 'doc', '(.*?)', '' + + f =~ /^#{pattern}/ + end + + method["source_path"] = if gem then + "gem #{$1}" + else + f + end + end + + name = method["full_name"] + cache[name] = method + end + end + + write_cache cache, path + end + + ## + # Finds the next ancestor of +orig_klass+ after +klass+. + + def lookup_ancestor(klass, orig_klass) + # This is a bit hacky, but ri will go into an infinite loop otherwise, + # since Object has an Object ancestor for some reason. Depending on the + # documentation state, I've seen Kernel as an ancestor of Object and not + # as an ancestor of Object. + if orig_klass == "Object" && (klass == "Kernel" || klass == "Object") then + return nil + end + + cache = class_cache[orig_klass] + + return nil unless cache + + ancestors = [orig_klass] + ancestors.push(*cache.includes.map { |inc| inc['name'] }) + ancestors << cache.superclass + + ancestor_index = ancestors.index klass + + if ancestor_index + ancestor = ancestors[ancestors.index(klass) + 1] + return ancestor if ancestor + end + + 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) + @doc_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 + + ## + # Reads ri YAML data from +path+, converting RDoc 1.x classes to RDoc 2.x + # classes + + def read_yaml(path) + data = File.read path + + # Necessary to be backward-compatible with documentation generated + # by earliar RDoc versions. + data = data.gsub(/ \!ruby\/(object|struct):(RDoc::RI|RI).*/, '') + data = data.gsub(/ \!ruby\/(object|struct):SM::(\S+)/, + ' !ruby/\1:RDoc::Markup::\2') + + OpenStructHash.convert YAML.load(data) + end + + ## + # Looks up and displays ri data according to the options given. + + def run + if @list_doc_dirs then + puts @doc_dirs + elsif @interactive then + interactive + elsif @names.empty? then + @display.list_known_classes class_cache.keys.sort + else + @names.each do |name| + display_name name + end + end + rescue NotFoundError => e + abort e.message + end + + ## + # Selects methods matching +pattern+ from all modules + + 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 + + ## + # Writes +cache+ to +path+ + + def write_cache(cache, path) + if @use_cache then + File.open path, "wb" do |cache_file| + Marshal.dump cache, cache_file + end + end + + cache + rescue Errno::EISDIR # HACK toplevel, replace with main + cache + end + +end + diff --git a/vendor/gems/rdoc-2.4.3/lib/rdoc/ri/formatter.rb b/vendor/gems/rdoc-2.4.3/lib/rdoc/ri/formatter.rb new file mode 100644 index 000000000..b2c9d07b3 --- /dev/null +++ b/vendor/gems/rdoc-2.4.3/lib/rdoc/ri/formatter.rb @@ -0,0 +1,654 @@ +require 'rdoc/ri' +require 'rdoc/markup' + +class RDoc::RI::Formatter + + attr_writer :indent + attr_accessor :output + + FORMATTERS = { } + + def self.for(name) + FORMATTERS[name.downcase] + end + + def self.list + FORMATTERS.keys.sort.join ", " + end + + def initialize(output, width, indent) + @output = output + @width = width + @indent = indent + @original_indent = indent.dup + end + + def draw_line(label=nil) + len = @width + len -= (label.size + 1) if label + + if len > 0 then + @output.print '-' * len + if label + @output.print ' ' + bold_print label + end + + @output.puts + else + @output.print '-' * @width + @output.puts + + @output.puts label + end + end + + def indent + return @indent unless block_given? + + begin + indent = @indent.dup + @indent += @original_indent + yield + ensure + @indent = indent + end + end + + def wrap(txt, prefix=@indent, linelen=@width) + return unless txt && !txt.empty? + + work = conv_markup(txt) + textLen = linelen - prefix.length + patt = Regexp.new("^(.{0,#{textLen}})[ \n]") + next_prefix = prefix.tr("^ ", " ") + + res = [] + + while work.length > textLen + if work =~ patt + res << $1 + work.slice!(0, $&.length) + else + res << work.slice!(0, textLen) + end + end + res << work if work.length.nonzero? + @output.puts(prefix + res.join("\n" + next_prefix)) + end + + def blankline + @output.puts + end + + ## + # Called when we want to ensure a new 'wrap' starts on a newline. Only + # needed for HtmlFormatter, because the rest do their own line breaking. + + def break_to_newline + end + + def bold_print(txt) + @output.print txt + end + + def raw_print_line(txt) + @output.print txt + end + + ## + # Convert HTML entities back to ASCII + + def conv_html(txt) + txt = txt.gsub(/>/, '>') + txt.gsub!(/</, '<') + txt.gsub!(/"/, '"') + txt.gsub!(/&/, '&') + txt + end + + ## + # Convert markup into display form + + def conv_markup(txt) + txt = txt.gsub(%r{<tt>(.*?)</tt>}, '+\1+') + txt.gsub!(%r{<code>(.*?)</code>}, '+\1+') + txt.gsub!(%r{<b>(.*?)</b>}, '*\1*') + txt.gsub!(%r{<em>(.*?)</em>}, '_\1_') + txt + end + + def display_list(list) + case list.type + when :BULLET + prefixer = proc { |ignored| @indent + "* " } + + when :NUMBER, :UPPERALPHA, :LOWERALPHA then + start = case list.type + when :NUMBER then 1 + when :UPPERALPHA then 'A' + when :LOWERALPHA then 'a' + end + + prefixer = proc do |ignored| + res = @indent + "#{start}.".ljust(4) + start = start.succ + res + end + + when :LABELED, :NOTE then + longest = 0 + + list.contents.each do |item| + if RDoc::Markup::Flow::LI === item and item.label.length > longest then + longest = item.label.length + end + end + + longest += 1 + + prefixer = proc { |li| @indent + li.label.ljust(longest) } + + else + raise ArgumentError, "unknown list type #{list.type}" + end + + list.contents.each do |item| + if RDoc::Markup::Flow::LI === item then + prefix = prefixer.call item + display_flow_item item, prefix + else + display_flow_item item + end + end + end + + def display_flow_item(item, prefix = @indent) + case item + when RDoc::Markup::Flow::P, RDoc::Markup::Flow::LI + wrap(conv_html(item.body), prefix) + blankline + + when RDoc::Markup::Flow::LIST + display_list(item) + + when RDoc::Markup::Flow::VERB + display_verbatim_flow_item(item, @indent) + + when RDoc::Markup::Flow::H + display_heading(conv_html(item.text), item.level, @indent) + + when RDoc::Markup::Flow::RULE + draw_line + + else + raise RDoc::Error, "Unknown flow element: #{item.class}" + end + end + + def display_verbatim_flow_item(item, prefix=@indent) + item.body.split(/\n/).each do |line| + @output.print @indent, conv_html(line), "\n" + end + blankline + end + + def display_heading(text, level, indent) + text = strip_attributes text + + case level + when 1 then + ul = "=" * text.length + @output.puts + @output.puts text.upcase + @output.puts ul + + when 2 then + ul = "-" * text.length + @output.puts + @output.puts text + @output.puts ul + else + @output.print indent, text, "\n" + end + + @output.puts + end + + def display_flow(flow) + flow.each do |f| + display_flow_item(f) + end + end + + def strip_attributes(text) + text.gsub(/(<\/?(?:b|code|em|i|tt)>)/, '') + end + +end + +## +# Handle text with attributes. We're a base class: there are different +# presentation classes (one, for example, uses overstrikes to handle bold and +# underlining, while another using ANSI escape sequences. + +class RDoc::RI::AttributeFormatter < RDoc::RI::Formatter + + BOLD = 1 + ITALIC = 2 + CODE = 4 + + ATTR_MAP = { + "b" => BOLD, + "code" => CODE, + "em" => ITALIC, + "i" => ITALIC, + "tt" => CODE + } + + AttrChar = Struct.new :char, :attr + + class AttributeString + attr_reader :txt + + def initialize + @txt = [] + @optr = 0 + end + + def <<(char) + @txt << char + end + + def empty? + @optr >= @txt.length + end + + # accept non space, then all following spaces + def next_word + start = @optr + len = @txt.length + + while @optr < len && @txt[@optr].char != " " + @optr += 1 + end + + while @optr < len && @txt[@optr].char == " " + @optr += 1 + end + + @txt[start...@optr] + end + end + + ## + # Overrides base class. Looks for <tt>...</tt> etc sequences and generates + # an array of AttrChars. This array is then used as the basis for the + # split. + + def wrap(txt, prefix=@indent, linelen=@width) + return unless txt && !txt.empty? + + txt = add_attributes_to(txt) + next_prefix = prefix.tr("^ ", " ") + linelen -= prefix.size + + line = [] + + until txt.empty? + word = txt.next_word + if word.size + line.size > linelen + write_attribute_text(prefix, line) + prefix = next_prefix + line = [] + end + line.concat(word) + end + + write_attribute_text(prefix, line) if line.length > 0 + end + + protected + + def write_attribute_text(prefix, line) + @output.print prefix + line.each do |achar| + @output.print achar.char + end + @output.puts + end + + def bold_print(txt) + @output.print txt + end + + private + + def add_attributes_to(txt) + tokens = txt.split(%r{(</?(?:b|code|em|i|tt)>)}) + text = AttributeString.new + attributes = 0 + tokens.each do |tok| + case tok + when %r{^</(\w+)>$} then attributes &= ~(ATTR_MAP[$1]||0) + when %r{^<(\w+)>$} then attributes |= (ATTR_MAP[$1]||0) + else + tok.split(//).each {|ch| text << AttrChar.new(ch, attributes)} + end + end + text + end + +end + +## +# This formatter generates overstrike-style formatting, which works with +# pagers such as man and less. + +class RDoc::RI::OverstrikeFormatter < RDoc::RI::AttributeFormatter + + BS = "\C-h" + + def write_attribute_text(prefix, line) + @output.print prefix + + line.each do |achar| + attr = achar.attr + @output.print "_", BS if (attr & (ITALIC + CODE)) != 0 + @output.print achar.char, BS if (attr & BOLD) != 0 + @output.print achar.char + end + + @output.puts + end + + ## + # Draw a string in bold + + def bold_print(text) + text.split(//).each do |ch| + @output.print ch, BS, ch + end + end + +end + +## +# This formatter uses ANSI escape sequences to colorize stuff works with +# pagers such as man and less. + +class RDoc::RI::AnsiFormatter < RDoc::RI::AttributeFormatter + + def initialize(*args) + super + @output.print "\033[0m" + end + + def write_attribute_text(prefix, line) + @output.print prefix + curr_attr = 0 + line.each do |achar| + attr = achar.attr + if achar.attr != curr_attr + update_attributes(achar.attr) + curr_attr = achar.attr + end + @output.print achar.char + end + update_attributes(0) unless curr_attr.zero? + @output.puts + end + + def bold_print(txt) + @output.print "\033[1m#{txt}\033[m" + end + + HEADINGS = { + 1 => ["\033[1;32m", "\033[m"], + 2 => ["\033[4;32m", "\033[m"], + 3 => ["\033[32m", "\033[m"], + } + + def display_heading(text, level, indent) + level = 3 if level > 3 + heading = HEADINGS[level] + @output.print indent + @output.print heading[0] + @output.print strip_attributes(text) + @output.puts heading[1] + end + + private + + ATTR_MAP = { + BOLD => "1", + ITALIC => "33", + CODE => "36" + } + + def update_attributes(attr) + str = "\033[" + for quality in [ BOLD, ITALIC, CODE] + unless (attr & quality).zero? + str << ATTR_MAP[quality] + end + end + @output.print str, "m" + end + +end + +## +# This formatter uses HTML. + +class RDoc::RI::HtmlFormatter < RDoc::RI::AttributeFormatter + + ## + # We depend on HTML4-conforming user agents to ignore an empty p element + + def blankline + @output.puts '<p />' + end + + ## + # Emboldens +text+ + + def bold_print(text) + tag("b") { text } + end + + ## + # Outputs a forced line break element + + def break_to_newline + @output.puts '<br />' + end + + ## + # Outputs heading elements for +text+ with +level+ up to 4. Ignores + # +indent+. + + def display_heading(text, level, indent) + level = 4 if level > 4 + tag("h#{level}") { text } + @output.puts + end + + ## + # Outputs +list+ which is displayed as follows: + # + # BULLET:: unordered list + # NUMBER:: ordered list + # LABELED:: definition list + # NOTE:: table + + def display_list(list) + case list.type + when :BULLET then + list_type = "ul" + prefixer = proc { |ignored| '<li>' } + suffix = '</li>' + + when :NUMBER, :UPPERALPHA, :LOWERALPHA then + list_type = "ol" + prefixer = proc { |ignored| '<li>' } + suffix = '</li>' + + when :LABELED then + list_type = "dl" + prefixer = proc do |li| + "<dt><b>#{escape li.label}</b></dt><dd>" + end + suffix = '</dd>' + + when :NOTE then + list_type = "table" + prefixer = proc do |li| + %{<tr valign="top"><td>#{li.label.gsub(/ /, ' ')}</td><td>} + end + suffix = '</td></tr>' + else + fail "unknown list type" + end + + @output.print "<#{list_type}>" + + list.contents.each do |item| + if item.kind_of? RDoc::Markup::Flow::LI + prefix = prefixer.call item + @output.print prefix + display_flow_item item, prefix + @output.print suffix + else + display_flow_item item + end + end + + @output.print "</#{list_type}>" + end + + ## + # Outputs a preformatted section for +item+. +prefix+ is ignored. + + def display_verbatim_flow_item(item, prefix=@indent) + @output.print '<pre>' + + item.body.split(/\n/).each do |line| + @output.puts escape(line) + end + + @output.puts '</pre>' + end + + ## + # Outputs a horizontal rule element, optionally labeled above with +label+ in + # bold. + + def draw_line(label = nil) + bold_print label if label + + @output.puts "<hr />" + end + + def write_attribute_text(prefix, line) + curr_attr = 0 + + line.each do |achar| + attr = achar.attr + if achar.attr != curr_attr then + update_attributes curr_attr, achar.attr + curr_attr = achar.attr + end + @output.print escape(achar.char) + end + + update_attributes curr_attr, 0 unless curr_attr.zero? + end + + private + + ATTR_MAP = { + BOLD => "b>", + ITALIC => "i>", + CODE => "tt>" + } + + def update_attributes(current, wanted) + str = "" + # first turn off unwanted ones + off = current & ~wanted + for quality in [ BOLD, ITALIC, CODE] + if (off & quality) > 0 + str << "</" + ATTR_MAP[quality] + end + end + + # now turn on wanted + for quality in [ BOLD, ITALIC, CODE] + unless (wanted & quality).zero? + str << "<" << ATTR_MAP[quality] + end + end + @output.print str + end + + def tag(code) + @output.print("<#{code}>") + @output.print(yield) + @output.print("</#{code}>") + end + + def escape(str) + str = str.gsub(/&/n, '&') + str.gsub!(/\"/n, '"') + str.gsub!(/>/n, '>') + str.gsub!(/</n, '<') + str + end + +end + +## +# This formatter reduces extra lines for a simpler output. It improves way +# output looks for tools like IRC bots. + +class RDoc::RI::SimpleFormatter < RDoc::RI::Formatter + + ## + # No extra blank lines + + def blankline + end + + ## + # Display labels only, no lines + + def draw_line(label=nil) + unless label.nil? then + bold_print(label) + @output.puts + end + end + + ## + # Place heading level indicators inline with heading. + + def display_heading(text, level, indent) + text = strip_attributes(text) + case level + when 1 + @output.puts "= " + text.upcase + when 2 + @output.puts "-- " + text + else + @output.print indent, text, "\n" + end + end + +end + +RDoc::RI::Formatter::FORMATTERS['plain'] = RDoc::RI::Formatter +RDoc::RI::Formatter::FORMATTERS['simple'] = RDoc::RI::SimpleFormatter +RDoc::RI::Formatter::FORMATTERS['bs'] = RDoc::RI::OverstrikeFormatter +RDoc::RI::Formatter::FORMATTERS['ansi'] = RDoc::RI::AnsiFormatter +RDoc::RI::Formatter::FORMATTERS['html'] = RDoc::RI::HtmlFormatter diff --git a/vendor/gems/rdoc-2.4.3/lib/rdoc/ri/paths.rb b/vendor/gems/rdoc-2.4.3/lib/rdoc/ri/paths.rb new file mode 100644 index 000000000..2f72b9dfd --- /dev/null +++ b/vendor/gems/rdoc-2.4.3/lib/rdoc/ri/paths.rb @@ -0,0 +1,93 @@ +require 'rdoc/ri' + +## +# Encapsulate all the strangeness to do with finding out where to find RDoc +# files +# +# We basically deal with three directories: +# +# 1. The 'system' documentation directory, which holds the documentation +# distributed with Ruby, and which is managed by the Ruby install process +# 2. The 'site' directory, which contains site-wide documentation added +# locally. +# 3. The 'user' documentation directory, stored under the user's own home +# directory. +# +# There's contention about all this, but for now: +# +# system:: $datadir/ri/<ver>/system/... +# site:: $datadir/ri/<ver>/site/... +# user:: ~/.rdoc + +module RDoc::RI::Paths + + #:stopdoc: + require 'rbconfig' + + DOC_DIR = "doc/rdoc" + + VERSION = RbConfig::CONFIG['ruby_version'] + + base = File.join(RbConfig::CONFIG['datadir'], "ri", VERSION) + SYSDIR = File.join(base, "system") + SITEDIR = File.join(base, "site") + homedir = ENV['HOME'] || ENV['USERPROFILE'] || ENV['HOMEPATH'] + + if homedir then + HOMEDIR = File.join(homedir, ".rdoc") + else + HOMEDIR = nil + end + + begin + require 'rubygems' unless defined?(Gem) and defined?(Gem::Enable) and + Gem::Enable + + # HACK dup'd from Gem.latest_partials and friends + all_paths = [] + + all_paths = Gem.path.map do |dir| + Dir[File.join(dir, 'doc', '*', 'ri')] + end.flatten + + 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] + end + end + end + + GEMDIRS = ri_paths.map { |k,v| v.last }.sort + rescue LoadError + GEMDIRS = [] + end + + # Returns the selected documentation directories as an Array, or PATH if no + # overriding directories were given. + + def self.path(use_system, use_site, use_home, use_gems, *extra_dirs) + path = raw_path(use_system, use_site, use_home, use_gems, *extra_dirs) + return path.select { |directory| File.directory? directory } + end + + # Returns the selected documentation directories including nonexistent + # directories. Used to print out what paths were searched if no ri was + # found. + + def self.raw_path(use_system, use_site, use_home, use_gems, *extra_dirs) + path = [] + path << extra_dirs unless extra_dirs.empty? + path << SYSDIR if use_system + path << SITEDIR if use_site + path << HOMEDIR if use_home + path << GEMDIRS if use_gems + + return path.flatten.compact + end +end diff --git a/vendor/gems/rdoc-2.4.3/lib/rdoc/ri/reader.rb b/vendor/gems/rdoc-2.4.3/lib/rdoc/ri/reader.rb new file mode 100644 index 000000000..de3c8d9af --- /dev/null +++ b/vendor/gems/rdoc-2.4.3/lib/rdoc/ri/reader.rb @@ -0,0 +1,106 @@ +require 'rdoc/ri' +require 'rdoc/ri/descriptions' +require 'rdoc/ri/writer' +require 'rdoc/markup/to_flow' + +class RDoc::RI::Reader + + def initialize(ri_cache) + @cache = ri_cache + end + + def top_level_namespace + [ @cache.toplevel ] + end + + def lookup_namespace_in(target, namespaces) + result = [] + for n in namespaces + result.concat(n.contained_modules_matching(target)) + end + result + end + + def find_class_by_name(full_name) + names = full_name.split(/::/) + ns = @cache.toplevel + for name in names + ns = ns.contained_class_named(name) + return nil if ns.nil? + end + get_class(ns) + end + + def find_methods(name, is_class_method, namespaces) + result = [] + namespaces.each do |ns| + result.concat ns.methods_matching(name, is_class_method) + end + result + end + + ## + # Return the MethodDescription for a given MethodEntry by deserializing the + # YAML + + def get_method(method_entry) + path = method_entry.path_name + File.open(path) { |f| RDoc::RI::Description.deserialize(f) } + end + + ## + # Return a class description + + def get_class(class_entry) + result = nil + for path in class_entry.path_names + path = RDoc::RI::Writer.class_desc_path(path, class_entry) + desc = File.open(path) {|f| RDoc::RI::Description.deserialize(f) } + if result + result.merge_in(desc) + else + result = desc + end + end + result + end + + ## + # Return the names of all classes and modules + + def full_class_names + res = [] + find_classes_in(res, @cache.toplevel) + end + + ## + # Return a list of all classes, modules, and methods + + def all_names + res = [] + find_names_in(res, @cache.toplevel) + end + + private + + def find_classes_in(res, klass) + classes = klass.classes_and_modules + for c in classes + res << c.full_name + find_classes_in(res, c) + end + res + end + + def find_names_in(res, klass) + classes = klass.classes_and_modules + for c in classes + res << c.full_name + res.concat c.all_method_names + find_names_in(res, c) + end + res + end + +end + diff --git a/vendor/gems/rdoc-2.4.3/lib/rdoc/ri/util.rb b/vendor/gems/rdoc-2.4.3/lib/rdoc/ri/util.rb new file mode 100644 index 000000000..4e91eb978 --- /dev/null +++ b/vendor/gems/rdoc-2.4.3/lib/rdoc/ri/util.rb @@ -0,0 +1,79 @@ +require 'rdoc/ri' + +## +# Break argument into its constituent class or module names, an +# optional method type, and a method name + +class RDoc::RI::NameDescriptor + + attr_reader :class_names + attr_reader :method_name + + ## + # true and false have the obvious meaning. nil means we don't care + + attr_reader :is_class_method + + ## + # +arg+ may be + # + # 1. A class or module name (optionally qualified with other class or module + # names (Kernel, File::Stat etc) + # 2. A method name + # 3. A method name qualified by a optionally fully qualified class or module + # name + # + # We're fairly casual about delimiters: folks can say Kernel::puts, + # Kernel.puts, or Kernel\#puts for example. There's one exception: if you + # say IO::read, we look for a class method, but if you say IO.read, we look + # for an instance method + + def initialize(arg) + @class_names = [] + separator = nil + + tokens = arg.split(/(\.|::|#)/) + + # Skip leading '::', '#' or '.', but remember it might + # be a method name qualifier + separator = tokens.shift if tokens[0] =~ /^(\.|::|#)/ + + # Skip leading '::', but remember we potentially have an inst + + # leading stuff must be class names + + while tokens[0] =~ /^[A-Z]/ + @class_names << tokens.shift + unless tokens.empty? + separator = tokens.shift + break unless separator == "::" + end + end + + # Now must have a single token, the method name, or an empty array + unless tokens.empty? + @method_name = tokens.shift + # We may now have a trailing !, ?, or = to roll into + # the method name + if !tokens.empty? && tokens[0] =~ /^[!?=]$/ + @method_name << tokens.shift + end + + if @method_name =~ /::|\.|#/ or !tokens.empty? + raise RDoc::RI::Error.new("Bad argument: #{arg}") + end + if separator && separator != '.' + @is_class_method = separator == "::" + end + end + end + + # Return the full class name (with '::' between the components) or "" if + # there's no class name + + def full_class_name + @class_names.join("::") + end + +end + diff --git a/vendor/gems/rdoc-2.4.3/lib/rdoc/ri/writer.rb b/vendor/gems/rdoc-2.4.3/lib/rdoc/ri/writer.rb new file mode 100644 index 000000000..92aaa1c2d --- /dev/null +++ b/vendor/gems/rdoc-2.4.3/lib/rdoc/ri/writer.rb @@ -0,0 +1,68 @@ +require 'fileutils' +require 'rdoc/ri' + +class RDoc::RI::Writer + + def self.class_desc_path(dir, class_desc) + File.join(dir, "cdesc-" + class_desc.name + ".yaml") + end + + ## + # Convert a name from internal form (containing punctuation) to an external + # form (where punctuation is replaced by %xx) + + def self.internal_to_external(name) + if ''.respond_to? :ord then + name.gsub(/\W/) { "%%%02x" % $&[0].ord } + else + name.gsub(/\W/) { "%%%02x" % $&[0] } + end + end + + ## + # And the reverse operation + + def self.external_to_internal(name) + name.gsub(/%([0-9a-f]{2,2})/) { $1.to_i(16).chr } + end + + def initialize(base_dir) + @base_dir = base_dir + end + + def remove_class(class_desc) + FileUtils.rm_rf(path_to_dir(class_desc.full_name)) + end + + def add_class(class_desc) + dir = path_to_dir(class_desc.full_name) + FileUtils.mkdir_p(dir) + class_file_name = self.class.class_desc_path(dir, class_desc) + File.open(class_file_name, "w") do |f| + f.write(class_desc.serialize) + end + end + + def add_method(class_desc, method_desc) + dir = path_to_dir(class_desc.full_name) + file_name = self.class.internal_to_external(method_desc.name) + meth_file_name = File.join(dir, file_name) + if method_desc.is_singleton + meth_file_name += "-c.yaml" + else + meth_file_name += "-i.yaml" + end + + File.open(meth_file_name, "w") do |f| + f.write(method_desc.serialize) + end + end + + private + + def path_to_dir(class_name) + File.join(@base_dir, *class_name.split('::')) + end + +end + |