1##
2# Base class for the RDoc code tree.
3#
4# We contain the common stuff for contexts (which are containers) and other
5# elements (methods, attributes and so on)
6#
7# Here's the tree of the CodeObject subclasses:
8#
9# * RDoc::Context
10#   * RDoc::TopLevel
11#   * RDoc::ClassModule
12#     * RDoc::AnonClass (never used so far)
13#     * RDoc::NormalClass
14#     * RDoc::NormalModule
15#     * RDoc::SingleClass
16# * RDoc::MethodAttr
17#   * RDoc::Attr
18#   * RDoc::AnyMethod
19#     * RDoc::GhostMethod
20#     * RDoc::MetaMethod
21# * RDoc::Alias
22# * RDoc::Constant
23# * RDoc::Require
24# * RDoc::Include
25
26class RDoc::CodeObject
27
28  include RDoc::Text
29
30  ##
31  # Our comment
32
33  attr_reader :comment
34
35  ##
36  # Do we document our children?
37
38  attr_reader :document_children
39
40  ##
41  # Do we document ourselves?
42
43  attr_reader :document_self
44
45  ##
46  # Are we done documenting (ie, did we come across a :enddoc:)?
47
48  attr_reader :done_documenting
49
50  ##
51  # Which file this code object was defined in
52
53  attr_reader :file
54
55  ##
56  # Force documentation of this CodeObject
57
58  attr_reader :force_documentation
59
60  ##
61  # Line in #file where this CodeObject was defined
62
63  attr_accessor :line
64
65  ##
66  # Hash of arbitrary metadata for this CodeObject
67
68  attr_reader :metadata
69
70  ##
71  # Offset in #file where this CodeObject was defined
72  #--
73  # TODO character or byte?
74
75  attr_accessor :offset
76
77  ##
78  # Sets the parent CodeObject
79
80  attr_writer :parent
81
82  ##
83  # Did we ever receive a +:nodoc:+ directive?
84
85  attr_reader :received_nodoc
86
87  ##
88  # Set the section this CodeObject is in
89
90  attr_writer :section
91
92  ##
93  # The RDoc::Store for this object.
94
95  attr_accessor :store
96
97  ##
98  # We are the model of the code, but we know that at some point we will be
99  # worked on by viewers. By implementing the Viewable protocol, viewers can
100  # associated themselves with these objects.
101
102  attr_accessor :viewer
103
104  ##
105  # Creates a new CodeObject that will document itself and its children
106
107  def initialize
108    @metadata      = {}
109    @comment       = ''
110    @parent        = nil
111    @parent_name   = nil # for loading
112    @parent_class  = nil # for loading
113    @section       = nil
114    @section_title = nil # for loading
115    @file          = nil
116    @full_name     = nil
117    @store         = nil
118
119    initialize_visibility
120  end
121
122  ##
123  # Initializes state for visibility of this CodeObject and its children.
124
125  def initialize_visibility # :nodoc:
126    @document_children   = true
127    @document_self       = true
128    @done_documenting    = false
129    @force_documentation = false
130    @received_nodoc      = false
131    @ignored             = false
132  end
133
134  ##
135  # Replaces our comment with +comment+, unless it is empty.
136
137  def comment=(comment)
138    @comment = case comment
139               when NilClass               then ''
140               when RDoc::Markup::Document then comment
141               when RDoc::Comment          then comment.normalize
142               else
143                 if comment and not comment.empty? then
144                   normalize_comment comment
145                 else
146                   # HACK correct fix is to have #initialize create @comment
147                   #      with the correct encoding
148                   if String === @comment and
149                      Object.const_defined? :Encoding and @comment.empty? then
150                     @comment.force_encoding comment.encoding
151                   end
152                   @comment
153                 end
154               end
155  end
156
157  ##
158  # Should this CodeObject be shown in documentation?
159
160  def display?
161    @document_self and not @ignored
162  end
163
164  ##
165  # Enables or disables documentation of this CodeObject's children unless it
166  # has been turned off by :enddoc:
167
168  def document_children=(document_children)
169    @document_children = document_children unless @done_documenting
170  end
171
172  ##
173  # Enables or disables documentation of this CodeObject unless it has been
174  # turned off by :enddoc:.  If the argument is +nil+ it means the
175  # documentation is turned off by +:nodoc:+.
176
177  def document_self=(document_self)
178    return if @done_documenting
179
180    @document_self = document_self
181    @received_nodoc = true if document_self.nil?
182  end
183
184  ##
185  # Does this object have a comment with content or is #received_nodoc true?
186
187  def documented?
188    @received_nodoc or !@comment.empty?
189  end
190
191  ##
192  # Turns documentation on/off, and turns on/off #document_self
193  # and #document_children.
194  #
195  # Once documentation has been turned off (by +:enddoc:+),
196  # the object will refuse to turn #document_self or
197  # #document_children on, so +:doc:+ and +:start_doc:+ directives
198  # will have no effect in the current file.
199
200  def done_documenting=(value)
201    @done_documenting = value
202    @document_self = !value
203    @document_children = @document_self
204  end
205
206  ##
207  # Yields each parent of this CodeObject.  See also
208  # RDoc::ClassModule#each_ancestor
209
210  def each_parent
211    code_object = self
212
213    while code_object = code_object.parent do
214      yield code_object
215    end
216
217    self
218  end
219
220  ##
221  # File name where this CodeObject was found.
222  #
223  # See also RDoc::Context#in_files
224
225  def file_name
226    return unless @file
227
228    @file.absolute_name
229  end
230
231  ##
232  # Force the documentation of this object unless documentation
233  # has been turned off by :enddoc:
234  #--
235  # HACK untested, was assigning to an ivar
236
237  def force_documentation=(value)
238    @force_documentation = value unless @done_documenting
239  end
240
241  ##
242  # Sets the full_name overriding any computed full name.
243  #
244  # Set to +nil+ to clear RDoc's cached value
245
246  def full_name= full_name
247    @full_name = full_name
248  end
249
250  ##
251  # Use this to ignore a CodeObject and all its children until found again
252  # (#record_location is called).  An ignored item will not be shown in
253  # documentation.
254  #
255  # See github issue #55
256  #
257  # The ignored status is temporary in order to allow implementation details
258  # to be hidden.  At the end of processing a file RDoc allows all classes
259  # and modules to add new documentation to previously created classes.
260  #
261  # If a class was ignored (via stopdoc) then reopened later with additional
262  # documentation it should be shown.  If a class was ignored and never
263  # reopened it should not be shown.  The ignore flag allows this to occur.
264
265  def ignore
266    @ignored = true
267
268    stop_doc
269  end
270
271  ##
272  # Has this class been ignored?
273
274  def ignored?
275    @ignored
276  end
277
278  ##
279  # Our parent CodeObject.  The parent may be missing for classes loaded from
280  # legacy RI data stores.
281
282  def parent
283    return @parent if @parent
284    return nil unless @parent_name
285
286    if @parent_class == RDoc::TopLevel then
287      @parent = @store.add_file @parent_name
288    else
289      @parent = @store.find_class_or_module @parent_name
290
291      return @parent if @parent
292
293      begin
294        @parent = @store.load_class @parent_name
295      rescue RDoc::Store::MissingFileError
296        nil
297      end
298    end
299  end
300
301  ##
302  # File name of our parent
303
304  def parent_file_name
305    @parent ? @parent.base_name : '(unknown)'
306  end
307
308  ##
309  # Name of our parent
310
311  def parent_name
312    @parent ? @parent.full_name : '(unknown)'
313  end
314
315  ##
316  # Records the RDoc::TopLevel (file) where this code object was defined
317
318  def record_location top_level
319    @ignored = false
320    @file = top_level
321  end
322
323  ##
324  # The section this CodeObject is in.  Sections allow grouping of constants,
325  # attributes and methods inside a class or module.
326
327  def section
328    return @section if @section
329
330    @section = parent.add_section @section_title if parent
331  end
332
333  ##
334  # Enable capture of documentation unless documentation has been
335  # turned off by :enddoc:
336
337  def start_doc
338    return if @done_documenting
339
340    @document_self = true
341    @document_children = true
342    @ignored = false
343  end
344
345  ##
346  # Disable capture of documentation
347
348  def stop_doc
349    @document_self = false
350    @document_children = false
351  end
352
353end
354
355