context.rb 27 KB

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