diff options
Diffstat (limited to 'trunk/lib/rdoc/code_objects.rb')
-rw-r--r-- | trunk/lib/rdoc/code_objects.rb | 995 |
1 files changed, 0 insertions, 995 deletions
diff --git a/trunk/lib/rdoc/code_objects.rb b/trunk/lib/rdoc/code_objects.rb deleted file mode 100644 index fbdb612b92..0000000000 --- a/trunk/lib/rdoc/code_objects.rb +++ /dev/null @@ -1,995 +0,0 @@ -# We represent the various high-level code constructs that appear -# in Ruby programs: classes, modules, methods, and so on. - -require 'rdoc/tokenstream' - -module RDoc - - ## - # We contain the common stuff for contexts (which are containers) and other - # elements (methods, attributes and so on) - - class CodeObject - - attr_accessor :parent - - # We are the model of the code, but we know that at some point - # we will be worked on by viewers. By implementing the Viewable - # protocol, viewers can associated themselves with these objects. - - attr_accessor :viewer - - # are we done documenting (ie, did we come across a :enddoc:)? - - attr_accessor :done_documenting - - # Which section are we in - - attr_accessor :section - - # do we document ourselves? - - attr_reader :document_self - - def initialize - @document_self = true - @document_children = true - @force_documentation = false - @done_documenting = false - end - - def document_self=(val) - @document_self = val - if !val - remove_methods_etc - end - end - - # set and cleared by :startdoc: and :enddoc:, this is used to toggle - # the capturing of documentation - def start_doc - @document_self = true - @document_children = true - end - - def stop_doc - @document_self = false - @document_children = false - end - - # do we document ourselves and our children - - attr_reader :document_children - - def document_children=(val) - @document_children = val - if !val - remove_classes_and_modules - end - end - - # Do we _force_ documentation, even is we wouldn't normally show the entity - attr_accessor :force_documentation - - def parent_file_name - @parent ? @parent.file_base_name : '(unknown)' - end - - def parent_name - @parent ? @parent.name : '(unknown)' - end - - # Default callbacks to nothing, but this is overridden for classes - # and modules - def remove_classes_and_modules - end - - def remove_methods_etc - end - - # Access the code object's comment - attr_reader :comment - - # Update the comment, but don't overwrite a real comment with an empty one - def comment=(comment) - @comment = comment unless comment.empty? - end - - # There's a wee trick we pull. Comment blocks can have directives that - # override the stuff we extract during the parse. So, we have a special - # class method, attr_overridable, that lets code objects list - # those directives. Wehn a comment is assigned, we then extract - # out any matching directives and update our object - - def self.attr_overridable(name, *aliases) - @overridables ||= {} - - attr_accessor name - - aliases.unshift name - aliases.each do |directive_name| - @overridables[directive_name.to_s] = name - end - end - - end - - ## - # A Context is something that can hold modules, classes, methods, - # attributes, aliases, requires, and includes. Classes, modules, and files - # are all Contexts. - - class Context < CodeObject - - attr_reader :aliases - attr_reader :attributes - attr_reader :constants - attr_reader :current_section - attr_reader :in_files - attr_reader :includes - attr_reader :method_list - attr_reader :name - attr_reader :requires - attr_reader :sections - attr_reader :visibility - - class Section - attr_reader :title, :comment, :sequence - - @@sequence = "SEC00000" - - def initialize(title, comment) - @title = title - @@sequence.succ! - @sequence = @@sequence.dup - @comment = nil - set_comment(comment) - end - - def ==(other) - self.class === other and @sequence == other.sequence - end - - def inspect - "#<%s:0x%x %s %p>" % [ - self.class, object_id, - @sequence, title - ] - end - - ## - # Set the comment for this section from the original comment block If - # the first line contains :section:, strip it and use the rest. - # Otherwise remove lines up to the line containing :section:, and look - # for those lines again at the end and remove them. This lets us write - # - # # --------------------- - # # :SECTION: The title - # # The body - # # --------------------- - - def set_comment(comment) - return unless comment - - if comment =~ /^#[ \t]*:section:.*\n/ - start = $` - rest = $' - - if start.empty? - @comment = rest - else - @comment = rest.sub(/#{start.chomp}\Z/, '') - end - else - @comment = comment - end - @comment = nil if @comment.empty? - end - - end - - def initialize - super - - @in_files = [] - - @name ||= "unknown" - @comment ||= "" - @parent = nil - @visibility = :public - - @current_section = Section.new(nil, nil) - @sections = [ @current_section ] - - initialize_methods_etc - initialize_classes_and_modules - end - - ## - # map the class hash to an array externally - - def classes - @classes.values - end - - ## - # map the module hash to an array externally - - def modules - @modules.values - end - - ## - # Change the default visibility for new methods - - def ongoing_visibility=(vis) - @visibility = vis - end - - ## - # Yields Method and Attr entries matching the list of names in +methods+. - # Attributes are only returned when +singleton+ is false. - - def methods_matching(methods, singleton = false) - count = 0 - - @method_list.each do |m| - if methods.include? m.name and m.singleton == singleton then - yield m - count += 1 - end - end - - return if count == methods.size || singleton - - # perhaps we need to look at attributes - - @attributes.each do |a| - yield a if methods.include? a.name - end - end - - ## - # Given an array +methods+ of method names, set the visibility of the - # corresponding AnyMethod object - - def set_visibility_for(methods, vis, singleton = false) - methods_matching methods, singleton do |m| - m.visibility = vis - end - end - - ## - # Record the file that we happen to find it in - - def record_location(toplevel) - @in_files << toplevel unless @in_files.include?(toplevel) - end - - # Return true if at least part of this thing was defined in +file+ - def defined_in?(file) - @in_files.include?(file) - end - - def add_class(class_type, name, superclass) - add_class_or_module @classes, class_type, name, superclass - end - - def add_module(class_type, name) - add_class_or_module(@modules, class_type, name, nil) - end - - def add_method(a_method) - a_method.visibility = @visibility - add_to(@method_list, a_method) - end - - def add_attribute(an_attribute) - add_to(@attributes, an_attribute) - end - - def add_alias(an_alias) - meth = find_instance_method_named(an_alias.old_name) - - if meth then - new_meth = AnyMethod.new(an_alias.text, an_alias.new_name) - new_meth.is_alias_for = meth - new_meth.singleton = meth.singleton - new_meth.params = meth.params - new_meth.comment = "Alias for \##{meth.name}" - meth.add_alias(new_meth) - add_method(new_meth) - else - add_to(@aliases, an_alias) - end - - an_alias - end - - def add_include(an_include) - add_to(@includes, an_include) - end - - def add_constant(const) - add_to(@constants, const) - end - - # Requires always get added to the top-level (file) context - def add_require(a_require) - if TopLevel === self then - add_to @requires, a_require - else - parent.add_require a_require - end - end - - def add_class_or_module(collection, class_type, name, superclass=nil) - cls = collection[name] - - if cls then - cls.superclass = superclass unless cls.module? - puts "Reusing class/module #{name}" if $DEBUG_RDOC - else - cls = class_type.new(name, superclass) -# collection[name] = cls if @document_self && !@done_documenting - collection[name] = cls if !@done_documenting - cls.parent = self - cls.section = @current_section - end - cls - end - - def add_to(array, thing) - array << thing if @document_self and not @done_documenting - thing.parent = self - thing.section = @current_section - end - - # If a class's documentation is turned off after we've started - # collecting methods etc., we need to remove the ones - # we have - - def remove_methods_etc - initialize_methods_etc - end - - def initialize_methods_etc - @method_list = [] - @attributes = [] - @aliases = [] - @requires = [] - @includes = [] - @constants = [] - end - - # and remove classes and modules when we see a :nodoc: all - def remove_classes_and_modules - initialize_classes_and_modules - end - - def initialize_classes_and_modules - @classes = {} - @modules = {} - end - - # Find a named module - def find_module_named(name) - return self if self.name == name - res = @modules[name] || @classes[name] - return res if res - find_enclosing_module_named(name) - end - - # find a module at a higher scope - def find_enclosing_module_named(name) - parent && parent.find_module_named(name) - end - - # Iterate over all the classes and modules in - # this object - - def each_classmodule - @modules.each_value {|m| yield m} - @classes.each_value {|c| yield c} - end - - def each_method - @method_list.each {|m| yield m} - end - - def each_attribute - @attributes.each {|a| yield a} - end - - def each_constant - @constants.each {|c| yield c} - end - - # Return the toplevel that owns us - - def toplevel - return @toplevel if defined? @toplevel - @toplevel = self - @toplevel = @toplevel.parent until TopLevel === @toplevel - @toplevel - end - - # allow us to sort modules by name - def <=>(other) - name <=> other.name - end - - ## - # Look up +symbol+. If +method+ is non-nil, then we assume the symbol - # references a module that contains that method. - - def find_symbol(symbol, method = nil) - result = nil - - case symbol - when /^::(.*)/ then - result = toplevel.find_symbol($1) - when /::/ then - modules = symbol.split(/::/) - - unless modules.empty? then - module_name = modules.shift - result = find_module_named(module_name) - if result then - modules.each do |name| - result = result.find_module_named(name) - break unless result - end - end - end - - else - # if a method is specified, then we're definitely looking for - # a module, otherwise it could be any symbol - if method - result = find_module_named(symbol) - else - result = find_local_symbol(symbol) - if result.nil? - if symbol =~ /^[A-Z]/ - result = parent - while result && result.name != symbol - result = result.parent - end - end - end - end - end - - if result and method then - fail unless result.respond_to? :find_local_symbol - result = result.find_local_symbol(method) - end - - result - end - - def find_local_symbol(symbol) - res = find_method_named(symbol) || - find_constant_named(symbol) || - find_attribute_named(symbol) || - find_module_named(symbol) || - find_file_named(symbol) - end - - # Handle sections - - def set_current_section(title, comment) - @current_section = Section.new(title, comment) - @sections << @current_section - end - - private - - # Find a named method, or return nil - def find_method_named(name) - @method_list.find {|meth| meth.name == name} - end - - # Find a named instance method, or return nil - def find_instance_method_named(name) - @method_list.find {|meth| meth.name == name && !meth.singleton} - end - - # Find a named constant, or return nil - def find_constant_named(name) - @constants.find {|m| m.name == name} - end - - # Find a named attribute, or return nil - def find_attribute_named(name) - @attributes.find {|m| m.name == name} - end - - ## - # Find a named file, or return nil - - def find_file_named(name) - toplevel.class.find_file_named(name) - end - - end - - ## - # A TopLevel context is a source file - - class TopLevel < Context - attr_accessor :file_stat - attr_accessor :file_relative_name - attr_accessor :file_absolute_name - attr_accessor :diagram - - @@all_classes = {} - @@all_modules = {} - @@all_files = {} - - def self.reset - @@all_classes = {} - @@all_modules = {} - @@all_files = {} - end - - def initialize(file_name) - super() - @name = "TopLevel" - @file_relative_name = file_name - @file_absolute_name = file_name - @file_stat = File.stat(file_name) - @diagram = nil - @@all_files[file_name] = self - end - - def file_base_name - File.basename @file_absolute_name - end - - def full_name - nil - end - - ## - # Adding a class or module to a TopLevel is special, as we only want one - # copy of a particular top-level class. For example, if both file A and - # file B implement class C, we only want one ClassModule object for C. - # This code arranges to share classes and modules between files. - - def add_class_or_module(collection, class_type, name, superclass) - cls = collection[name] - - if cls then - cls.superclass = superclass unless cls.module? - puts "Reusing class/module #{cls.full_name}" if $DEBUG_RDOC - else - if class_type == NormalModule then - all = @@all_modules - else - all = @@all_classes - end - - cls = all[name] - - unless cls then - cls = class_type.new name, superclass - all[name] = cls unless @done_documenting - end - - collection[name] = cls unless @done_documenting - - cls.parent = self - end - - cls - end - - def self.all_classes_and_modules - @@all_classes.values + @@all_modules.values - end - - def self.find_class_named(name) - @@all_classes.each_value do |c| - res = c.find_class_named(name) - return res if res - end - nil - end - - def self.find_file_named(name) - @@all_files[name] - end - - def find_local_symbol(symbol) - find_class_or_module_named(symbol) || super - end - - def find_class_or_module_named(symbol) - @@all_classes.each_value {|c| return c if c.name == symbol} - @@all_modules.each_value {|m| return m if m.name == symbol} - nil - end - - ## - # Find a named module - - def find_module_named(name) - find_class_or_module_named(name) || find_enclosing_module_named(name) - end - - def inspect - "#<%s:0x%x %p modules: %p classes: %p>" % [ - self.class, object_id, - file_base_name, - @modules.map { |n,m| m }, - @classes.map { |n,c| c } - ] - end - - end - - ## - # ClassModule is the base class for objects representing either a class or a - # module. - - class ClassModule < Context - - attr_accessor :diagram - - def initialize(name, superclass = nil) - @name = name - @diagram = nil - @superclass = superclass - @comment = "" - super() - end - - def find_class_named(name) - return self if full_name == name - @classes.each_value {|c| return c if c.find_class_named(name) } - nil - end - - ## - # Return the fully qualified name of this class or module - - def full_name - if @parent && @parent.full_name - @parent.full_name + "::" + @name - else - @name - end - end - - def http_url(prefix) - path = full_name.split("::") - File.join(prefix, *path) + ".html" - end - - ## - # Does this object represent a module? - - def module? - false - end - - ## - # Get the superclass of this class. Attempts to retrieve the superclass' - # real name by following module nesting. - - def superclass - raise NoMethodError, "#{full_name} is a module" if module? - - scope = self - - begin - superclass = scope.classes.find { |c| c.name == @superclass } - - return superclass.full_name if superclass - scope = scope.parent - end until scope.nil? or TopLevel === scope - - @superclass - end - - ## - # Set the superclass of this class - - def superclass=(superclass) - raise NoMethodError, "#{full_name} is a module" if module? - - if @superclass.nil? or @superclass == 'Object' then - @superclass = superclass - end - end - - def to_s - "#{self.class}: #{@name} #{@comment} #{super}" - end - - end - - ## - # Anonymous classes - - class AnonClass < ClassModule - end - - ## - # Normal classes - - class NormalClass < ClassModule - - def inspect - superclass = @superclass ? " < #{@superclass}" : nil - "<%s:0x%x class %s%s includes: %p attributes: %p methods: %p aliases: %p>" % [ - self.class, object_id, - @name, superclass, @includes, @attributes, @method_list, @aliases - ] - end - - end - - ## - # Singleton classes - - class SingleClass < ClassModule - end - - ## - # Module - - class NormalModule < ClassModule - - def comment=(comment) - return if comment.empty? - comment = @comment << "# ---\n" << comment unless @comment.empty? - - super - end - - def inspect - "#<%s:0x%x module %s includes: %p attributes: %p methods: %p aliases: %p>" % [ - self.class, object_id, - @name, @includes, @attributes, @method_list, @aliases - ] - end - - def module? - true - end - - end - - ## - # AnyMethod is the base class for objects representing methods - - class AnyMethod < CodeObject - - attr_accessor :name - attr_accessor :visibility - attr_accessor :block_params - attr_accessor :dont_rename_initialize - attr_accessor :singleton - attr_reader :text - - # list of other names for this method - attr_reader :aliases - - # method we're aliasing - attr_accessor :is_alias_for - - attr_overridable :params, :param, :parameters, :parameter - - attr_accessor :call_seq - - include TokenStream - - def initialize(text, name) - super() - @text = text - @name = name - @token_stream = nil - @visibility = :public - @dont_rename_initialize = false - @block_params = nil - @aliases = [] - @is_alias_for = nil - @comment = "" - @call_seq = nil - end - - def <=>(other) - @name <=> other.name - end - - def add_alias(method) - @aliases << method - end - - def inspect - alias_for = @is_alias_for ? " (alias for #{@is_alias_for.name})" : nil - "#<%s:0x%x %s%s%s (%s)%s>" % [ - self.class, object_id, - parent_name, - singleton ? '::' : '#', - name, - visibility, - alias_for, - ] - end - - def param_seq - params = params.gsub(/\s*\#.*/, '') - params = params.tr("\n", " ").squeeze(" ") - params = "(#{params})" unless p[0] == ?( - - if block = block_params then # yes, = - # If this method has explicit block parameters, remove any explicit - # &block - params.sub!(/,?\s*&\w+/) - - block.gsub!(/\s*\#.*/, '') - block = block.tr("\n", " ").squeeze(" ") - if block[0] == ?( - block.sub!(/^\(/, '').sub!(/\)/, '') - end - params << " { |#{block}| ... }" - end - - params - end - - def to_s - res = self.class.name + ": " + @name + " (" + @text + ")\n" - res << @comment.to_s - res - end - - end - - ## - # GhostMethod represents a method referenced only by a comment - - class GhostMethod < AnyMethod - end - - ## - # MetaMethod represents a meta-programmed method - - class MetaMethod < AnyMethod - end - - ## - # Represent an alias, which is an old_name/ new_name pair associated with a - # particular context - - class Alias < CodeObject - - attr_accessor :text, :old_name, :new_name, :comment - - def initialize(text, old_name, new_name, comment) - super() - @text = text - @old_name = old_name - @new_name = new_name - self.comment = comment - end - - def inspect - "#<%s:0x%x %s.alias_method %s, %s>" % [ - self.class, object_id, - parent.name, @old_name, @new_name, - ] - end - - def to_s - "alias: #{self.old_name} -> #{self.new_name}\n#{self.comment}" - end - - end - - ## - # Represent a constant - - class Constant < CodeObject - attr_accessor :name, :value - - def initialize(name, value, comment) - super() - @name = name - @value = value - self.comment = comment - end - end - - ## - # Represent attributes - - class Attr < CodeObject - attr_accessor :text, :name, :rw, :visibility - - def initialize(text, name, rw, comment) - super() - @text = text - @name = name - @rw = rw - @visibility = :public - self.comment = comment - end - - def <=>(other) - self.name <=> other.name - end - - def inspect - attr = case rw - when 'RW' then :attr_accessor - when 'R' then :attr_reader - when 'W' then :attr_writer - else - " (#{rw})" - end - - "#<%s:0x%x %s.%s :%s>" % [ - self.class, object_id, - parent_name, attr, @name, - ] - end - - def to_s - "attr: #{self.name} #{self.rw}\n#{self.comment}" - end - - end - - ## - # A required file - - class Require < CodeObject - attr_accessor :name - - def initialize(name, comment) - super() - @name = name.gsub(/'|"/, "") #' - self.comment = comment - end - - def inspect - "#<%s:0x%x require '%s' in %s>" % [ - self.class, - object_id, - @name, - parent_file_name, - ] - end - - end - - ## - # An included module - - class Include < CodeObject - - attr_accessor :name - - def initialize(name, comment) - super() - @name = name - self.comment = comment - - end - - def inspect - "#<%s:0x%x %s.include %s>" % [ - self.class, - object_id, - parent_name, @name, - ] - end - - end - -end |