From ec519b9b43db7756fcc2c19020ece2d7e2263fe4 Mon Sep 17 00:00:00 2001 From: drbrain Date: Sun, 13 Jan 2008 03:13:37 +0000 Subject: Complete RDoc namespace change git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@15018 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- lib/rdoc/generator/ri.rb | 228 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 lib/rdoc/generator/ri.rb (limited to 'lib/rdoc/generator/ri.rb') diff --git a/lib/rdoc/generator/ri.rb b/lib/rdoc/generator/ri.rb new file mode 100644 index 0000000000..bb0c4f4021 --- /dev/null +++ b/lib/rdoc/generator/ri.rb @@ -0,0 +1,228 @@ +require 'rdoc/generator' +require 'rdoc/markup/simple_markup/to_flow' + +require 'rdoc/ri/cache' +require 'rdoc/ri/reader' +require 'rdoc/ri/writer' +require 'rdoc/ri/descriptions' + +class RDoc::Generator::RI + + ## + # Generator may need to return specific subclasses depending on the + # options they are passed. Because of this we create them using a factory + + def self.for(options) + new(options) + end + + class << self + protected :new + end + + ## + # Set up a new RDoc::Generator::RI. + + def initialize(options) #:not-new: + @options = options + @ri_writer = RDoc::RI::Writer.new "." + @markup = SM::SimpleMarkup.new + @to_flow = SM::ToFlow.new + + @generated = {} + end + + ## + # Build the initial indices and output objects based on an array of + # TopLevel objects containing the extracted information. + + def generate(toplevels) + RDoc::TopLevel.all_classes_and_modules.each do |cls| + process_class(cls) + end + end + + def process_class(from_class) + generate_class_info(from_class) + + # now recure into this classes constituent classess + from_class.each_classmodule do |mod| + process_class(mod) + end + end + + def generate_class_info(cls) + if cls === RDoc::NormalModule + cls_desc = RDoc::RI::ModuleDescription.new + else + cls_desc = RDoc::RI::ClassDescription.new + cls_desc.superclass = cls.superclass + end + cls_desc.name = cls.name + cls_desc.full_name = cls.full_name + cls_desc.comment = markup(cls.comment) + + cls_desc.attributes = cls.attributes.sort.map do |a| + RDoc::RI::Attribute.new(a.name, a.rw, markup(a.comment)) + end + + cls_desc.constants = cls.constants.map do |c| + RDoc::RI::Constant.new(c.name, c.value, markup(c.comment)) + end + + cls_desc.includes = cls.includes.map do |i| + RDoc::RI::IncludedModule.new(i.name) + end + + class_methods, instance_methods = method_list(cls) + + cls_desc.class_methods = class_methods.map do |m| + RDoc::RI::MethodSummary.new(m.name) + end + + cls_desc.instance_methods = instance_methods.map do |m| + RDoc::RI::MethodSummary.new(m.name) + end + + update_or_replace(cls_desc) + + class_methods.each do |m| + generate_method_info(cls_desc, m) + end + + instance_methods.each do |m| + generate_method_info(cls_desc, m) + end + end + + def generate_method_info(cls_desc, method) + meth_desc = RDoc::RI::MethodDescription.new + meth_desc.name = method.name + meth_desc.full_name = cls_desc.full_name + if method.singleton + meth_desc.full_name += "::" + else + meth_desc.full_name += "#" + end + meth_desc.full_name << method.name + + meth_desc.comment = markup(method.comment) + meth_desc.params = params_of(method) + meth_desc.visibility = method.visibility.to_s + meth_desc.is_singleton = method.singleton + meth_desc.block_params = method.block_params + + meth_desc.aliases = method.aliases.map do |a| + RDoc::RI::AliasName.new(a.name) + end + + @ri_writer.add_method(cls_desc, meth_desc) + end + + private + + ## + # Returns a list of class and instance methods that we'll be documenting + + def method_list(cls) + list = cls.method_list + unless @options.show_all + list = list.find_all do |m| + m.visibility == :public || m.visibility == :protected || m.force_documentation + end + end + + c = [] + i = [] + list.sort.each do |m| + if m.singleton + c << m + else + i << m + end + end + return c,i + end + + def params_of(method) + if method.call_seq + method.call_seq + else + params = method.params || "" + + p = params.gsub(/\s*\#.*/, '') + p = p.tr("\n", " ").squeeze(" ") + p = "(" + p + ")" unless p[0] == ?( + + if (block = method.block_params) + block.gsub!(/\s*\#.*/, '') + block = block.tr("\n", " ").squeeze(" ") + if block[0] == ?( + block.sub!(/^\(/, '').sub!(/\)/, '') + end + p << " {|#{block.strip}| ...}" + end + p + end + end + + def markup(comment) + return nil if !comment || comment.empty? + + # Convert leading comment markers to spaces, but only + # if all non-blank lines have them + + if comment =~ /^(?>\s*)[^\#]/ + content = comment + else + content = comment.gsub(/^\s*(#+)/) { $1.tr('#',' ') } + end + @markup.convert(content, @to_flow) + end + + ## + # By default we replace existing classes with the same name. If the + # --merge option was given, we instead merge this definition into an + # existing class. We add our methods, aliases, etc to that class, but do + # not change the class's description. + + def update_or_replace(cls_desc) + old_cls = nil + + if @options.merge + rdr = RDoc::RI::Reader.new RDoc::RI::Cache.new(@options.op_dir) + + namespace = rdr.top_level_namespace + namespace = rdr.lookup_namespace_in(cls_desc.name, namespace) + if namespace.empty? + $stderr.puts "You asked me to merge this source into existing " + $stderr.puts "documentation. This file references a class or " + $stderr.puts "module called #{cls_desc.name} which I don't" + $stderr.puts "have existing documentation for." + $stderr.puts + $stderr.puts "Perhaps you need to generate its documentation first" + exit 1 + else + old_cls = namespace[0] + end + end + + prev_cls = @generated[cls_desc.full_name] + + if old_cls and not prev_cls then + old_desc = rdr.get_class old_cls + cls_desc.merge_in old_desc + end + + if prev_cls then + cls_desc.merge_in prev_cls + end + + @generated[cls_desc.full_name] = cls_desc + + @ri_writer.remove_class cls_desc + @ri_writer.add_class cls_desc + end + +end + -- cgit v1.2.3