ruby-changes:3457
From: ko1@a...
Date: 8 Jan 2008 18:07:53 +0900
Subject: [ruby-changes:3457] drbrain - Ruby:r14950 (trunk): Replace ri with Ryan Davis' cached ri
drbrain 2008-01-08 18:07:31 +0900 (Tue, 08 Jan 2008) New Revision: 14950 Added files: trunk/lib/rdoc/ri.rb trunk/lib/rdoc.rb Removed files: trunk/lib/rdoc/ri/ri_options.rb Modified files: trunk/ChangeLog trunk/bin/ri trunk/lib/rdoc/rdoc.rb trunk/lib/rdoc/ri/ri_display.rb trunk/lib/rdoc/ri/ri_driver.rb trunk/lib/rdoc/ri/ri_formatter.rb trunk/lib/rdoc/ri/ri_paths.rb trunk/lib/rdoc/template.rb Log: Replace ri with Ryan Davis' cached ri http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rdoc/ri/ri_options.rb http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rdoc/ri/ri_driver.rb?r1=14950&r2=14949&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/ChangeLog?r1=14950&r2=14949&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rdoc/template.rb?r1=14950&r2=14949&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rdoc/ri/ri_paths.rb?r1=14950&r2=14949&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/bin/ri?r1=14950&r2=14949&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rdoc/ri.rb?revision=14950&view=markup http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rdoc.rb?revision=14950&view=markup http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rdoc/ri/ri_formatter.rb?r1=14950&r2=14949&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rdoc/ri/ri_display.rb?r1=14950&r2=14949&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/rdoc/rdoc.rb?r1=14950&r2=14949&diff_format=u Index: ChangeLog =================================================================== --- ChangeLog (revision 14949) +++ ChangeLog (revision 14950) @@ -1,3 +1,7 @@ +Tue Jan 8 18:05:35 2008 Eric Hodel <drbrain@s...> + + * bin/ri, lib/rdoc/ri/*: Replace with Ryan Davis' cached ri. + Tue Jan 8 15:47:43 2008 NAKAMURA Usaku <usa@r...> * enc/utf{16,32}_{be,le}.c: use &OnigEncodingName(*) instead of Index: lib/rdoc.rb =================================================================== --- lib/rdoc.rb (revision 0) +++ lib/rdoc.rb (revision 14950) @@ -0,0 +1,18 @@ +## +# :include: rdoc/README + +module RDoc + + ## + # RDoc version you are using + + VERSION = "2.0.0" + + ## + # Name of the dotfile that contains the description of files to be processed + # in the current directory + + DOT_DOC_FILENAME = ".document" + +end + Index: lib/rdoc/rdoc.rb =================================================================== --- lib/rdoc/rdoc.rb (revision 14949) +++ lib/rdoc/rdoc.rb (revision 14950) @@ -1,3 +1,5 @@ +require 'rdoc' + require 'rdoc/parsers/parse_rb.rb' require 'rdoc/parsers/parse_c.rb' require 'rdoc/parsers/parse_f95.rb' @@ -11,23 +13,9 @@ require 'fileutils' require 'time' -## -# :include: README - module RDoc ## - # RDoc version you are using - - VERSION = "2.0.0" - - ## - # Name of the dotfile that contains the description of files to be processed - # in the current directory - - DOT_DOC_FILENAME = ".document" - - ## # Simple stats collector class Stats Index: lib/rdoc/ri/ri_options.rb =================================================================== --- lib/rdoc/ri/ri_options.rb (revision 14949) +++ lib/rdoc/ri/ri_options.rb (revision 14950) @@ -1,319 +0,0 @@ -# We handle the parsing of options, and subsequently as a singleton -# object to be queried for option values - -module RI - - require 'rdoc/ri/ri_paths' - require 'rdoc/ri/ri_display' - - VERSION_STRING = "ri v1.0.1 - 20041108" - - class Options - - require 'singleton' - require 'getoptlong' - - include Singleton - - # No not use a pager. Writable, because ri sets it if it - # can't find a pager - attr_accessor :use_stdout - - # should we just display a class list and exit - attr_reader :list_classes - - # should we display a list of all names - attr_reader :list_names - - # The width of the output line - attr_reader :width - - # the formatting we apply to the output - attr_reader :formatter - - # the directory we search for original documentation - attr_reader :doc_dir - - module OptionList - - OPTION_LIST = [ - [ "--help", "-h", nil, - "you're looking at it" ], - - [ "--classes", "-c", nil, - "Display the names of classes and modules we\n" + - "know about"], - - [ "--doc-dir", "-d", "<dirname>", - "A directory to search for documentation. If not\n" + - "specified, we search the standard rdoc/ri directories.\n" + - "May be repeated."], - - [ "--system", nil, nil, - "Include documentation from Ruby's standard library:\n " + - RI::Paths::SYSDIR ], - - [ "--site", nil, nil, - "Include documentation from libraries installed in site_lib:\n " + - RI::Paths::SITEDIR ], - - [ "--home", nil, nil, - "Include documentation stored in ~/.rdoc:\n " + - (RI::Paths::HOMEDIR || "No ~/.rdoc found") ], - - [ "--gems", nil, nil, - "Include documentation from RubyGems:\n" + - (RI::Paths::GEMDIRS ? - Gem.path.map { |dir| " #{dir}/doc/*/ri" }.join("\n") : - "No Rubygems ri found.") ], - - [ "--format", "-f", "<name>", - "Format to use when displaying output:\n" + - " " + RI::TextFormatter.list + "\n" + - "Use 'bs' (backspace) with most pager programs.\n" + - "To use ANSI, either also use the -T option, or\n" + - "tell your pager to allow control characters\n" + - "(for example using the -R option to less)"], - - [ "--list-names", "-l", nil, - "List all the names known to RDoc, one per line" - ], - - [ "--no-pager", "-T", nil, - "Send output directly to stdout." - ], - - [ "--width", "-w", "output width", - "Set the width of the output" ], - - [ "--version", "-v", nil, - "Display the version of ri" - ], - - ] - - def OptionList.options - OPTION_LIST.map do |long, short, arg,| - option = [] - option << long - option << short unless short.nil? - option << (arg ? GetoptLong::REQUIRED_ARGUMENT : - GetoptLong::NO_ARGUMENT) - option - end - end - - - def OptionList.strip_output(text) - text =~ /^\s+/ - leading_spaces = $& - text.gsub!(/^#{leading_spaces}/, '') - $stdout.puts text - end - - - # Show an error and exit - - def OptionList.error(msg) - $stderr.puts - $stderr.puts msg - name = File.basename $PROGRAM_NAME - $stderr.puts "\nFor help on options, try '#{name} --help'\n\n" - exit 1 - end - - # Show usage and exit - - def OptionList.usage(short_form=false) - - puts - puts(RI::VERSION_STRING) - puts - - name = File.basename($0) - - directories = [ - RI::Paths::SYSDIR, - RI::Paths::SITEDIR, - RI::Paths::HOMEDIR - ] - - if RI::Paths::GEMDIRS then - Gem.path.each do |dir| - directories << "#{dir}/doc/*/ri" - end - end - - directories = directories.join("\n ") - - OptionList.strip_output(<<-EOT) - Usage: - - #{name} [options] [names...] - - Display information on Ruby classes, modules, and methods. - Give the names of classes or methods to see their documentation. - Partial names may be given: if the names match more than - one entity, a list will be shown, otherwise details on - that entity will be displayed. - - Nested classes and modules can be specified using the normal - Name::Name notation, and instance methods can be distinguished - from class methods using "." (or "#") instead of "::". - - For example: - - #{name} File - #{name} File.new - #{name} F.n - #{name} zip - - Note that shell quoting may be required for method names - containing punctuation: - - #{name} 'Array.[]' - #{name} compact\\! - - By default ri searches for documentation in the following - directories: - - #{directories} - - Specifying the --system, --site, --home, --gems or --doc-dir - options will limit ri to searching only the specified - directories. - - EOT - - if short_form - puts "For help on options, type '#{name} -h'" - puts "For a list of classes I know about, type '#{name} -c'" - else - puts "Options:\n\n" - OPTION_LIST.each do|long, short, arg, desc| - opt = '' - opt << (short ? sprintf("%15s", "#{long}, #{short}") : - sprintf("%15s", long)) - if arg - opt << " " << arg - end - print opt - desc = desc.split("\n") - if opt.size < 17 - print " "*(18-opt.size) - puts desc.shift - else - puts - end - desc.each do |line| - puts(" "*18 + line) - end - puts - end - puts "Options may also be passed in the 'RI' environment variable" - exit 0 - end - end - end - - # Show the version and exit - def show_version - puts VERSION_STRING - exit(0) - end - - def initialize - @use_stdout = !STDOUT.tty? - @width = 72 - @formatter = RI::TextFormatter.for("plain") - @list_classes = false - @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 = [] - end - - # Parse command line options. - - def parse(args) - - old_argv = ARGV.dup - - ARGV.replace(args) - - begin - - go = GetoptLong.new(*OptionList.options) - go.quiet = true - - go.each do |opt, arg| - case opt - when "--help" then OptionList.usage - when "--version" then show_version - when "--list-names" then @list_names = true - when "--no-pager" then @use_stdout = true - when "--classes" then @list_classes = true - - when "--system" then @use_system = true - when "--site" then @use_site = true - when "--home" then @use_home = true - when "--gems" then @use_gems = true - - when "--doc-dir" - if File.directory?(arg) - @doc_dirs << arg - else - $stderr.puts "Invalid directory: #{arg}" - exit 1 - end - - when "--format" - @formatter = RI::TextFormatter.for(arg) - unless @formatter - $stderr.print "Invalid formatter (should be one of " - $stderr.puts RI::TextFormatter.list + ")" - exit 1 - end - when "--width" - begin - @width = Integer(arg) - rescue - $stderr.puts "Invalid width: '#{arg}'" - exit 1 - end - end - end - - rescue GetoptLong::InvalidOption, GetoptLong::MissingArgument => error - OptionList.error(error.message) - - end - end - - # Return the selected documentation directories. - - def path - RI::Paths.path(@use_system, @use_site, @use_home, @use_gems, *@doc_dirs) - end - - def raw_path - RI::Paths.raw_path(@use_system, @use_site, @use_home, @use_gems, - *@doc_dirs) - end - - # Return an instance of the displayer (the thing that actually writes - # the information). This allows us to load in new displayer classes - # at runtime (for example to help with IDE integration) - - def displayer - ::RiDisplay.new(self) - end - end - -end - Index: lib/rdoc/ri/ri_formatter.rb =================================================================== --- lib/rdoc/ri/ri_formatter.rb (revision 14949) +++ lib/rdoc/ri/ri_formatter.rb (revision 14950) @@ -2,29 +2,28 @@ class TextFormatter attr_reader :indent - - def initialize(options, indent) - @options = options - @width = options.width + + def initialize(width, indent) + @width = width @indent = indent end - - + + ###################################################################### - + def draw_line(label=nil) len = @width len -= (label.size+1) if label print "-"*len if label print(" ") - bold_print(label) + bold_print(label) end puts end - + ###################################################################### - + def wrap(txt, prefix=@indent, linelen=@width) return unless txt && !txt.empty? work = conv_markup(txt) @@ -51,7 +50,7 @@ def blankline puts end - + ###################################################################### # called when we want to ensure a nbew 'wrap' starts on a newline @@ -60,7 +59,7 @@ def break_to_newline end - + ###################################################################### def bold_print(txt) @@ -82,7 +81,7 @@ gsub(/</, '<'). gsub(/"/, '"'). gsub(/&/, '&') - + end # convert markup into display form @@ -99,7 +98,7 @@ def display_list(list) case list.type - when SM::ListBase::BULLET + when SM::ListBase::BULLET prefixer = proc { |ignored| @indent + "* " } when SM::ListBase::NUMBER, @@ -116,7 +115,7 @@ start = start.succ res end - + when SM::ListBase::LABELED prefixer = proc do |li| li.label @@ -156,7 +155,7 @@ when SM::Flow::P, SM::Flow::LI wrap(conv_html(item.body), prefix) blankline - + when SM::Flow::LIST display_list(item) @@ -194,7 +193,7 @@ puts text.upcase puts ul # puts - + when 2 ul = "-" * text.length puts @@ -215,7 +214,7 @@ def strip_attributes(txt) tokens = txt.split(%r{(</?(?:b|code|em|i|tt)>)}) - text = [] + text = [] attributes = 0 tokens.each do |tok| case tok @@ -230,16 +229,16 @@ 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 AttributeFormatter < TextFormatter - + BOLD = 1 ITALIC = 2 CODE = 4 @@ -263,7 +262,7 @@ end end - + class AttributeString attr_reader :txt @@ -363,7 +362,7 @@ ################################################## - + # This formatter generates overstrike-style formatting, which # works with pagers such as man and less. @@ -395,7 +394,7 @@ end ################################################## - + # This formatter uses ANSI escape sequences # to colorize stuff # works with pages such as man and less. @@ -441,7 +440,7 @@ print strip_attributes(text) puts heading[1] end - + private ATTR_MAP = { @@ -462,7 +461,7 @@ end ################################################## - + # This formatter uses HTML. class HtmlFormatter < AttributeFormatter @@ -508,13 +507,13 @@ tag("h#{level}") { text } puts end - + ###################################################################### def display_list(list) case list.type - when SM::ListBase::BULLET + when SM::ListBase::BULLET list_type = "ul" prefixer = proc { |ignored| "<li>" } @@ -523,7 +522,7 @@ SM::ListBase::LOWERALPHA list_type = "ol" prefixer = proc { |ignored| "<li>" } - + when SM::ListBase::LABELED list_type = "dl" prefixer = proc do |li| @@ -554,7 +553,9 @@ def display_verbatim_flow_item(item, prefix=@indent) print("<pre>") - puts item.body + item.body.split(/\n/).each do |line| + puts conv_html(line) + end puts("</pre>") end @@ -602,7 +603,7 @@ end ################################################## - + # This formatter reduces extra lines for a simpler output. # It improves way output looks for tools like IRC bots. @@ -621,7 +622,7 @@ def draw_line(label=nil) unless label.nil? then - bold_print(label) + bold_print(label) puts end end @@ -656,7 +657,7 @@ "plain" => TextFormatter, "simple" => SimpleFormatter, } - + def TextFormatter.list FORMATTERS.keys.sort.join(", ") end Index: lib/rdoc/ri/ri_display.rb =================================================================== --- lib/rdoc/ri/ri_display.rb (revision 14949) +++ lib/rdoc/ri/ri_display.rb (revision 14950) @@ -1,13 +1,8 @@ -require 'rdoc/ri/ri_util' -require 'rdoc/ri/ri_formatter' -require 'rdoc/ri/ri_options' - - # This is a kind of 'flag' module. If you want to write your # own 'ri' display module (perhaps because you'r writing # an IDE or somesuch beast), you simply write a class # which implements the various 'display' methods in 'DefaultDisplay', -# and include the 'RiDisplay' module in that class. +# and include the 'RiDisplay' module in that class. # # To access your class from the command line, you can do # @@ -34,18 +29,17 @@ # actual presentation # -class DefaultDisplay +class DefaultDisplay include RiDisplay - def initialize(options) - @options = options - @formatter = @options.formatter.new(@options, " ") - end - - + def initialize(formatter, width, use_stdout) + @use_stdout = use_stdout + @formatter = formatter.new width, " " + end + ###################################################################### - + def display_usage page do RI::Options::OptionList.usage(short_form=true) @@ -54,7 +48,7 @@ ###################################################################### - + def display_method_info(method) page do @formatter.draw_line(method.full_name) @@ -64,31 +58,31 @@ if method.aliases && !method.aliases.empty? @formatter.blankline aka = "(also known as " - aka << method.aliases.map {|a| a.name }.join(", ") + aka << method.aliases.map {|a| a.name }.join(", ") aka << ")" @formatter.wrap(aka) end end end - + ###################################################################### - + def display_class_info(klass, ri_reader) - page do + page do superclass = klass.superclass_string - + if superclass superclass = " < " + superclass else superclass = "" end - + @formatter.draw_line(klass.display_name + ": " + klass.full_name + superclass) - + display_flow(klass.comment) - @formatter.draw_line - + @formatter.draw_line + unless klass.includes.empty? @formatter.blankline @formatter.display_heading("Includes:", 2, "") @@ -106,7 +100,7 @@ end @formatter.wrap(incs.sort.join(', ')) end - + unless klass.constants.empty? @formatter.blankline @formatter.display_heading("Constants:", 2, "") @@ -114,23 +108,35 @@ klass.constants.each { |c| len = c.name.length if c.name.length > len } len += 2 klass.constants.each do |c| - @formatter.wrap(c.value, + @formatter.wrap(c.value, @formatter.indent+((c.name+":").ljust(len))) - end + end end - + unless klass.class_methods.empty? @formatter.blankline @formatter.display_heading("Class methods:", 2, "") @formatter.wrap(klass.class_methods.map{|m| m.name}.sort.join(', ')) end - + + unless klass.class_method_extensions.empty? + @formatter.blankline + @formatter.display_heading("Class Method Extensions:", 2, "") + @formatter.wrap(klass.class_method_extensions.map{|m| m.name}.sort.join(', ')) + end + unless klass.instance_methods.empty? @formatter.blankline @formatter.display_heading("Instance methods:", 2, "") @formatter.wrap(klass.instance_methods.map{|m| m.name}.sort.join(', ')) end - + + unless klass.instance_method_extensions.empty? + @formatter.blankline + @formatter.display_heading("Instance Method Extensions:", 2, "") + @formatter.wrap(klass.instance_method_extensions.map{|m| m.name}.sort.join(', ')) + end + unless klass.attributes.empty? @formatter.blankline @formatter.wrap("Attributes:", "") @@ -138,11 +144,11 @@ end end end - + ###################################################################### - + # Display a list of method names - + def display_method_list(methods) page do puts "More than one method matched your request. You can refine" @@ -150,9 +156,9 @@ @formatter.wrap(methods.map {|m| m.full_name} .join(", ")) end end - + ###################################################################### - + def display_class_list(namespaces) page do puts "More than one class or module matched your request. You can refine" @@ -160,14 +166,14 @@ @formatter.wrap(namespaces.map {|m| m.full_name}.join(", ")) end end - + ###################################################################### def list_known_classes(classes) if classes.empty? warn_no_database else - page do + page do @formatter.draw_line("Known classes and modules") @formatter.blankline @formatter.wrap(classes.sort.join(", ")) @@ -181,7 +187,7 @@ if names.empty? warn_no_database else - page do + page do names.each {|n| @formatter.raw_print_line(n)} end end @@ -194,34 +200,36 @@ ###################################################################### def page - return yield unless pager = setup_pager - begin - save_stdout = STDOUT.clone - STDOUT.reopen(pager) + if pager = setup_pager then + begin + orig_stdout = $stdout + $stdout = pager + yield + ensure + $stdout = orig_stdout + pager.close + end + else yield - ensure - STDOUT.reopen(save_stdout) - save_stdout.close - pager.close end + rescue Errno::EPIPE end ###################################################################### def setup_pager - unless @options.use_stdout + unless @use_stdout then for pager in [ ENV['PAGER'], "less", "more", 'pager' ].compact.uniq return IO.popen(pager, "w") rescue nil end - @options.use_stdout = true + @use_stdout = true nil end end ###################################################################### - + def display_params(method) - params = method.params if params[0,1] == "(" @@ -232,13 +240,16 @@ end end params.split(/\n/).each do |p| - @formatter.wrap(p) + @formatter.wrap(p) @formatter.break_to_newline end + if method.source_path then + @formatter.blankline + @formatter.wrap("Extension from #{method.source_path}") + end end + ###################################################################### - ###################################################################### - def display_flow(flow) if !flow || flow.empty? @formatter.wrap("(no description...)") @@ -248,9 +259,17 @@ end ###################################################################### - + def warn_no_database - puts "Before using ri, you need to generate documentation" - puts "using 'rdoc' with the --ri option" + puts "No ri data found" + puts + puts "If you've installed Ruby yourself, you need to generate documentation using:" + puts + puts " make install-doc" + puts + puts "from the same place you ran `make` to build ruby." + puts + puts "If you installed Ruby from a packaging system, then you may need to" + puts "install an additional package, or ask the packager to enable ri generation." end end # class RiDisplay Index: lib/rdoc/ri/ri_driver.rb =================================================================== --- lib/rdoc/ri/ri_driver.rb (revision 14949) +++ lib/rdoc/ri/ri_driver.rb (revision 14950) @@ -1,143 +1,424 @@ +require 'optparse' +require 'yaml' + +require 'rdoc/ri' require 'rdoc/ri/ri_paths' -require 'rdoc/usage' -require 'rdoc/ri/ri_cache' -require 'rdoc/ri/ri_util' -require 'rdoc/ri/ri_reader' require 'rdoc/ri/ri_formatter' -require 'rdoc/ri/ri_options' +require 'rdoc/ri/ri_display' +require 'fileutils' +require 'rdoc/markup/simple_markup' +require 'rdoc/markup/simple_markup/to_flow' +class RDoc::RI::RiDriver + + def self.process_args(argv) + options = {} + options[:use_stdout] = !$stdout.tty? + options[:width] = 72 + options[:formatter] = RI::TextFormatter.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 = [] -class RiDriver + opts = OptionParser.new do |opt| + opt.program_name = File.basename $0 + opt.version = RDoc::VERSION + opt.summary_indent = ' ' * 4 - def initialize - @options = RI::Options.instance + directories = [ + RI::Paths::SYSDIR, + RI::Paths::SITEDIR, + RI::Paths::HOMEDIR + ] - args = ARGV - if ENV["RI"] - args = ENV["RI"].split.concat(ARGV) + if 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", + RI::TextFormatter.list.split(', '), # HACK + "Format to use when displaying output:", + " #{RI::TextFormatter.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] = RI::TextFormatter.for value + end + + opt.separator nil + + if RI::Paths::GEMDIRS 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 - @options.parse(args) + argv = ENV['RI'].to_s.split.concat argv - path = @options.path - report_missing_documentation @options.raw_path if path.empty? + opts.parse! argv - @ri_reader = RI::RiReader.new(RI::RiCache.new(path)) - @display = @options.displayer + options[:names] = argv + + options[:path] = RI::Paths.path(use_system, use_site, use_home, use_gems, + *doc_dirs) + options[:raw_path] = 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 - - # Couldn't find documentation in +path+, so tell the user what to do - def report_missing_documentation(path) - STDERR.puts "No ri documentation found in:" - path.each do |d| - STDERR.puts " #{d}" - end - STDERR.puts "\nWas rdoc run to create documentation?\n\n" - RDoc::usage("Installing Documentation") + def self.run(argv = ARGV) + options = process_args argv + ri = new options + ri.run end - - ###################################################################### - - # If the list of matching methods contains exactly one entry, or - # if it contains an entry that exactly matches the requested method, - # then display that entry, otherwise display the list of - # matching method names - - def report_method_stuff(requested_method_name, methods) - if methods.size == 1 - method = @ri_reader.get_method(methods[0]) - @display.display_method_info(method) - else - entries = methods.find_all {|m| m.name == requested_method_name} - if entries.size == 1 - method = @ri_reader.get_method(entries[0]) - @display.display_method_info(method) - else - @display.display_method_list(methods) - end - end + + def initialize(options) + @names = options[:names] + + @class_cache_name = 'classes' + @all_dirs = RI::Paths.path(true, true, true, true) + @homepath = RI::Paths.raw_path(false, false, true, false).first + @homepath = @homepath.sub(/\.rdoc/, '.ri') + @sys_dirs = RI::Paths.raw_path(true, false, false, false) + + FileUtils.mkdir_p cache_file_path unless File.directory? cache_file_path + + @class_cache = nil + + @display = DefaultDisplay.new(options[:formatter], options[:width], + options[:use_stdout]) end - - ###################################################################### - - def report_class_stuff(namespaces) - if namespaces.size == 1 - klass = @ri_reader.get_class(namespaces[0]) - @display.display_class_info(klass, @ri_reader) - else -# entries = namespaces.find_all {|m| m.full_name == requested_class_name} -# if entries.size == 1 -# klass = @ri_reader.get_class(entries[0]) -# @display.display_class_info(klass, @ri_reader) -# else - @display.display_class_list(namespaces) -# end - 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 < File.mtime(class_cache_file_path)) + + @class_cache = if up_to_date then + load_cache_for @class_cache_name + else + class_cache = {} + + 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 end - - ###################################################################### - - - def get_info_for(arg) - desc = NameDescriptor.new(arg) - namespaces = @ri_reader.top_level_namespace - - for class_name in desc.class_names - namespaces = @ri_reader.lookup_namespace_in(class_name, namespaces) - if namespaces.empty? - raise RiError.new("Nothing known about #{arg}") + 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 + end + + def cache_file_path + File.join @homepath, 'cache' + end + + def display_class(name) + klass = class_cache[name] + @display.display_class_info klass, class_cache + end + + def load_cache_for(klassname) + path = cache_file_for klassname + + if File.exist? path and + File.mtime(path) >= File.mtime(class_cache_file_path) then + File.open path, 'rb' do |fp| + Marshal.load fp end + else + class_cache = nil + + File.open class_cache_file_path, 'rb' do |fp| + class_cache = Marshal.load fp + end + + klass = class_cache[klassname] + return nil unless klass + + method_files = klass["sources"] + cache = {} + + 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] = method + end + end + + write_cache cache, path end + end - # at this point, if we have multiple possible namespaces, but one - # is an exact match for our requested class, prune down to just it + 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 - full_class_name = desc.full_class_name - entries = namespaces.find_all {|m| m.full_name == full_class_name} - namespaces = entries if entries.size == 1 + dirs.map { |dir| yield File.join(dir, file_name) }.flatten.compact + end - if desc.method_name.nil? - report_class_stuff(namespaces) - else - methods = @ri_reader.find_methods(desc.method_name, - desc.is_class_method, - namespaces) + def populate_class_cache(class_cache, classes, extension = false) + classes.each do |cdesc| + desc = read_yaml cdesc + klassname = desc["full_name"] - if methods.empty? - raise RiError.new("Nothing known about #{arg}") + 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 - report_method_stuff(desc.method_name, methods) + 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 read_yaml(path) + YAML.load File.read(path).gsub(/ \!ruby\/(object|struct):RI.*/, '') + end - def process_args - if @options.list_classes - classes = @ri_reader.full_class_names - @display.list_known_classes(classes) - elsif @options.list_names - names = @ri_reader.all_names - @display.list_known_names(names) + def run + if @names.empty? then + @display.list_known_classes select_classes else - if ARGV.size.zero? - @display.display_usage - else - begin - ARGV.each do |arg| - get_info_for(arg) + @names.each do |name| + case name + when /::|\#|\./ then + if class_cache.key? name then + display_class name + else + klass, meth = name.split(/::|\#|\./) + cache = load_cache_for klass + # HACK Does not support F.n + abort "Nothing known about #{name}" unless cache + method = cache[name.gsub(/\./, '#')] + abort "Nothing known about #{name}" unless method + @display.display_method_info method end - rescue RiError => e - STDERR.puts(e.message) - exit(1) + else + if class_cache.key? name then + display_class name + else + @display.list_known_classes select_classes(/^#{name}/) + end end end end end + + def select_classes(pattern = nil) + classes = class_cache.keys.sort + classes = classes.grep pattern if pattern + classes + end -end # class RiDriver + def write_cache(cache, path) + File.open path, "wb" do |cache_file| + Marshal.dump cache, cache_file + end + + cache + end + + # Couldn't find documentation in +path+, so tell the user what to do + + def report_missing_documentation(path) + STDERR.puts "No ri documentation found in:" + path.each do |d| + STDERR.puts " #{d}" + end + STDERR.puts "\nWas rdoc run to create documentation?\n\n" + RDoc::usage("Installing Documentation") + end + +end + +class Hash + 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 + self[k] += v + when Hash then + self[k].merge! v + else + # do nothing + end + else + self[k] = v + end + end + end +end + Index: lib/rdoc/ri/ri_paths.rb =================================================================== --- lib/rdoc/ri/ri_paths.rb (revision 14949) +++ lib/rdoc/ri/ri_paths.rb (revision 14950) @@ -23,7 +23,7 @@ #:stopdoc: require 'rbconfig' - + DOC_DIR = "doc/rdoc" version = RbConfig::CONFIG['ruby_version'] @@ -42,7 +42,7 @@ # This is the search path for 'ri' PATH = [ SYSDIR, SITEDIR, HOMEDIR ].find_all {|p| p && File.directory?(p)} - require 'rubygems' + require 'rubygems' unless defined?(Gem) and Gem::Enable # HACK dup'd from Gem.latest_partials and friends all_paths = [] Index: lib/rdoc/ri.rb =================================================================== --- lib/rdoc/ri.rb (revision 0) +++ lib/rdoc/ri.rb (revision 14950) @@ -0,0 +1,4 @@ +require 'rdoc' + +module RDoc::RI; end + Index: lib/rdoc/template.rb =================================================================== --- lib/rdoc/template.rb (revision 14949) +++ lib/rdoc/template.rb (revision 14950) @@ -48,10 +48,9 @@ # Process the template using +values+, writing the result to +io+. def write_html_on(io, values) + b = binding template_include = "" - b = binding - @templates.reverse_each do |template| template_include = ERB.new(template).result b end Index: bin/ri =================================================================== --- bin/ri (revision 14949) +++ bin/ri (revision 14950) @@ -1,49 +1,6 @@ -#!/usr/bin/env ruby -# usage: -# -# ri name... -# -# where name can be -# -# Class | Class::method | Class#method | Class.method | method -# -# All names may be abbreviated to their minimum 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. -# -# -# == Installing Documentation -# -# 'ri' uses a database of documentation built by the RDoc utility. -# -# So, how do you install this documentation on your system? -# It depends on how you installed Ruby. -# -# <em>If you installed Ruby from source files</em> (that is, if it some point -# you typed 'make' during the process :), you can install the RDoc -# documentation yourself. Just go back to the place where you have -# your Ruby source and type -# -# make install-doc -# -# You'll probably need to do this as a superuser, as the documentation -# is installed in the Ruby target tree (normally somewhere under -# <tt>/usr/local</tt>. -# -# <em>If you installed Ruby from a binary distribution</em> (perhaps -# using a one-click installer, or using some other packaging system), -# then the team that produced the package probably forgot to package -# the documentation as well. Contact them, and see if they can add -# it to the next release. -# +#!/usr//bin/env ruby - require 'rdoc/ri/ri_driver' -###################################################################### +RDoc::RI::RiDriver.run ARGV -ri = RiDriver.new -ri.process_args - -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml