ruby/lib/rdoc/code_object.rb

429 строки
9.4 KiB
Ruby

# frozen_string_literal: false
##
# Base class for the RDoc code tree.
#
# We contain the common stuff for contexts (which are containers) and other
# elements (methods, attributes and so on)
#
# Here's the tree of the CodeObject subclasses:
#
# * RDoc::Context
# * RDoc::TopLevel
# * RDoc::ClassModule
# * RDoc::AnonClass (never used so far)
# * RDoc::NormalClass
# * RDoc::NormalModule
# * RDoc::SingleClass
# * RDoc::MethodAttr
# * RDoc::Attr
# * RDoc::AnyMethod
# * RDoc::GhostMethod
# * RDoc::MetaMethod
# * RDoc::Alias
# * RDoc::Constant
# * RDoc::Mixin
# * RDoc::Require
# * RDoc::Include
class RDoc::CodeObject
include RDoc::Text
##
# Our comment
attr_reader :comment
##
# Do we document our children?
attr_reader :document_children
##
# Do we document ourselves?
attr_reader :document_self
##
# Are we done documenting (ie, did we come across a :enddoc:)?
attr_reader :done_documenting
##
# Which file this code object was defined in
attr_reader :file
##
# Force documentation of this CodeObject
attr_reader :force_documentation
##
# Line in #file where this CodeObject was defined
attr_accessor :line
##
# Hash of arbitrary metadata for this CodeObject
attr_reader :metadata
##
# Offset in #file where this CodeObject was defined
#--
# TODO character or byte?
attr_accessor :offset
##
# Sets the parent CodeObject
attr_writer :parent
##
# Did we ever receive a +:nodoc:+ directive?
attr_reader :received_nodoc
##
# Set the section this CodeObject is in
attr_writer :section
##
# The RDoc::Store for this object.
attr_reader :store
##
# We are the model of the code, but we know that at some point we will be
# worked on by viewers. By implementing the Viewable protocol, viewers can
# associated themselves with these objects.
attr_accessor :viewer
##
# Creates a new CodeObject that will document itself and its children
def initialize
@metadata = {}
@comment = ''
@parent = nil
@parent_name = nil # for loading
@parent_class = nil # for loading
@section = nil
@section_title = nil # for loading
@file = nil
@full_name = nil
@store = nil
@track_visibility = true
initialize_visibility
end
##
# Initializes state for visibility of this CodeObject and its children.
def initialize_visibility # :nodoc:
@document_children = true
@document_self = true
@done_documenting = false
@force_documentation = false
@received_nodoc = false
@ignored = false
@suppressed = false
@track_visibility = true
end
##
# Replaces our comment with +comment+, unless it is empty.
def comment=(comment)
@comment = case comment
when NilClass then ''
when RDoc::Markup::Document then comment
when RDoc::Comment then comment.normalize
else
if comment and not comment.empty? then
normalize_comment comment
else
# HACK correct fix is to have #initialize create @comment
# with the correct encoding
if String === @comment and @comment.empty? then
@comment.force_encoding comment.encoding
end
@comment
end
end
end
##
# Should this CodeObject be displayed in output?
#
# A code object should be displayed if:
#
# * The item didn't have a nodoc or wasn't in a container that had nodoc
# * The item wasn't ignored
# * The item has documentation and was not suppressed
def display?
@document_self and not @ignored and
(documented? or not @suppressed)
end
##
# Enables or disables documentation of this CodeObject's children unless it
# has been turned off by :enddoc:
def document_children=(document_children)
return unless @track_visibility
@document_children = document_children unless @done_documenting
end
##
# Enables or disables documentation of this CodeObject unless it has been
# turned off by :enddoc:. If the argument is +nil+ it means the
# documentation is turned off by +:nodoc:+.
def document_self=(document_self)
return unless @track_visibility
return if @done_documenting
@document_self = document_self
@received_nodoc = true if document_self.nil?
end
##
# Does this object have a comment with content or is #received_nodoc true?
def documented?
@received_nodoc or !@comment.empty?
end
##
# Turns documentation on/off, and turns on/off #document_self
# and #document_children.
#
# Once documentation has been turned off (by +:enddoc:+),
# the object will refuse to turn #document_self or
# #document_children on, so +:doc:+ and +:start_doc:+ directives
# will have no effect in the current file.
def done_documenting=(value)
return unless @track_visibility
@done_documenting = value
@document_self = !value
@document_children = @document_self
end
##
# Yields each parent of this CodeObject. See also
# RDoc::ClassModule#each_ancestor
def each_parent
code_object = self
while code_object = code_object.parent do
yield code_object
end
self
end
##
# File name where this CodeObject was found.
#
# See also RDoc::Context#in_files
def file_name
return unless @file
@file.absolute_name
end
##
# Force the documentation of this object unless documentation
# has been turned off by :enddoc:
#--
# HACK untested, was assigning to an ivar
def force_documentation=(value)
@force_documentation = value unless @done_documenting
end
##
# Sets the full_name overriding any computed full name.
#
# Set to +nil+ to clear RDoc's cached value
def full_name= full_name
@full_name = full_name
end
##
# Use this to ignore a CodeObject and all its children until found again
# (#record_location is called). An ignored item will not be displayed in
# documentation.
#
# See github issue #55
#
# The ignored status is temporary in order to allow implementation details
# to be hidden. At the end of processing a file RDoc allows all classes
# and modules to add new documentation to previously created classes.
#
# If a class was ignored (via stopdoc) then reopened later with additional
# documentation it should be displayed. If a class was ignored and never
# reopened it should not be displayed. The ignore flag allows this to
# occur.
def ignore
return unless @track_visibility
@ignored = true
stop_doc
end
##
# Has this class been ignored?
#
# See also #ignore
def ignored?
@ignored
end
##
# The options instance from the store this CodeObject is attached to, or a
# default options instance if the CodeObject is not attached.
#
# This is used by Text#snippet
def options
if @store and @store.rdoc then
@store.rdoc.options
else
RDoc::Options.new
end
end
##
# Our parent CodeObject. The parent may be missing for classes loaded from
# legacy RI data stores.
def parent
return @parent if @parent
return nil unless @parent_name
if @parent_class == RDoc::TopLevel then
@parent = @store.add_file @parent_name
else
@parent = @store.find_class_or_module @parent_name
return @parent if @parent
begin
@parent = @store.load_class @parent_name
rescue RDoc::Store::MissingFileError
nil
end
end
end
##
# File name of our parent
def parent_file_name
@parent ? @parent.base_name : '(unknown)'
end
##
# Name of our parent
def parent_name
@parent ? @parent.full_name : '(unknown)'
end
##
# Records the RDoc::TopLevel (file) where this code object was defined
def record_location top_level
@ignored = false
@suppressed = false
@file = top_level
end
##
# The section this CodeObject is in. Sections allow grouping of constants,
# attributes and methods inside a class or module.
def section
return @section if @section
@section = parent.add_section @section_title if parent
end
##
# Enable capture of documentation unless documentation has been
# turned off by :enddoc:
def start_doc
return if @done_documenting
@document_self = true
@document_children = true
@ignored = false
@suppressed = false
end
##
# Disable capture of documentation
def stop_doc
return unless @track_visibility
@document_self = false
@document_children = false
end
##
# Sets the +store+ that contains this CodeObject
def store= store
@store = store
return unless @track_visibility
if :nodoc == options.visibility then
initialize_visibility
@track_visibility = false
end
end
##
# Use this to suppress a CodeObject and all its children until the next file
# it is seen in or documentation is discovered. A suppressed item with
# documentation will be displayed while an ignored item with documentation
# may not be displayed.
def suppress
return unless @track_visibility
@suppressed = true
stop_doc
end
##
# Has this class been suppressed?
#
# See also #suppress
def suppressed?
@suppressed
end
end