1require 'cgi'
2
3##
4# A Context is something that can hold modules, classes, methods, attributes,
5# aliases, requires, and includes. Classes, modules, and files are all
6# Contexts.
7
8class RDoc::Context < RDoc::CodeObject
9
10  include Comparable
11
12  ##
13  # Types of methods
14
15  TYPES = %w[class instance]
16
17  ##
18  # If a context has these titles it will be sorted in this order.
19
20  TOMDOC_TITLES = [nil, 'Public', 'Internal', 'Deprecated'] # :nodoc:
21  TOMDOC_TITLES_SORT = TOMDOC_TITLES.sort_by { |title| title.to_s } # :nodoc:
22
23  ##
24  # Class/module aliases
25
26  attr_reader :aliases
27
28  ##
29  # All attr* methods
30
31  attr_reader :attributes
32
33  ##
34  # Block params to be used in the next MethodAttr parsed under this context
35
36  attr_accessor :block_params
37
38  ##
39  # Constants defined
40
41  attr_reader :constants
42
43  ##
44  # Sets the current documentation section of documentation
45
46  attr_writer :current_section
47
48  ##
49  # Files this context is found in
50
51  attr_reader :in_files
52
53  ##
54  # Modules this context includes
55
56  attr_reader :includes
57
58  ##
59  # Modules this context is extended with
60
61  attr_reader :extends
62
63  ##
64  # Methods defined in this context
65
66  attr_reader :method_list
67
68  ##
69  # Name of this class excluding namespace.  See also full_name
70
71  attr_reader :name
72
73  ##
74  # Files this context requires
75
76  attr_reader :requires
77
78  ##
79  # Use this section for the next method, attribute or constant added.
80
81  attr_accessor :temporary_section
82
83  ##
84  # Hash <tt>old_name => [aliases]</tt>, for aliases
85  # that haven't (yet) been resolved to a method/attribute.
86  # (Not to be confused with the aliases of the context.)
87
88  attr_accessor :unmatched_alias_lists
89
90  ##
91  # Aliases that could not be resolved.
92
93  attr_reader :external_aliases
94
95  ##
96  # Current visibility of this context
97
98  attr_accessor :visibility
99
100  ##
101  # Hash of registered methods. Attributes are also registered here,
102  # twice if they are RW.
103
104  attr_reader :methods_hash
105
106  ##
107  # Params to be used in the next MethodAttr parsed under this context
108
109  attr_accessor :params
110
111  ##
112  # Hash of registered constants.
113
114  attr_reader :constants_hash
115
116  ##
117  # Creates an unnamed empty context with public current visibility
118
119  def initialize
120    super
121
122    @in_files = []
123
124    @name    ||= "unknown"
125    @parent  = nil
126    @visibility = :public
127
128    @current_section = Section.new self, nil, nil
129    @sections = { nil => @current_section }
130    @temporary_section = nil
131
132    @classes = {}
133    @modules = {}
134
135    initialize_methods_etc
136  end
137
138  ##
139  # Sets the defaults for methods and so-forth
140
141  def initialize_methods_etc
142    @method_list = []
143    @attributes  = []
144    @aliases     = []
145    @requires    = []
146    @includes    = []
147    @extends     = []
148    @constants   = []
149    @external_aliases = []
150
151    # This Hash maps a method name to a list of unmatched aliases (aliases of
152    # a method not yet encountered).
153    @unmatched_alias_lists = {}
154
155    @methods_hash   = {}
156    @constants_hash = {}
157
158    @params = nil
159
160    @store ||= nil
161  end
162
163  ##
164  # Contexts are sorted by full_name
165
166  def <=>(other)
167    full_name <=> other.full_name
168  end
169
170  ##
171  # Adds +an_alias+ that is automatically resolved
172
173  def add_alias an_alias
174    return an_alias unless @document_self
175
176    method_attr = find_method(an_alias.old_name, an_alias.singleton) ||
177                  find_attribute(an_alias.old_name, an_alias.singleton)
178
179    if method_attr then
180      method_attr.add_alias an_alias, self
181    else
182      add_to @external_aliases, an_alias
183      unmatched_alias_list =
184        @unmatched_alias_lists[an_alias.pretty_old_name] ||= []
185      unmatched_alias_list.push an_alias
186    end
187
188    an_alias
189  end
190
191  ##
192  # Adds +attribute+ if not already there. If it is (as method(s) or attribute),
193  # updates the comment if it was empty.
194  #
195  # The attribute is registered only if it defines a new method.
196  # For instance, <tt>attr_reader :foo</tt> will not be registered
197  # if method +foo+ exists, but <tt>attr_accessor :foo</tt> will be registered
198  # if method +foo+ exists, but <tt>foo=</tt> does not.
199
200  def add_attribute attribute
201    return attribute unless @document_self
202
203    # mainly to check for redefinition of an attribute as a method
204    # TODO find a policy for 'attr_reader :foo' + 'def foo=()'
205    register = false
206
207    key = nil
208
209    if attribute.rw.index 'R' then
210      key = attribute.pretty_name
211      known = @methods_hash[key]
212
213      if known then
214        known.comment = attribute.comment if known.comment.empty?
215      elsif registered = @methods_hash[attribute.pretty_name << '='] and
216            RDoc::Attr === registered then
217        registered.rw = 'RW'
218      else
219        @methods_hash[key] = attribute
220        register = true
221      end
222    end
223
224    if attribute.rw.index 'W' then
225      key = attribute.pretty_name << '='
226      known = @methods_hash[key]
227
228      if known then
229        known.comment = attribute.comment if known.comment.empty?
230      elsif registered = @methods_hash[attribute.pretty_name] and
231            RDoc::Attr === registered then
232        registered.rw = 'RW'
233      else
234        @methods_hash[key] = attribute
235        register = true
236      end
237    end
238
239    if register then
240      attribute.visibility = @visibility
241      add_to @attributes, attribute
242      resolve_aliases attribute
243    end
244
245    attribute
246  end
247
248  ##
249  # Adds a class named +given_name+ with +superclass+.
250  #
251  # Both +given_name+ and +superclass+ may contain '::', and are
252  # interpreted relative to the +self+ context. This allows handling correctly
253  # examples like these:
254  #   class RDoc::Gauntlet < Gauntlet
255  #   module Mod
256  #     class Object   # implies < ::Object
257  #     class SubObject < Object  # this is _not_ ::Object
258  #
259  # Given <tt>class Container::Item</tt> RDoc assumes +Container+ is a module
260  # unless it later sees <tt>class Container</tt>.  +add_class+ automatically
261  # upgrades +given_name+ to a class in this case.
262
263  def add_class class_type, given_name, superclass = '::Object'
264    # superclass +nil+ is passed by the C parser in the following cases:
265    # - registering Object in 1.8 (correct)
266    # - registering BasicObject in 1.9 (correct)
267    # - registering RubyVM in 1.9 in iseq.c (incorrect: < Object in vm.c)
268    #
269    # If we later find a superclass for a registered class with a nil
270    # superclass, we must honor it.
271
272    # find the name & enclosing context
273    if given_name =~ /^:+(\w+)$/ then
274      full_name = $1
275      enclosing = top_level
276      name = full_name.split(/:+/).last
277    else
278      full_name = child_name given_name
279
280      if full_name =~ /^(.+)::(\w+)$/ then
281        name = $2
282        ename = $1
283        enclosing = @store.classes_hash[ename] || @store.modules_hash[ename]
284        # HACK: crashes in actionpack/lib/action_view/helpers/form_helper.rb (metaprogramming)
285        unless enclosing then
286          # try the given name at top level (will work for the above example)
287          enclosing = @store.classes_hash[given_name] ||
288                      @store.modules_hash[given_name]
289          return enclosing if enclosing
290          # not found: create the parent(s)
291          names = ename.split('::')
292          enclosing = self
293          names.each do |n|
294            enclosing = enclosing.classes_hash[n] ||
295                        enclosing.modules_hash[n] ||
296                        enclosing.add_module(RDoc::NormalModule, n)
297          end
298        end
299      else
300        name = full_name
301        enclosing = self
302      end
303    end
304
305    # fix up superclass
306    superclass = nil if full_name == 'BasicObject'
307    superclass = nil if full_name == 'Object' and defined?(::BasicObject)
308    superclass = '::BasicObject' if
309      defined?(::BasicObject) and full_name == 'Object'
310
311    # find the superclass full name
312    if superclass then
313      if superclass =~ /^:+/ then
314        superclass = $' #'
315      else
316        if superclass =~ /^(\w+):+(.+)$/ then
317          suffix = $2
318          mod = find_module_named($1)
319          superclass = mod.full_name + '::' + suffix if mod
320        else
321          mod = find_module_named(superclass)
322          superclass = mod.full_name if mod
323        end
324      end
325
326      # did we believe it was a module?
327      mod = @store.modules_hash.delete superclass
328
329      upgrade_to_class mod, RDoc::NormalClass, mod.parent if mod
330
331      # e.g., Object < Object
332      superclass = nil if superclass == full_name
333    end
334
335    klass = @store.classes_hash[full_name]
336
337    if klass then
338      # if TopLevel, it may not be registered in the classes:
339      enclosing.classes_hash[name] = klass
340
341      # update the superclass if needed
342      if superclass then
343        existing = klass.superclass
344        existing = existing.full_name unless existing.is_a?(String) if existing
345        if existing.nil? ||
346           (existing == 'Object' && superclass != 'Object') then
347          klass.superclass = superclass
348        end
349      end
350    else
351      # this is a new class
352      mod = @store.modules_hash.delete full_name
353
354      if mod then
355        klass = upgrade_to_class mod, RDoc::NormalClass, enclosing
356
357        klass.superclass = superclass unless superclass.nil?
358      else
359        klass = class_type.new name, superclass
360
361        enclosing.add_class_or_module(klass, enclosing.classes_hash,
362                                      @store.classes_hash)
363      end
364    end
365
366    klass.parent = self
367
368    klass
369  end
370
371  ##
372  # Adds the class or module +mod+ to the modules or
373  # classes Hash +self_hash+, and to +all_hash+ (either
374  # <tt>TopLevel::modules_hash</tt> or <tt>TopLevel::classes_hash</tt>),
375  # unless #done_documenting is +true+. Sets the #parent of +mod+
376  # to +self+, and its #section to #current_section. Returns +mod+.
377
378  def add_class_or_module mod, self_hash, all_hash
379    mod.section = current_section # TODO declaring context? something is
380                                  # wrong here...
381    mod.parent = self
382    mod.store = @store
383
384    unless @done_documenting then
385      self_hash[mod.name] = mod
386      # this must be done AFTER adding mod to its parent, so that the full
387      # name is correct:
388      all_hash[mod.full_name] = mod
389    end
390
391    mod
392  end
393
394  ##
395  # Adds +constant+ if not already there. If it is, updates the comment,
396  # value and/or is_alias_for of the known constant if they were empty/nil.
397
398  def add_constant constant
399    return constant unless @document_self
400
401    # HACK: avoid duplicate 'PI' & 'E' in math.c (1.8.7 source code)
402    # (this is a #ifdef: should be handled by the C parser)
403    known = @constants_hash[constant.name]
404
405    if known then
406      known.comment = constant.comment if known.comment.empty?
407
408      known.value = constant.value if
409        known.value.nil? or known.value.strip.empty?
410
411      known.is_alias_for ||= constant.is_alias_for
412    else
413      @constants_hash[constant.name] = constant
414      add_to @constants, constant
415    end
416
417    constant
418  end
419
420  ##
421  # Adds included module +include+ which should be an RDoc::Include
422
423  def add_include include
424    add_to @includes, include
425
426    include
427  end
428
429  ##
430  # Adds extension module +ext+ which should be an RDoc::Extend
431
432  def add_extend ext
433    add_to @extends, ext
434
435    ext
436  end
437
438  ##
439  # Adds +method+ if not already there. If it is (as method or attribute),
440  # updates the comment if it was empty.
441
442  def add_method method
443    return method unless @document_self
444
445    # HACK: avoid duplicate 'new' in io.c & struct.c (1.8.7 source code)
446    key = method.pretty_name
447    known = @methods_hash[key]
448
449    if known then
450      if @store then # otherwise we are loading
451        known.comment = method.comment if known.comment.empty?
452        previously = ", previously in #{known.file}" unless
453          method.file == known.file
454        @store.rdoc.options.warn \
455          "Duplicate method #{known.full_name} in #{method.file}#{previously}"
456      end
457    else
458      @methods_hash[key] = method
459      method.visibility = @visibility
460      add_to @method_list, method
461      resolve_aliases method
462    end
463
464    method
465  end
466
467  ##
468  # Adds a module named +name+.  If RDoc already knows +name+ is a class then
469  # that class is returned instead.  See also #add_class.
470
471  def add_module(class_type, name)
472    mod = @classes[name] || @modules[name]
473    return mod if mod
474
475    full_name = child_name name
476    mod = @store.modules_hash[full_name] || class_type.new(name)
477
478    add_class_or_module mod, @modules, @store.modules_hash
479  end
480
481  ##
482  # Adds an alias from +from+ (a class or module) to +name+ which was defined
483  # in +file+.
484
485  def add_module_alias from, name, file
486    return from if @done_documenting
487
488    to_name = child_name name
489
490    # if we already know this name, don't register an alias:
491    # see the metaprogramming in lib/active_support/basic_object.rb,
492    # where we already know BasicObject is a class when we find
493    # BasicObject = BlankSlate
494    return from if @store.find_class_or_module to_name
495
496    to = from.dup
497    to.name = name
498    to.full_name = nil
499
500    if to.module? then
501      @store.modules_hash[to_name] = to
502      @modules[name] = to
503    else
504      @store.classes_hash[to_name] = to
505      @classes[name] = to
506    end
507
508    # Registers a constant for this alias.  The constant value and comment
509    # will be updated later, when the Ruby parser adds the constant
510    const = RDoc::Constant.new name, nil, to.comment
511    const.record_location file
512    const.is_alias_for = from
513    add_constant const
514
515    to
516  end
517
518  ##
519  # Adds +require+ to this context's top level
520
521  def add_require(require)
522    return require unless @document_self
523
524    if RDoc::TopLevel === self then
525      add_to @requires, require
526    else
527      parent.add_require require
528    end
529  end
530
531  ##
532  # Returns a section with +title+, creating it if it doesn't already exist.
533  # +comment+ will be appended to the section's comment.
534  #
535  # A section with a +title+ of +nil+ will return the default section.
536  #
537  # See also RDoc::Context::Section
538
539  def add_section title, comment = nil
540    if section = @sections[title] then
541      section.add_comment comment if comment
542    else
543      section = Section.new self, title, comment
544      @sections[title] = section
545    end
546
547    section
548  end
549
550  ##
551  # Adds +thing+ to the collection +array+
552
553  def add_to array, thing
554    array << thing if @document_self
555
556    thing.parent  = self
557    thing.store   = @store if @store
558    thing.section = current_section
559  end
560
561  ##
562  # Is there any content?
563  #
564  # This means any of: comment, aliases, methods, attributes, external
565  # aliases, require, constant.
566  #
567  # Includes and extends are also checked unless <tt>includes == false</tt>.
568
569  def any_content(includes = true)
570    @any_content ||= !(
571      @comment.empty? &&
572      @method_list.empty? &&
573      @attributes.empty? &&
574      @aliases.empty? &&
575      @external_aliases.empty? &&
576      @requires.empty? &&
577      @constants.empty?
578    )
579    @any_content || (includes && !(@includes + @extends).empty? )
580  end
581
582  ##
583  # Creates the full name for a child with +name+
584
585  def child_name name
586    if name =~ /^:+/
587      $'  #'
588    elsif RDoc::TopLevel === self then
589      name
590    else
591      "#{self.full_name}::#{name}"
592    end
593  end
594
595  ##
596  # Class attributes
597
598  def class_attributes
599    @class_attributes ||= attributes.select { |a| a.singleton }
600  end
601
602  ##
603  # Class methods
604
605  def class_method_list
606    @class_method_list ||= method_list.select { |a| a.singleton }
607  end
608
609  ##
610  # Array of classes in this context
611
612  def classes
613    @classes.values
614  end
615
616  ##
617  # All classes and modules in this namespace
618
619  def classes_and_modules
620    classes + modules
621  end
622
623  ##
624  # Hash of classes keyed by class name
625
626  def classes_hash
627    @classes
628  end
629
630  ##
631  # The current documentation section that new items will be added to.  If
632  # temporary_section is available it will be used.
633
634  def current_section
635    if section = @temporary_section then
636      @temporary_section = nil
637    else
638      section = @current_section
639    end
640
641    section
642  end
643
644  ##
645  # Is part of this thing was defined in +file+?
646
647  def defined_in?(file)
648    @in_files.include?(file)
649  end
650
651  def display(method_attr) # :nodoc:
652    if method_attr.is_a? RDoc::Attr
653      "#{method_attr.definition} #{method_attr.pretty_name}"
654    else
655      "method #{method_attr.pretty_name}"
656    end
657  end
658
659  ##
660  # Iterator for ancestors for duck-typing.  Does nothing.  See
661  # RDoc::ClassModule#each_ancestor.
662  #
663  # This method exists to make it easy to work with Context subclasses that
664  # aren't part of RDoc.
665
666  def each_ancestor # :nodoc:
667  end
668
669  ##
670  # Iterator for attributes
671
672  def each_attribute # :yields: attribute
673    @attributes.each { |a| yield a }
674  end
675
676  ##
677  # Iterator for classes and modules
678
679  def each_classmodule(&block) # :yields: module
680    classes_and_modules.sort.each(&block)
681  end
682
683  ##
684  # Iterator for constants
685
686  def each_constant # :yields: constant
687    @constants.each {|c| yield c}
688  end
689
690  ##
691  # Iterator for included modules
692
693  def each_include # :yields: include
694    @includes.each do |i| yield i end
695  end
696
697  ##
698  # Iterator for extension modules
699
700  def each_extend # :yields: extend
701    @extends.each do |e| yield e end
702  end
703
704  ##
705  # Iterator for methods
706
707  def each_method # :yields: method
708    return enum_for __method__ unless block_given?
709
710    @method_list.sort.each { |m| yield m }
711  end
712
713  ##
714  # Iterator for each section's contents sorted by title.  The +section+, the
715  # section's +constants+ and the sections +attributes+ are yielded.  The
716  # +constants+ and +attributes+ collections are sorted.
717  #
718  # To retrieve methods in a section use #methods_by_type with the optional
719  # +section+ parameter.
720  #
721  # NOTE: Do not edit collections yielded by this method
722
723  def each_section # :yields: section, constants, attributes
724    return enum_for __method__ unless block_given?
725
726    constants  = @constants.group_by  do |constant|  constant.section end
727    attributes = @attributes.group_by do |attribute| attribute.section end
728
729    constants.default  = []
730    attributes.default = []
731
732    sort_sections.each do |section|
733      yield section, constants[section].sort, attributes[section].sort
734    end
735  end
736
737  ##
738  # Finds an attribute +name+ with singleton value +singleton+.
739
740  def find_attribute(name, singleton)
741    name = $1 if name =~ /^(.*)=$/
742    @attributes.find { |a| a.name == name && a.singleton == singleton }
743  end
744
745  ##
746  # Finds an attribute with +name+ in this context
747
748  def find_attribute_named(name)
749    case name
750    when /\A#/ then
751      find_attribute name[1..-1], false
752    when /\A::/ then
753      find_attribute name[2..-1], true
754    else
755      @attributes.find { |a| a.name == name }
756    end
757  end
758
759  ##
760  # Finds a class method with +name+ in this context
761
762  def find_class_method_named(name)
763    @method_list.find { |meth| meth.singleton && meth.name == name }
764  end
765
766  ##
767  # Finds a constant with +name+ in this context
768
769  def find_constant_named(name)
770    @constants.find {|m| m.name == name}
771  end
772
773  ##
774  # Find a module at a higher scope
775
776  def find_enclosing_module_named(name)
777    parent && parent.find_module_named(name)
778  end
779
780  ##
781  # Finds an external alias +name+ with singleton value +singleton+.
782
783  def find_external_alias(name, singleton)
784    @external_aliases.find { |m| m.name == name && m.singleton == singleton }
785  end
786
787  ##
788  # Finds an external alias with +name+ in this context
789
790  def find_external_alias_named(name)
791    case name
792    when /\A#/ then
793      find_external_alias name[1..-1], false
794    when /\A::/ then
795      find_external_alias name[2..-1], true
796    else
797      @external_aliases.find { |a| a.name == name }
798    end
799  end
800
801  ##
802  # Finds a file with +name+ in this context
803
804  def find_file_named name
805    @store.find_file_named name
806  end
807
808  ##
809  # Finds an instance method with +name+ in this context
810
811  def find_instance_method_named(name)
812    @method_list.find { |meth| !meth.singleton && meth.name == name }
813  end
814
815  ##
816  # Finds a method, constant, attribute, external alias, module or file
817  # named +symbol+ in this context.
818
819  def find_local_symbol(symbol)
820    find_method_named(symbol) or
821    find_constant_named(symbol) or
822    find_attribute_named(symbol) or
823    find_external_alias_named(symbol) or
824    find_module_named(symbol) or
825    find_file_named(symbol)
826  end
827
828  ##
829  # Finds a method named +name+ with singleton value +singleton+.
830
831  def find_method(name, singleton)
832    @method_list.find { |m| m.name == name && m.singleton == singleton }
833  end
834
835  ##
836  # Finds a instance or module method with +name+ in this context
837
838  def find_method_named(name)
839    case name
840    when /\A#/ then
841      find_method name[1..-1], false
842    when /\A::/ then
843      find_method name[2..-1], true
844    else
845      @method_list.find { |meth| meth.name == name }
846    end
847  end
848
849  ##
850  # Find a module with +name+ using ruby's scoping rules
851
852  def find_module_named(name)
853    res = @modules[name] || @classes[name]
854    return res if res
855    return self if self.name == name
856    find_enclosing_module_named name
857  end
858
859  ##
860  # Look up +symbol+, first as a module, then as a local symbol.
861
862  def find_symbol(symbol)
863    find_symbol_module(symbol) || find_local_symbol(symbol)
864  end
865
866  ##
867  # Look up a module named +symbol+.
868
869  def find_symbol_module(symbol)
870    result = nil
871
872    # look for a class or module 'symbol'
873    case symbol
874    when /^::/ then
875      result = @store.find_class_or_module symbol
876    when /^(\w+):+(.+)$/
877      suffix = $2
878      top = $1
879      searched = self
880      while searched do
881        mod = searched.find_module_named(top)
882        break unless mod
883        result = @store.find_class_or_module "#{mod.full_name}::#{suffix}"
884        break if result || searched.is_a?(RDoc::TopLevel)
885        searched = searched.parent
886      end
887    else
888      searched = self
889      while searched do
890        result = searched.find_module_named(symbol)
891        break if result || searched.is_a?(RDoc::TopLevel)
892        searched = searched.parent
893      end
894    end
895
896    result
897  end
898
899  ##
900  # The full name for this context.  This method is overridden by subclasses.
901
902  def full_name
903    '(unknown)'
904  end
905
906  ##
907  # Does this context and its methods and constants all have documentation?
908  #
909  # (Yes, fully documented doesn't mean everything.)
910
911  def fully_documented?
912    documented? and
913      attributes.all? { |a| a.documented? } and
914      method_list.all? { |m| m.documented? } and
915      constants.all? { |c| c.documented? }
916  end
917
918  ##
919  # URL for this with a +prefix+
920
921  def http_url(prefix)
922    path = name_for_path
923    path = path.gsub(/<<\s*(\w*)/, 'from-\1') if path =~ /<</
924    path = [prefix] + path.split('::')
925
926    File.join(*path.compact) + '.html'
927  end
928
929  ##
930  # Instance attributes
931
932  def instance_attributes
933    @instance_attributes ||= attributes.reject { |a| a.singleton }
934  end
935
936  ##
937  # Instance methods
938  #--
939  # TODO rename to instance_methods
940
941  def instance_method_list
942    @instance_method_list ||= method_list.reject { |a| a.singleton }
943  end
944
945  ##
946  # Breaks method_list into a nested hash by type (<tt>'class'</tt> or
947  # <tt>'instance'</tt>) and visibility (+:public+, +:protected+, +:private+).
948  #
949  # If +section+ is provided only methods in that RDoc::Context::Section will
950  # be returned.
951
952  def methods_by_type section = nil
953    methods = {}
954
955    TYPES.each do |type|
956      visibilities = {}
957      RDoc::VISIBILITIES.each do |vis|
958        visibilities[vis] = []
959      end
960
961      methods[type] = visibilities
962    end
963
964    each_method do |method|
965      next if section and not method.section == section
966      methods[method.type][method.visibility] << method
967    end
968
969    methods
970  end
971
972  ##
973  # Yields AnyMethod and Attr entries matching the list of names in +methods+.
974
975  def methods_matching(methods, singleton = false, &block)
976    (@method_list + @attributes).each do |m|
977      yield m if methods.include?(m.name) and m.singleton == singleton
978    end
979
980    each_ancestor do |parent|
981      parent.methods_matching(methods, singleton, &block)
982    end
983  end
984
985  ##
986  # Array of modules in this context
987
988  def modules
989    @modules.values
990  end
991
992  ##
993  # Hash of modules keyed by module name
994
995  def modules_hash
996    @modules
997  end
998
999  ##
1000  # Name to use to generate the url.
1001  # <tt>#full_name</tt> by default.
1002
1003  def name_for_path
1004    full_name
1005  end
1006
1007  ##
1008  # Changes the visibility for new methods to +visibility+
1009
1010  def ongoing_visibility=(visibility)
1011    @visibility = visibility
1012  end
1013
1014  ##
1015  # Record +top_level+ as a file +self+ is in.
1016
1017  def record_location(top_level)
1018    @in_files << top_level unless @in_files.include?(top_level)
1019  end
1020
1021  ##
1022  # Should we remove this context from the documentation?
1023  #
1024  # The answer is yes if:
1025  # * #received_nodoc is +true+
1026  # * #any_content is +false+ (not counting includes)
1027  # * All #includes are modules (not a string), and their module has
1028  #   <tt>#remove_from_documentation? == true</tt>
1029  # * All classes and modules have <tt>#remove_from_documentation? == true</tt>
1030
1031  def remove_from_documentation?
1032    @remove_from_documentation ||=
1033      @received_nodoc &&
1034      !any_content(false) &&
1035      @includes.all? { |i| !i.module.is_a?(String) && i.module.remove_from_documentation? } &&
1036      classes_and_modules.all? { |cm| cm.remove_from_documentation? }
1037  end
1038
1039  ##
1040  # Removes methods and attributes with a visibility less than +min_visibility+.
1041  #--
1042  # TODO mark the visibility of attributes in the template (if not public?)
1043
1044  def remove_invisible(min_visibility)
1045    return if min_visibility == :private
1046    remove_invisible_in @method_list, min_visibility
1047    remove_invisible_in @attributes, min_visibility
1048  end
1049
1050  ##
1051  # Only called when min_visibility == :public or :private
1052
1053  def remove_invisible_in array, min_visibility # :nodoc:
1054    if min_visibility == :public then
1055      array.reject! { |e|
1056        e.visibility != :public and not e.force_documentation
1057      }
1058    else
1059      array.reject! { |e|
1060        e.visibility == :private and not e.force_documentation
1061      }
1062    end
1063  end
1064
1065  ##
1066  # Tries to resolve unmatched aliases when a method or attribute has just
1067  # been added.
1068
1069  def resolve_aliases added
1070    # resolve any pending unmatched aliases
1071    key = added.pretty_name
1072    unmatched_alias_list = @unmatched_alias_lists[key]
1073    return unless unmatched_alias_list
1074    unmatched_alias_list.each do |unmatched_alias|
1075      added.add_alias unmatched_alias, self
1076      @external_aliases.delete unmatched_alias
1077    end
1078    @unmatched_alias_lists.delete key
1079  end
1080
1081  ##
1082  # Returns RDoc::Context::Section objects referenced in this context for use
1083  # in a table of contents.
1084
1085  def section_contents
1086    used_sections = {}
1087
1088    each_method do |method|
1089      next unless method.display?
1090
1091      used_sections[method.section] = true
1092    end
1093
1094    # order found sections
1095    sections = sort_sections.select do |section|
1096      used_sections[section]
1097    end
1098
1099    # only the default section is used
1100    return [] if
1101      sections.length == 1 and not sections.first.title
1102
1103    sections
1104  end
1105
1106  ##
1107  # Sections in this context
1108
1109  def sections
1110    @sections.values
1111  end
1112
1113  def sections_hash # :nodoc:
1114    @sections
1115  end
1116
1117  ##
1118  # Sets the current section to a section with +title+.  See also #add_section
1119
1120  def set_current_section title, comment
1121    @current_section = add_section title, comment
1122  end
1123
1124  ##
1125  # Given an array +methods+ of method names, set the visibility of each to
1126  # +visibility+
1127
1128  def set_visibility_for(methods, visibility, singleton = false)
1129    methods_matching methods, singleton do |m|
1130      m.visibility = visibility
1131    end
1132  end
1133
1134  ##
1135  # Sorts sections alphabetically (default) or in TomDoc fashion (none,
1136  # Public, Internal, Deprecated)
1137
1138  def sort_sections
1139    titles = @sections.map { |title, _| title }
1140
1141    if titles.length > 1 and
1142       TOMDOC_TITLES_SORT ==
1143         (titles | TOMDOC_TITLES).sort_by { |title| title.to_s } then
1144      @sections.values_at(*TOMDOC_TITLES).compact
1145    else
1146      @sections.sort_by { |title, _|
1147        title.to_s
1148      }.map { |_, section|
1149        section
1150      }
1151    end
1152  end
1153
1154  def to_s # :nodoc:
1155    "#{self.class.name} #{self.full_name}"
1156  end
1157
1158  ##
1159  # Return the TopLevel that owns us
1160  #--
1161  # FIXME we can be 'owned' by several TopLevel (see #record_location &
1162  # #in_files)
1163
1164  def top_level
1165    return @top_level if defined? @top_level
1166    @top_level = self
1167    @top_level = @top_level.parent until RDoc::TopLevel === @top_level
1168    @top_level
1169  end
1170
1171  ##
1172  # Upgrades NormalModule +mod+ in +enclosing+ to a +class_type+
1173
1174  def upgrade_to_class mod, class_type, enclosing
1175    enclosing.modules_hash.delete mod.name
1176
1177    klass = RDoc::ClassModule.from_module class_type, mod
1178    klass.store = @store
1179
1180    # if it was there, then we keep it even if done_documenting
1181    @store.classes_hash[mod.full_name] = klass
1182    enclosing.classes_hash[mod.name]   = klass
1183
1184    klass
1185  end
1186
1187  autoload :Section, 'rdoc/context/section'
1188
1189end
1190
1191