diff options
author | drbrain <drbrain@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2008-09-25 02:43:03 +0000 |
---|---|---|
committer | drbrain <drbrain@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2008-09-25 02:43:03 +0000 |
commit | 858362e761a41e7d96efbcb9b38ae815b1e388d7 (patch) | |
tree | b47a0968d921320591f9218bc746e11a7922c53f /lib/rdoc/ri | |
parent | 00b4a3f9c4aaf5aa038a9530ec515e1718ae1c42 (diff) | |
download | ruby-858362e761a41e7d96efbcb9b38ae815b1e388d7.tar.gz |
Import RDoc 2.2.1 r185
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@19537 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib/rdoc/ri')
-rw-r--r-- | lib/rdoc/ri/cache.rb | 11 | ||||
-rw-r--r-- | lib/rdoc/ri/descriptions.rb | 3 | ||||
-rw-r--r-- | lib/rdoc/ri/display.rb | 201 | ||||
-rw-r--r-- | lib/rdoc/ri/driver.rb | 522 | ||||
-rw-r--r-- | lib/rdoc/ri/formatter.rb | 2 | ||||
-rw-r--r-- | lib/rdoc/ri/paths.rb | 13 | ||||
-rw-r--r-- | lib/rdoc/ri/reader.rb | 6 | ||||
-rw-r--r-- | lib/rdoc/ri/util.rb | 2 |
8 files changed, 494 insertions, 266 deletions
diff --git a/lib/rdoc/ri/cache.rb b/lib/rdoc/ri/cache.rb index 2e267d95fb..06177a00de 100644 --- a/lib/rdoc/ri/cache.rb +++ b/lib/rdoc/ri/cache.rb @@ -14,7 +14,7 @@ class RDoc::RI::ClassEntry @inferior_classes = [] end - # We found this class in more tha one place, so add + # We found this class in more than one place, so add # in the name from there. def add_path(path) @path_names << path @@ -37,10 +37,10 @@ class RDoc::RI::ClassEntry if name =~ /^(.*?)-(c|i).yaml$/ external_name = $1 is_class_method = $2 == "c" - internal_name = RiWriter.external_to_internal(external_name) + internal_name = RDoc::RI::Writer.external_to_internal(external_name) list = is_class_method ? @class_methods : @instance_methods path = File.join(dir, name) - list << MethodEntry.new(path, internal_name, is_class_method, self) + list << RDoc::RI::MethodEntry.new(path, internal_name, is_class_method, self) else full_name = File.join(dir, name) if File.directory?(full_name) @@ -48,7 +48,7 @@ class RDoc::RI::ClassEntry if inf_class inf_class.add_path(full_name) else - inf_class = ClassEntry.new(full_name, name, self) + inf_class = RDoc::RI::ClassEntry.new(full_name, name, self) @inferior_classes << inf_class end inf_class.load_from(full_name) @@ -168,7 +168,7 @@ class RDoc::RI::MethodEntry end ## -# We represent everything know about all 'ri' files accessible to this program +# We represent everything known about all 'ri' files accessible to this program class RDoc::RI::Cache @@ -185,4 +185,3 @@ class RDoc::RI::Cache end end - diff --git a/lib/rdoc/ri/descriptions.rb b/lib/rdoc/ri/descriptions.rb index 0d8560323a..467b7de2a9 100644 --- a/lib/rdoc/ri/descriptions.rb +++ b/lib/rdoc/ri/descriptions.rb @@ -77,7 +77,9 @@ 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 @@ -148,6 +150,7 @@ class RDoc::RI::MethodDescription < RDoc::RI::Description attr_accessor :aliases attr_accessor :is_alias_for attr_accessor :params + attr_accessor :source_path end diff --git a/lib/rdoc/ri/display.rb b/lib/rdoc/ri/display.rb index 379cef11b3..05a7cf253d 100644 --- a/lib/rdoc/ri/display.rb +++ b/lib/rdoc/ri/display.rb @@ -1,5 +1,15 @@ require 'rdoc/ri' +# readline support might not be present, so be careful +# when requiring it. +begin + require('readline') + require('abbrev') + CAN_USE_READLINE = true +rescue + CAN_USE_READLINE = false +end + ## # 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 @@ -41,7 +51,7 @@ class RDoc::RI::DefaultDisplay # Display information about +klass+. Fetches additional information from # +ri_reader+ as necessary. - def display_class_info(klass, ri_reader) + def display_class_info(klass) page do superclass = klass.superclass_string @@ -61,17 +71,11 @@ class RDoc::RI::DefaultDisplay @formatter.blankline @formatter.display_heading("Includes:", 2, "") incs = [] + klass.includes.each do |inc| - inc_desc = ri_reader.find_class_by_name(inc.name) - if inc_desc - str = inc.name + "(" - str << inc_desc.instance_methods.map{|m| m.name}.join(", ") - str << ")" - incs << str - else - incs << inc.name - end - end + incs << inc.name + end + @formatter.wrap(incs.sort.join(', ')) end @@ -82,42 +86,19 @@ class RDoc::RI::DefaultDisplay constants = klass.constants.sort_by { |constant| constant.name } constants.each do |constant| + @formatter.wrap "#{constant.name} = #{constant.value}" if constant.comment then - @formatter.wrap "#{constant.name}:" - @formatter.indent do @formatter.display_flow constant.comment end else - @formatter.wrap constant.name + @formatter.break_to_newline end end end - 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.empty? then - @formatter.blankline - - heading = data_type.to_s.split('_').join(' ').capitalize << ':' - @formatter.display_heading heading, 2, '' - - data = data.map { |item| item.name }.sort.join ', ' - @formatter.wrap data - end - end - unless klass.attributes.empty? then @formatter.blankline - @formatter.display_heading 'Attributes:', 2, '' attributes = klass.attributes.sort_by { |attribute| attribute.name } @@ -130,11 +111,119 @@ class RDoc::RI::DefaultDisplay 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) + if CAN_USE_READLINE + # prepare abbreviations for tab completion + abbreviations = method_map.keys.abbrev + Readline.completion_proc = proc do |string| + abbreviations.values.uniq.grep(/^#{string}/) + end + end + + @formatter.raw_print_line "\nEnter the method name you want.\n" + @formatter.raw_print_line "Class methods can be preceeded by '::' and instance methods by '#'.\n" + + if CAN_USE_READLINE + @formatter.raw_print_line "You can use tab to autocomplete.\n" + @formatter.raw_print_line "Enter a blank line to exit.\n" + + choice_string = Readline.readline(">> ").strip + else + @formatter.raw_print_line "Enter a blank line to exit.\n" + @formatter.raw_print_line ">> " + choice_string = $stdin.gets.strip + end + + if choice_string == '' + return nil + else + class_or_instance = method_map[choice_string] + + if class_or_instance + # If the user's choice is not preceeded by a '::' or a '#', figure + # out whether they want a class or an instance method and decorate + # the choice appropriately. + if(choice_string =~ /^[a-zA-Z]/) + if(class_or_instance == :class) + choice_string = "::#{choice_string}" + else + choice_string = "##{choice_string}" + end + end + + return choice_string + else + @formatter.raw_print_line "No method matched '#{choice_string}'.\n" + return nil + end + end + end + + + ## + # Display methods on +klass+ + # Returns a hash mapping method name to method contents (HACK?) + + 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+. @@ -172,10 +261,42 @@ class RDoc::RI::DefaultDisplay 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 - @formatter.wrap methods.map { |m| m.full_name }.join(", ") + 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 @@ -198,10 +319,8 @@ class RDoc::RI::DefaultDisplay @formatter.break_to_newline end - if method.source_path then - @formatter.blankline - @formatter.wrap("Extension from #{method.source_path}") - end + @formatter.blankline + @formatter.wrap("From #{method.source_path}") end ## diff --git a/lib/rdoc/ri/driver.rb b/lib/rdoc/ri/driver.rb index dfc5f2f98a..0c91232b70 100644 --- a/lib/rdoc/ri/driver.rb +++ b/lib/rdoc/ri/driver.rb @@ -11,29 +11,33 @@ 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 + # + # 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 - def method_missing method, *args - self[method.to_s] + new_hash + when Array then + object.map do |element| + convert(element) + end + else + object + end end def merge_enums(other) @@ -57,6 +61,10 @@ class RDoc::RI::Driver end end end + + def method_missing method, *args + self[method.to_s] + end end class Error < RDoc::RI::Error; end @@ -69,25 +77,31 @@ class RDoc::RI::Driver attr_accessor :homepath # :nodoc: - def self.process_args(argv) + def self.default_options options = {} options[:use_stdout] = !$stdout.tty? options[:width] = 72 options[:formatter] = RDoc::RI::Formatter.for 'plain' - options[:list_classes] = false - options[:list_names] = false + 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 - # 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 = [] + 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 = [ @@ -142,86 +156,114 @@ Options may also be set in the 'RI' environment variable. 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 + 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 to search for", - "documentation. If not specified, we search", - "the standard rdoc/ri directories. May be", - "repeated.") do |value| + "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 - doc_dirs.concat value + 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("--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 + 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 - unless RDoc::RI::Paths::GEMDIRS.empty? then - opt.on("--[no-]gems", - "Include documentation from RubyGems.") do |value| - use_gems = value - end + 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-]home", - "Include documentation stored in ~/.rdoc.") do |value| - use_home = value + 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-]list-names", "-l", - "List all the names known to RDoc, one per", - "line.") do |value| - options[:list_names] = value + opt.on("--[no-]gems", + "Include documentation from RubyGems.", + "Defaults to true.") do |value| + options[:use_gems] = value end opt.separator nil - opt.on("--no-pager", "-T", - "Send output directly to stdout.") do |value| - options[:use_stdout] = !value + opt.on("--[no-]home", + "Include documentation stored in ~/.rdoc.", + "Defaults to true.") do |value| + options[:use_home] = value end opt.separator nil - opt.on("--[no-]site", - "Include documentation from libraries", - "installed in site_lib.") do |value| - use_site = value + 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-]system", - "Include documentation from Ruby's standard", - "library.") do |value| - use_system = value + 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 @@ -238,10 +280,10 @@ Options may also be set in the 'RI' environment variable. 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[:formatter] ||= RDoc::RI::Formatter.for('plain') + options[:use_stdout] ||= !$stdout.tty? + options[:use_stdout] ||= options[:interactive] + options[:width] ||= 72 options @@ -258,22 +300,30 @@ Options may also be set in the 'RI' environment variable. ri.run end - def initialize(options={}) - options[:formatter] ||= RDoc::RI::Formatter.for('plain') - options[:use_stdout] ||= !$stdout.tty? - options[:width] ||= 72 - @names = options[:names] + def initialize(initial_options={}) + options = self.class.default_options.update(initial_options) + @names = options[:names] @class_cache_name = 'classes' - @all_dirs = RDoc::RI::Paths.path(true, true, true, true) + + @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_dirs = RDoc::RI::Paths.raw_path(true, false, false, false) + @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]) @@ -282,30 +332,92 @@ Options may also be set in the 'RI' environment variable. def class_cache return @class_cache if @class_cache - newest = map_dirs('created.rid', :all) do |f| + # 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)) + 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 - @class_cache = if up_to_date then - load_cache_for @class_cache_name - else - class_cache = RDoc::RI::Driver::Hash.new + def create_class_cache + class_cache = OpenStructHash.new - classes = map_dirs('**/cdesc*.yaml', :sys) { |f| Dir[f] } - populate_class_cache class_cache, classes + 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 class cache with #{classes.size} classes..." + classes = map_dirs('**/cdesc*.yaml') { |f| Dir[f] } + warn "Updating class cache with #{classes.size} classes..." + populate_class_cache class_cache, classes - populate_class_cache class_cache, classes, true - write_cache class_cache, class_cache_file_path - end + write_cache class_cache, class_cache_file_path - @class_cache = RDoc::RI::Driver::Hash.convert @class_cache - @class_cache + class_cache + 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.merge_enums desc + klass["sources"] << cdesc + end + end end def class_cache_file_path @@ -322,8 +434,11 @@ Options may also be set in the 'RI' environment variable. def display_class(name) klass = class_cache[name] - klass = RDoc::RI::Driver::Hash.convert klass - @display.display_class_info klass, class_cache + @display.display_class_info klass + end + + def display_method(method) + @display.display_method_info method end def get_info_for(arg) @@ -337,48 +452,74 @@ Options may also be set in the 'RI' environment variable. cache = nil if File.exist? path and - File.mtime(path) >= File.mtime(class_cache_file_path) then + File.mtime(path) >= File.mtime(class_cache_file_path) and + @use_cache then open path, 'rb' do |fp| - cache = Marshal.load fp.read + 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 - class_cache = nil + cache = create_cache_for klassname, path + end - open class_cache_file_path, 'rb' do |fp| - class_cache = Marshal.load fp.read - end + cache + 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 + 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 + if(f =~ %r%gems/[\d.]+/doc/([^/]+)%) then + ext_path = "gem #{$1}" + else + ext_path = f + end + + method["source_path"] = ext_path end - end - write_cache cache, path + name = method["full_name"] + cache[name] = method + end end - RDoc::RI::Driver::Hash.convert cache + 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"))) + return nil + end + cache = class_cache[orig_klass] return nil unless cache @@ -386,10 +527,13 @@ Options may also be set in the 'RI' environment variable. ancestors = [orig_klass] ancestors.push(*cache.includes.map { |inc| inc['name'] }) ancestors << cache.superclass + + ancestor_index = ancestors.index(klass) - ancestor = ancestors[ancestors.index(klass) + 1] - - return ancestor if ancestor + if ancestor_index + ancestor = ancestors[ancestors.index(klass) + 1] + return ancestor if ancestor + end lookup_ancestor klass, cache.superclass end @@ -406,18 +550,8 @@ Options may also be set in the 'RI' environment variable. 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 + def map_dirs(file_name) + @doc_dirs.map { |dir| yield File.join(dir, file_name) }.flatten.compact end ## @@ -436,83 +570,66 @@ Options may also be set in the 'RI' environment variable. [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 + + # 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') - YAML.load data + OpenStructHash.convert(YAML.load(data)) end def run - if @names.empty? then + if(@list_doc_dirs) + puts @doc_dirs.join("\n") + elsif @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 + if class_cache.key? name then + method_map = display_class name + if(@interactive) + method_name = @display.get_class_method_choice(method_map) + + if(method_name != nil) + method = lookup_method "#{name}#{method_name}", name + display_method method + end + end + elsif name =~ /::|\#|\./ then + klass, = parse_name name - orig_klass = klass - orig_name = name + orig_klass = klass + orig_name = name - until klass == 'Kernel' do - method = lookup_method name, klass + loop do + method = lookup_method name, klass - break method if method + break method if method - ancestor = lookup_ancestor klass, orig_klass + ancestor = lookup_ancestor klass, orig_klass - break unless ancestor + break unless ancestor - name = name.sub klass, ancestor - klass = ancestor - end + name = name.sub klass, ancestor + klass = ancestor + end - raise NotFoundError, orig_name unless method + raise NotFoundError, orig_name unless method - @display.display_method_info method - end + display_method method else - if class_cache.key? name then - display_class name - else - methods = select_methods(/^#{name}/) + methods = select_methods(/#{name}/) - if methods.size == 0 - raise NotFoundError, name - elsif methods.size == 1 - @display.display_method_info methods.first + if methods.size == 0 + raise NotFoundError, name + elsif methods.size == 1 + display_method methods[0] + else + if(@interactive) + @display.display_method_list_choice methods else @display.display_method_list methods end @@ -540,12 +657,13 @@ Options may also be set in the 'RI' environment variable. end def write_cache(cache, path) - File.open path, "wb" do |cache_file| - Marshal.dump cache, cache_file + if(@use_cache) + File.open path, "wb" do |cache_file| + Marshal.dump cache, cache_file + end end cache end end - diff --git a/lib/rdoc/ri/formatter.rb b/lib/rdoc/ri/formatter.rb index 0a0c3f7380..933882abc4 100644 --- a/lib/rdoc/ri/formatter.rb +++ b/lib/rdoc/ri/formatter.rb @@ -93,7 +93,7 @@ class RDoc::RI::Formatter end def raw_print_line(txt) - @output.puts txt + @output.print txt end ## diff --git a/lib/rdoc/ri/paths.rb b/lib/rdoc/ri/paths.rb index b4b6c64925..2f72b9dfd5 100644 --- a/lib/rdoc/ri/paths.rb +++ b/lib/rdoc/ri/paths.rb @@ -26,9 +26,9 @@ module RDoc::RI::Paths DOC_DIR = "doc/rdoc" - version = RbConfig::CONFIG['ruby_version'] + VERSION = RbConfig::CONFIG['ruby_version'] - base = File.join(RbConfig::CONFIG['datadir'], "ri", 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'] @@ -39,9 +39,6 @@ module RDoc::RI::Paths HOMEDIR = nil end - # This is the search path for 'ri' - PATH = [ SYSDIR, SITEDIR, HOMEDIR ].find_all {|p| p && File.directory?(p)} - begin require 'rubygems' unless defined?(Gem) and defined?(Gem::Enable) and Gem::Enable @@ -67,7 +64,6 @@ module RDoc::RI::Paths end GEMDIRS = ri_paths.map { |k,v| v.last }.sort - GEMDIRS.each { |dir| PATH << dir } rescue LoadError GEMDIRS = [] end @@ -85,9 +81,6 @@ module RDoc::RI::Paths # found. def self.raw_path(use_system, use_site, use_home, use_gems, *extra_dirs) - return PATH unless use_system or use_site or use_home or use_gems or - not extra_dirs.empty? - path = [] path << extra_dirs unless extra_dirs.empty? path << SYSDIR if use_system @@ -97,6 +90,4 @@ module RDoc::RI::Paths return path.flatten.compact end - end - diff --git a/lib/rdoc/ri/reader.rb b/lib/rdoc/ri/reader.rb index 986bb75954..de3c8d9afa 100644 --- a/lib/rdoc/ri/reader.rb +++ b/lib/rdoc/ri/reader.rb @@ -45,7 +45,7 @@ class RDoc::RI::Reader def get_method(method_entry) path = method_entry.path_name - File.open(path) { |f| RI::Description.deserialize(f) } + File.open(path) { |f| RDoc::RI::Description.deserialize(f) } end ## @@ -54,8 +54,8 @@ class RDoc::RI::Reader def get_class(class_entry) result = nil for path in class_entry.path_names - path = RiWriter.class_desc_path(path, class_entry) - desc = File.open(path) {|f| RI::Description.deserialize(f) } + 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 diff --git a/lib/rdoc/ri/util.rb b/lib/rdoc/ri/util.rb index 34277f2594..4e91eb978d 100644 --- a/lib/rdoc/ri/util.rb +++ b/lib/rdoc/ri/util.rb @@ -1,7 +1,5 @@ require 'rdoc/ri' -class RDoc::RI::Error < RuntimeError; end - ## # Break argument into its constituent class or module names, an # optional method type, and a method name |