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