summaryrefslogtreecommitdiff
path: root/jni/ruby/lib/rdoc/context.rb
diff options
context:
space:
mode:
authorJari Vetoniemi <jari.vetoniemi@indooratlas.com>2020-03-16 18:49:26 +0900
committerJari Vetoniemi <jari.vetoniemi@indooratlas.com>2020-03-30 00:39:06 +0900
commitfcbf63e62c627deae76c1b8cb8c0876c536ed811 (patch)
tree64cb17de3f41a2b6fef2368028fbd00349946994 /jni/ruby/lib/rdoc/context.rb
Fresh start
Diffstat (limited to 'jni/ruby/lib/rdoc/context.rb')
-rw-r--r--jni/ruby/lib/rdoc/context.rb1211
1 files changed, 1211 insertions, 0 deletions
diff --git a/jni/ruby/lib/rdoc/context.rb b/jni/ruby/lib/rdoc/context.rb
new file mode 100644
index 0000000..5eb86dd
--- /dev/null
+++ b/jni/ruby/lib/rdoc/context.rb
@@ -0,0 +1,1211 @@
+require 'cgi'
+
+##
+# A Context is something that can hold modules, classes, methods, attributes,
+# aliases, requires, and includes. Classes, modules, and files are all
+# Contexts.
+
+class RDoc::Context < RDoc::CodeObject
+
+ include Comparable
+
+ ##
+ # Types of methods
+
+ TYPES = %w[class instance]
+
+ ##
+ # If a context has these titles it will be sorted in this order.
+
+ TOMDOC_TITLES = [nil, 'Public', 'Internal', 'Deprecated'] # :nodoc:
+ TOMDOC_TITLES_SORT = TOMDOC_TITLES.sort_by { |title| title.to_s } # :nodoc:
+
+ ##
+ # Class/module aliases
+
+ attr_reader :aliases
+
+ ##
+ # All attr* methods
+
+ attr_reader :attributes
+
+ ##
+ # Block params to be used in the next MethodAttr parsed under this context
+
+ attr_accessor :block_params
+
+ ##
+ # Constants defined
+
+ attr_reader :constants
+
+ ##
+ # Sets the current documentation section of documentation
+
+ attr_writer :current_section
+
+ ##
+ # Files this context is found in
+
+ attr_reader :in_files
+
+ ##
+ # Modules this context includes
+
+ attr_reader :includes
+
+ ##
+ # Modules this context is extended with
+
+ attr_reader :extends
+
+ ##
+ # Methods defined in this context
+
+ attr_reader :method_list
+
+ ##
+ # Name of this class excluding namespace. See also full_name
+
+ attr_reader :name
+
+ ##
+ # Files this context requires
+
+ attr_reader :requires
+
+ ##
+ # Use this section for the next method, attribute or constant added.
+
+ attr_accessor :temporary_section
+
+ ##
+ # Hash <tt>old_name => [aliases]</tt>, for aliases
+ # that haven't (yet) been resolved to a method/attribute.
+ # (Not to be confused with the aliases of the context.)
+
+ attr_accessor :unmatched_alias_lists
+
+ ##
+ # Aliases that could not be resolved.
+
+ attr_reader :external_aliases
+
+ ##
+ # Current visibility of this context
+
+ attr_accessor :visibility
+
+ ##
+ # Hash of registered methods. Attributes are also registered here,
+ # twice if they are RW.
+
+ attr_reader :methods_hash
+
+ ##
+ # Params to be used in the next MethodAttr parsed under this context
+
+ attr_accessor :params
+
+ ##
+ # Hash of registered constants.
+
+ attr_reader :constants_hash
+
+ ##
+ # Creates an unnamed empty context with public current visibility
+
+ def initialize
+ super
+
+ @in_files = []
+
+ @name ||= "unknown"
+ @parent = nil
+ @visibility = :public
+
+ @current_section = Section.new self, nil, nil
+ @sections = { nil => @current_section }
+ @temporary_section = nil
+
+ @classes = {}
+ @modules = {}
+
+ initialize_methods_etc
+ end
+
+ ##
+ # Sets the defaults for methods and so-forth
+
+ def initialize_methods_etc
+ @method_list = []
+ @attributes = []
+ @aliases = []
+ @requires = []
+ @includes = []
+ @extends = []
+ @constants = []
+ @external_aliases = []
+
+ # This Hash maps a method name to a list of unmatched aliases (aliases of
+ # a method not yet encountered).
+ @unmatched_alias_lists = {}
+
+ @methods_hash = {}
+ @constants_hash = {}
+
+ @params = nil
+
+ @store ||= nil
+ end
+
+ ##
+ # Contexts are sorted by full_name
+
+ def <=>(other)
+ return nil unless RDoc::CodeObject === other
+
+ full_name <=> other.full_name
+ end
+
+ ##
+ # Adds an item of type +klass+ with the given +name+ and +comment+ to the
+ # context.
+ #
+ # Currently only RDoc::Extend and RDoc::Include are supported.
+
+ def add klass, name, comment
+ if RDoc::Extend == klass then
+ ext = RDoc::Extend.new name, comment
+ add_extend ext
+ elsif RDoc::Include == klass then
+ incl = RDoc::Include.new name, comment
+ add_include incl
+ else
+ raise NotImplementedError, "adding a #{klass} is not implemented"
+ end
+ end
+
+ ##
+ # Adds +an_alias+ that is automatically resolved
+
+ def add_alias an_alias
+ return an_alias unless @document_self
+
+ method_attr = find_method(an_alias.old_name, an_alias.singleton) ||
+ find_attribute(an_alias.old_name, an_alias.singleton)
+
+ if method_attr then
+ method_attr.add_alias an_alias, self
+ else
+ add_to @external_aliases, an_alias
+ unmatched_alias_list =
+ @unmatched_alias_lists[an_alias.pretty_old_name] ||= []
+ unmatched_alias_list.push an_alias
+ end
+
+ an_alias
+ end
+
+ ##
+ # Adds +attribute+ if not already there. If it is (as method(s) or attribute),
+ # updates the comment if it was empty.
+ #
+ # The attribute is registered only if it defines a new method.
+ # For instance, <tt>attr_reader :foo</tt> will not be registered
+ # if method +foo+ exists, but <tt>attr_accessor :foo</tt> will be registered
+ # if method +foo+ exists, but <tt>foo=</tt> does not.
+
+ def add_attribute attribute
+ return attribute unless @document_self
+
+ # mainly to check for redefinition of an attribute as a method
+ # TODO find a policy for 'attr_reader :foo' + 'def foo=()'
+ register = false
+
+ key = nil
+
+ if attribute.rw.index 'R' then
+ key = attribute.pretty_name
+ known = @methods_hash[key]
+
+ if known then
+ known.comment = attribute.comment if known.comment.empty?
+ elsif registered = @methods_hash[attribute.pretty_name << '='] and
+ RDoc::Attr === registered then
+ registered.rw = 'RW'
+ else
+ @methods_hash[key] = attribute
+ register = true
+ end
+ end
+
+ if attribute.rw.index 'W' then
+ key = attribute.pretty_name << '='
+ known = @methods_hash[key]
+
+ if known then
+ known.comment = attribute.comment if known.comment.empty?
+ elsif registered = @methods_hash[attribute.pretty_name] and
+ RDoc::Attr === registered then
+ registered.rw = 'RW'
+ else
+ @methods_hash[key] = attribute
+ register = true
+ end
+ end
+
+ if register then
+ attribute.visibility = @visibility
+ add_to @attributes, attribute
+ resolve_aliases attribute
+ end
+
+ attribute
+ end
+
+ ##
+ # Adds a class named +given_name+ with +superclass+.
+ #
+ # Both +given_name+ and +superclass+ may contain '::', and are
+ # interpreted relative to the +self+ context. This allows handling correctly
+ # examples like these:
+ # class RDoc::Gauntlet < Gauntlet
+ # module Mod
+ # class Object # implies < ::Object
+ # class SubObject < Object # this is _not_ ::Object
+ #
+ # Given <tt>class Container::Item</tt> RDoc assumes +Container+ is a module
+ # unless it later sees <tt>class Container</tt>. +add_class+ automatically
+ # upgrades +given_name+ to a class in this case.
+
+ def add_class class_type, given_name, superclass = '::Object'
+ # superclass +nil+ is passed by the C parser in the following cases:
+ # - registering Object in 1.8 (correct)
+ # - registering BasicObject in 1.9 (correct)
+ # - registering RubyVM in 1.9 in iseq.c (incorrect: < Object in vm.c)
+ #
+ # If we later find a superclass for a registered class with a nil
+ # superclass, we must honor it.
+
+ # find the name & enclosing context
+ if given_name =~ /^:+(\w+)$/ then
+ full_name = $1
+ enclosing = top_level
+ name = full_name.split(/:+/).last
+ else
+ full_name = child_name given_name
+
+ if full_name =~ /^(.+)::(\w+)$/ then
+ name = $2
+ ename = $1
+ enclosing = @store.classes_hash[ename] || @store.modules_hash[ename]
+ # HACK: crashes in actionpack/lib/action_view/helpers/form_helper.rb (metaprogramming)
+ unless enclosing then
+ # try the given name at top level (will work for the above example)
+ enclosing = @store.classes_hash[given_name] ||
+ @store.modules_hash[given_name]
+ return enclosing if enclosing
+ # not found: create the parent(s)
+ names = ename.split('::')
+ enclosing = self
+ names.each do |n|
+ enclosing = enclosing.classes_hash[n] ||
+ enclosing.modules_hash[n] ||
+ enclosing.add_module(RDoc::NormalModule, n)
+ end
+ end
+ else
+ name = full_name
+ enclosing = self
+ end
+ end
+
+ # fix up superclass
+ if full_name == 'BasicObject' then
+ superclass = nil
+ elsif full_name == 'Object' then
+ superclass = defined?(::BasicObject) ? '::BasicObject' : nil
+ end
+
+ # find the superclass full name
+ if superclass then
+ if superclass =~ /^:+/ then
+ superclass = $' #'
+ else
+ if superclass =~ /^(\w+):+(.+)$/ then
+ suffix = $2
+ mod = find_module_named($1)
+ superclass = mod.full_name + '::' + suffix if mod
+ else
+ mod = find_module_named(superclass)
+ superclass = mod.full_name if mod
+ end
+ end
+
+ # did we believe it was a module?
+ mod = @store.modules_hash.delete superclass
+
+ upgrade_to_class mod, RDoc::NormalClass, mod.parent if mod
+
+ # e.g., Object < Object
+ superclass = nil if superclass == full_name
+ end
+
+ klass = @store.classes_hash[full_name]
+
+ if klass then
+ # if TopLevel, it may not be registered in the classes:
+ enclosing.classes_hash[name] = klass
+
+ # update the superclass if needed
+ if superclass then
+ existing = klass.superclass
+ existing = existing.full_name unless existing.is_a?(String) if existing
+ if existing.nil? ||
+ (existing == 'Object' && superclass != 'Object') then
+ klass.superclass = superclass
+ end
+ end
+ else
+ # this is a new class
+ mod = @store.modules_hash.delete full_name
+
+ if mod then
+ klass = upgrade_to_class mod, RDoc::NormalClass, enclosing
+
+ klass.superclass = superclass unless superclass.nil?
+ else
+ klass = class_type.new name, superclass
+
+ enclosing.add_class_or_module(klass, enclosing.classes_hash,
+ @store.classes_hash)
+ end
+ end
+
+ klass.parent = self
+
+ klass
+ end
+
+ ##
+ # Adds the class or module +mod+ to the modules or
+ # classes Hash +self_hash+, and to +all_hash+ (either
+ # <tt>TopLevel::modules_hash</tt> or <tt>TopLevel::classes_hash</tt>),
+ # unless #done_documenting is +true+. Sets the #parent of +mod+
+ # to +self+, and its #section to #current_section. Returns +mod+.
+
+ def add_class_or_module mod, self_hash, all_hash
+ mod.section = current_section # TODO declaring context? something is
+ # wrong here...
+ mod.parent = self
+ mod.store = @store
+
+ unless @done_documenting then
+ self_hash[mod.name] = mod
+ # this must be done AFTER adding mod to its parent, so that the full
+ # name is correct:
+ all_hash[mod.full_name] = mod
+ end
+
+ mod
+ end
+
+ ##
+ # Adds +constant+ if not already there. If it is, updates the comment,
+ # value and/or is_alias_for of the known constant if they were empty/nil.
+
+ def add_constant constant
+ return constant unless @document_self
+
+ # HACK: avoid duplicate 'PI' & 'E' in math.c (1.8.7 source code)
+ # (this is a #ifdef: should be handled by the C parser)
+ known = @constants_hash[constant.name]
+
+ if known then
+ known.comment = constant.comment if known.comment.empty?
+
+ known.value = constant.value if
+ known.value.nil? or known.value.strip.empty?
+
+ known.is_alias_for ||= constant.is_alias_for
+ else
+ @constants_hash[constant.name] = constant
+ add_to @constants, constant
+ end
+
+ constant
+ end
+
+ ##
+ # Adds included module +include+ which should be an RDoc::Include
+
+ def add_include include
+ add_to @includes, include
+
+ include
+ end
+
+ ##
+ # Adds extension module +ext+ which should be an RDoc::Extend
+
+ def add_extend ext
+ add_to @extends, ext
+
+ ext
+ end
+
+ ##
+ # Adds +method+ if not already there. If it is (as method or attribute),
+ # updates the comment if it was empty.
+
+ def add_method method
+ return method unless @document_self
+
+ # HACK: avoid duplicate 'new' in io.c & struct.c (1.8.7 source code)
+ key = method.pretty_name
+ known = @methods_hash[key]
+
+ if known then
+ if @store then # otherwise we are loading
+ known.comment = method.comment if known.comment.empty?
+ previously = ", previously in #{known.file}" unless
+ method.file == known.file
+ @store.rdoc.options.warn \
+ "Duplicate method #{known.full_name} in #{method.file}#{previously}"
+ end
+ else
+ @methods_hash[key] = method
+ method.visibility = @visibility
+ add_to @method_list, method
+ resolve_aliases method
+ end
+
+ method
+ end
+
+ ##
+ # Adds a module named +name+. If RDoc already knows +name+ is a class then
+ # that class is returned instead. See also #add_class.
+
+ def add_module(class_type, name)
+ mod = @classes[name] || @modules[name]
+ return mod if mod
+
+ full_name = child_name name
+ mod = @store.modules_hash[full_name] || class_type.new(name)
+
+ add_class_or_module mod, @modules, @store.modules_hash
+ end
+
+ ##
+ # Adds an alias from +from+ (a class or module) to +name+ which was defined
+ # in +file+.
+
+ def add_module_alias from, name, file
+ return from if @done_documenting
+
+ to_name = child_name name
+
+ # if we already know this name, don't register an alias:
+ # see the metaprogramming in lib/active_support/basic_object.rb,
+ # where we already know BasicObject is a class when we find
+ # BasicObject = BlankSlate
+ return from if @store.find_class_or_module to_name
+
+ to = from.dup
+ to.name = name
+ to.full_name = nil
+
+ if to.module? then
+ @store.modules_hash[to_name] = to
+ @modules[name] = to
+ else
+ @store.classes_hash[to_name] = to
+ @classes[name] = to
+ end
+
+ # Registers a constant for this alias. The constant value and comment
+ # will be updated later, when the Ruby parser adds the constant
+ const = RDoc::Constant.new name, nil, to.comment
+ const.record_location file
+ const.is_alias_for = from
+ add_constant const
+
+ to
+ end
+
+ ##
+ # Adds +require+ to this context's top level
+
+ def add_require(require)
+ return require unless @document_self
+
+ if RDoc::TopLevel === self then
+ add_to @requires, require
+ else
+ parent.add_require require
+ end
+ end
+
+ ##
+ # Returns a section with +title+, creating it if it doesn't already exist.
+ # +comment+ will be appended to the section's comment.
+ #
+ # A section with a +title+ of +nil+ will return the default section.
+ #
+ # See also RDoc::Context::Section
+
+ def add_section title, comment = nil
+ if section = @sections[title] then
+ section.add_comment comment if comment
+ else
+ section = Section.new self, title, comment
+ @sections[title] = section
+ end
+
+ section
+ end
+
+ ##
+ # Adds +thing+ to the collection +array+
+
+ def add_to array, thing
+ array << thing if @document_self
+
+ thing.parent = self
+ thing.store = @store if @store
+ thing.section = current_section
+ end
+
+ ##
+ # Is there any content?
+ #
+ # This means any of: comment, aliases, methods, attributes, external
+ # aliases, require, constant.
+ #
+ # Includes and extends are also checked unless <tt>includes == false</tt>.
+
+ def any_content(includes = true)
+ @any_content ||= !(
+ @comment.empty? &&
+ @method_list.empty? &&
+ @attributes.empty? &&
+ @aliases.empty? &&
+ @external_aliases.empty? &&
+ @requires.empty? &&
+ @constants.empty?
+ )
+ @any_content || (includes && !(@includes + @extends).empty? )
+ end
+
+ ##
+ # Creates the full name for a child with +name+
+
+ def child_name name
+ if name =~ /^:+/
+ $' #'
+ elsif RDoc::TopLevel === self then
+ name
+ else
+ "#{self.full_name}::#{name}"
+ end
+ end
+
+ ##
+ # Class attributes
+
+ def class_attributes
+ @class_attributes ||= attributes.select { |a| a.singleton }
+ end
+
+ ##
+ # Class methods
+
+ def class_method_list
+ @class_method_list ||= method_list.select { |a| a.singleton }
+ end
+
+ ##
+ # Array of classes in this context
+
+ def classes
+ @classes.values
+ end
+
+ ##
+ # All classes and modules in this namespace
+
+ def classes_and_modules
+ classes + modules
+ end
+
+ ##
+ # Hash of classes keyed by class name
+
+ def classes_hash
+ @classes
+ end
+
+ ##
+ # The current documentation section that new items will be added to. If
+ # temporary_section is available it will be used.
+
+ def current_section
+ if section = @temporary_section then
+ @temporary_section = nil
+ else
+ section = @current_section
+ end
+
+ section
+ end
+
+ ##
+ # Is part of this thing was defined in +file+?
+
+ def defined_in?(file)
+ @in_files.include?(file)
+ end
+
+ def display(method_attr) # :nodoc:
+ if method_attr.is_a? RDoc::Attr
+ "#{method_attr.definition} #{method_attr.pretty_name}"
+ else
+ "method #{method_attr.pretty_name}"
+ end
+ end
+
+ ##
+ # Iterator for ancestors for duck-typing. Does nothing. See
+ # RDoc::ClassModule#each_ancestor.
+ #
+ # This method exists to make it easy to work with Context subclasses that
+ # aren't part of RDoc.
+
+ def each_ancestor # :nodoc:
+ end
+
+ ##
+ # Iterator for attributes
+
+ def each_attribute # :yields: attribute
+ @attributes.each { |a| yield a }
+ end
+
+ ##
+ # Iterator for classes and modules
+
+ def each_classmodule(&block) # :yields: module
+ classes_and_modules.sort.each(&block)
+ end
+
+ ##
+ # Iterator for constants
+
+ def each_constant # :yields: constant
+ @constants.each {|c| yield c}
+ end
+
+ ##
+ # Iterator for included modules
+
+ def each_include # :yields: include
+ @includes.each do |i| yield i end
+ end
+
+ ##
+ # Iterator for extension modules
+
+ def each_extend # :yields: extend
+ @extends.each do |e| yield e end
+ end
+
+ ##
+ # Iterator for methods
+
+ def each_method # :yields: method
+ return enum_for __method__ unless block_given?
+
+ @method_list.sort.each { |m| yield m }
+ end
+
+ ##
+ # Iterator for each section's contents sorted by title. The +section+, the
+ # section's +constants+ and the sections +attributes+ are yielded. The
+ # +constants+ and +attributes+ collections are sorted.
+ #
+ # To retrieve methods in a section use #methods_by_type with the optional
+ # +section+ parameter.
+ #
+ # NOTE: Do not edit collections yielded by this method
+
+ def each_section # :yields: section, constants, attributes
+ return enum_for __method__ unless block_given?
+
+ constants = @constants.group_by do |constant| constant.section end
+ attributes = @attributes.group_by do |attribute| attribute.section end
+
+ constants.default = []
+ attributes.default = []
+
+ sort_sections.each do |section|
+ yield section, constants[section].sort, attributes[section].sort
+ end
+ end
+
+ ##
+ # Finds an attribute +name+ with singleton value +singleton+.
+
+ def find_attribute(name, singleton)
+ name = $1 if name =~ /^(.*)=$/
+ @attributes.find { |a| a.name == name && a.singleton == singleton }
+ end
+
+ ##
+ # Finds an attribute with +name+ in this context
+
+ def find_attribute_named(name)
+ case name
+ when /\A#/ then
+ find_attribute name[1..-1], false
+ when /\A::/ then
+ find_attribute name[2..-1], true
+ else
+ @attributes.find { |a| a.name == name }
+ end
+ end
+
+ ##
+ # Finds a class method with +name+ in this context
+
+ def find_class_method_named(name)
+ @method_list.find { |meth| meth.singleton && meth.name == name }
+ end
+
+ ##
+ # Finds a constant with +name+ in this context
+
+ def find_constant_named(name)
+ @constants.find {|m| m.name == name}
+ end
+
+ ##
+ # Find a module at a higher scope
+
+ def find_enclosing_module_named(name)
+ parent && parent.find_module_named(name)
+ end
+
+ ##
+ # Finds an external alias +name+ with singleton value +singleton+.
+
+ def find_external_alias(name, singleton)
+ @external_aliases.find { |m| m.name == name && m.singleton == singleton }
+ end
+
+ ##
+ # Finds an external alias with +name+ in this context
+
+ def find_external_alias_named(name)
+ case name
+ when /\A#/ then
+ find_external_alias name[1..-1], false
+ when /\A::/ then
+ find_external_alias name[2..-1], true
+ else
+ @external_aliases.find { |a| a.name == name }
+ end
+ end
+
+ ##
+ # Finds a file with +name+ in this context
+
+ def find_file_named name
+ @store.find_file_named name
+ end
+
+ ##
+ # Finds an instance method with +name+ in this context
+
+ def find_instance_method_named(name)
+ @method_list.find { |meth| !meth.singleton && meth.name == name }
+ end
+
+ ##
+ # Finds a method, constant, attribute, external alias, module or file
+ # named +symbol+ in this context.
+
+ def find_local_symbol(symbol)
+ find_method_named(symbol) or
+ find_constant_named(symbol) or
+ find_attribute_named(symbol) or
+ find_external_alias_named(symbol) or
+ find_module_named(symbol) or
+ find_file_named(symbol)
+ end
+
+ ##
+ # Finds a method named +name+ with singleton value +singleton+.
+
+ def find_method(name, singleton)
+ @method_list.find { |m| m.name == name && m.singleton == singleton }
+ end
+
+ ##
+ # Finds a instance or module method with +name+ in this context
+
+ def find_method_named(name)
+ case name
+ when /\A#/ then
+ find_method name[1..-1], false
+ when /\A::/ then
+ find_method name[2..-1], true
+ else
+ @method_list.find { |meth| meth.name == name }
+ end
+ end
+
+ ##
+ # Find a module with +name+ using ruby's scoping rules
+
+ def find_module_named(name)
+ res = @modules[name] || @classes[name]
+ return res if res
+ return self if self.name == name
+ find_enclosing_module_named name
+ end
+
+ ##
+ # Look up +symbol+, first as a module, then as a local symbol.
+
+ def find_symbol(symbol)
+ find_symbol_module(symbol) || find_local_symbol(symbol)
+ end
+
+ ##
+ # Look up a module named +symbol+.
+
+ def find_symbol_module(symbol)
+ result = nil
+
+ # look for a class or module 'symbol'
+ case symbol
+ when /^::/ then
+ result = @store.find_class_or_module symbol
+ when /^(\w+):+(.+)$/
+ suffix = $2
+ top = $1
+ searched = self
+ while searched do
+ mod = searched.find_module_named(top)
+ break unless mod
+ result = @store.find_class_or_module "#{mod.full_name}::#{suffix}"
+ break if result || searched.is_a?(RDoc::TopLevel)
+ searched = searched.parent
+ end
+ else
+ searched = self
+ while searched do
+ result = searched.find_module_named(symbol)
+ break if result || searched.is_a?(RDoc::TopLevel)
+ searched = searched.parent
+ end
+ end
+
+ result
+ end
+
+ ##
+ # The full name for this context. This method is overridden by subclasses.
+
+ def full_name
+ '(unknown)'
+ end
+
+ ##
+ # Does this context and its methods and constants all have documentation?
+ #
+ # (Yes, fully documented doesn't mean everything.)
+
+ def fully_documented?
+ documented? and
+ attributes.all? { |a| a.documented? } and
+ method_list.all? { |m| m.documented? } and
+ constants.all? { |c| c.documented? }
+ end
+
+ ##
+ # URL for this with a +prefix+
+
+ def http_url(prefix)
+ path = name_for_path
+ path = path.gsub(/<<\s*(\w*)/, 'from-\1') if path =~ /<</
+ path = [prefix] + path.split('::')
+
+ File.join(*path.compact) + '.html'
+ end
+
+ ##
+ # Instance attributes
+
+ def instance_attributes
+ @instance_attributes ||= attributes.reject { |a| a.singleton }
+ end
+
+ ##
+ # Instance methods
+ #--
+ # TODO rename to instance_methods
+
+ def instance_method_list
+ @instance_method_list ||= method_list.reject { |a| a.singleton }
+ end
+
+ ##
+ # Breaks method_list into a nested hash by type (<tt>'class'</tt> or
+ # <tt>'instance'</tt>) and visibility (+:public+, +:protected+, +:private+).
+ #
+ # If +section+ is provided only methods in that RDoc::Context::Section will
+ # be returned.
+
+ def methods_by_type section = nil
+ methods = {}
+
+ TYPES.each do |type|
+ visibilities = {}
+ RDoc::VISIBILITIES.each do |vis|
+ visibilities[vis] = []
+ end
+
+ methods[type] = visibilities
+ end
+
+ each_method do |method|
+ next if section and not method.section == section
+ methods[method.type][method.visibility] << method
+ end
+
+ methods
+ end
+
+ ##
+ # Yields AnyMethod and Attr entries matching the list of names in +methods+.
+
+ def methods_matching(methods, singleton = false, &block)
+ (@method_list + @attributes).each do |m|
+ yield m if methods.include?(m.name) and m.singleton == singleton
+ end
+
+ each_ancestor do |parent|
+ parent.methods_matching(methods, singleton, &block)
+ end
+ end
+
+ ##
+ # Array of modules in this context
+
+ def modules
+ @modules.values
+ end
+
+ ##
+ # Hash of modules keyed by module name
+
+ def modules_hash
+ @modules
+ end
+
+ ##
+ # Name to use to generate the url.
+ # <tt>#full_name</tt> by default.
+
+ def name_for_path
+ full_name
+ end
+
+ ##
+ # Changes the visibility for new methods to +visibility+
+
+ def ongoing_visibility=(visibility)
+ @visibility = visibility
+ end
+
+ ##
+ # Record +top_level+ as a file +self+ is in.
+
+ def record_location(top_level)
+ @in_files << top_level unless @in_files.include?(top_level)
+ end
+
+ ##
+ # Should we remove this context from the documentation?
+ #
+ # The answer is yes if:
+ # * #received_nodoc is +true+
+ # * #any_content is +false+ (not counting includes)
+ # * All #includes are modules (not a string), and their module has
+ # <tt>#remove_from_documentation? == true</tt>
+ # * All classes and modules have <tt>#remove_from_documentation? == true</tt>
+
+ def remove_from_documentation?
+ @remove_from_documentation ||=
+ @received_nodoc &&
+ !any_content(false) &&
+ @includes.all? { |i| !i.module.is_a?(String) && i.module.remove_from_documentation? } &&
+ classes_and_modules.all? { |cm| cm.remove_from_documentation? }
+ end
+
+ ##
+ # Removes methods and attributes with a visibility less than +min_visibility+.
+ #--
+ # TODO mark the visibility of attributes in the template (if not public?)
+
+ def remove_invisible min_visibility
+ return if [:private, :nodoc].include? min_visibility
+ remove_invisible_in @method_list, min_visibility
+ remove_invisible_in @attributes, min_visibility
+ end
+
+ ##
+ # Only called when min_visibility == :public or :private
+
+ def remove_invisible_in array, min_visibility # :nodoc:
+ if min_visibility == :public then
+ array.reject! { |e|
+ e.visibility != :public and not e.force_documentation
+ }
+ else
+ array.reject! { |e|
+ e.visibility == :private and not e.force_documentation
+ }
+ end
+ end
+
+ ##
+ # Tries to resolve unmatched aliases when a method or attribute has just
+ # been added.
+
+ def resolve_aliases added
+ # resolve any pending unmatched aliases
+ key = added.pretty_name
+ unmatched_alias_list = @unmatched_alias_lists[key]
+ return unless unmatched_alias_list
+ unmatched_alias_list.each do |unmatched_alias|
+ added.add_alias unmatched_alias, self
+ @external_aliases.delete unmatched_alias
+ end
+ @unmatched_alias_lists.delete key
+ end
+
+ ##
+ # Returns RDoc::Context::Section objects referenced in this context for use
+ # in a table of contents.
+
+ def section_contents
+ used_sections = {}
+
+ each_method do |method|
+ next unless method.display?
+
+ used_sections[method.section] = true
+ end
+
+ # order found sections
+ sections = sort_sections.select do |section|
+ used_sections[section]
+ end
+
+ # only the default section is used
+ return [] if
+ sections.length == 1 and not sections.first.title
+
+ sections
+ end
+
+ ##
+ # Sections in this context
+
+ def sections
+ @sections.values
+ end
+
+ def sections_hash # :nodoc:
+ @sections
+ end
+
+ ##
+ # Sets the current section to a section with +title+. See also #add_section
+
+ def set_current_section title, comment
+ @current_section = add_section title, comment
+ end
+
+ ##
+ # Given an array +methods+ of method names, set the visibility of each to
+ # +visibility+
+
+ def set_visibility_for(methods, visibility, singleton = false)
+ methods_matching methods, singleton do |m|
+ m.visibility = visibility
+ end
+ end
+
+ ##
+ # Sorts sections alphabetically (default) or in TomDoc fashion (none,
+ # Public, Internal, Deprecated)
+
+ def sort_sections
+ titles = @sections.map { |title, _| title }
+
+ if titles.length > 1 and
+ TOMDOC_TITLES_SORT ==
+ (titles | TOMDOC_TITLES).sort_by { |title| title.to_s } then
+ @sections.values_at(*TOMDOC_TITLES).compact
+ else
+ @sections.sort_by { |title, _|
+ title.to_s
+ }.map { |_, section|
+ section
+ }
+ end
+ end
+
+ def to_s # :nodoc:
+ "#{self.class.name} #{self.full_name}"
+ end
+
+ ##
+ # Return the TopLevel that owns us
+ #--
+ # FIXME we can be 'owned' by several TopLevel (see #record_location &
+ # #in_files)
+
+ def top_level
+ return @top_level if defined? @top_level
+ @top_level = self
+ @top_level = @top_level.parent until RDoc::TopLevel === @top_level
+ @top_level
+ end
+
+ ##
+ # Upgrades NormalModule +mod+ in +enclosing+ to a +class_type+
+
+ def upgrade_to_class mod, class_type, enclosing
+ enclosing.modules_hash.delete mod.name
+
+ klass = RDoc::ClassModule.from_module class_type, mod
+ klass.store = @store
+
+ # if it was there, then we keep it even if done_documenting
+ @store.classes_hash[mod.full_name] = klass
+ enclosing.classes_hash[mod.name] = klass
+
+ klass
+ end
+
+ autoload :Section, 'rdoc/context/section'
+
+end
+